diff --git a/Code/data/calib.html b/Code/data/calib.html index 6a2c73e0..ca82bd4f 100644 --- a/Code/data/calib.html +++ b/Code/data/calib.html @@ -3,18 +3,25 @@ Lay-Z-Spa Module | SPA Manual calibration - + + - - + +
- SPA Manual calibration +
+ +
+
@@ -81,5 +88,7 @@ } + + diff --git a/Code/data/config.html b/Code/data/config.html index 5a66f898..fb3e5709 100644 --- a/Code/data/config.html +++ b/Code/data/config.html @@ -3,18 +3,25 @@ Lay-Z-Spa Module | SPA Config - + + - - + +
- SPA Config +
+ +
+
@@ -415,5 +422,7 @@

Command queue

if(obj.checked) obj.checked = false; } + + diff --git a/Code/data/darkmode.js b/Code/data/darkmode.js new file mode 100644 index 00000000..443809cf --- /dev/null +++ b/Code/data/darkmode.js @@ -0,0 +1,40 @@ +// will create a dark mode and store status in local storage + +// Check if the dark mode status is stored in localStorage +var darkModeToggle = document.getElementById("darkModeToggle"); + +if (localStorage.getItem("darkModeStatus")) { + // Update the dark mode toggle switch and status text + darkModeToggle.checked = localStorage.getItem("darkModeStatus") === "On"; + + // Set dark mode immediately based on stored status + toggleDarkMode(); +} + +function toggleDarkMode() { + var sliderElement = darkModeForm.querySelector(".slider"); + if (darkModeToggle.checked) { + // Dark Mode is On + document.documentElement.classList.add("darkmode"); + localStorage.setItem("darkModeStatus", "On"); + sliderElement.classList.remove("moon"); + sliderElement.classList.add("sun"); + } else { + // Dark Mode is Off + document.documentElement.classList.remove("darkmode"); + localStorage.setItem("darkModeStatus", "Off"); + sliderElement.classList.remove("sun"); + sliderElement.classList.add("moon"); + } +} + +// move to admin or elsewhere afterward +document.addEventListener("DOMContentLoaded", function () { + const topNavIcon = document.querySelector(".topnavicon"); + + topNavIcon.addEventListener("click", function () { + topNavIcon.classList.toggle("show-before"); + const afterIcon = topNavIcon.nextElementSibling; + afterIcon.style.display = afterIcon.style.display === "none" ? "inline" : "none"; + }); +}); diff --git a/Code/data/favicon.ico b/Code/data/favicon.ico index 2bca6624..5950c5aa 100644 Binary files a/Code/data/favicon.ico and b/Code/data/favicon.ico differ diff --git a/Code/data/favicon.png b/Code/data/favicon.png index 3ce0202d..49798e09 100644 Binary files a/Code/data/favicon.png and b/Code/data/favicon.png differ diff --git a/Code/data/function.js b/Code/data/function.js index dc8051f5..f39613d7 100644 --- a/Code/data/function.js +++ b/Code/data/function.js @@ -1,63 +1,74 @@ - function topNav() { - var x = document.getElementById("topnav"); - if (x.className === "topnav") { - x.className += " responsive"; - } else { - x.className = "topnav"; - } + var x = document.getElementById("topnav"); + if (x.className === "topnav") { + x.className += " responsive"; + } else { + x.className = "topnav"; + } } function togglePlainText(id) { - var x = document.getElementById(id); - if (x.type === "password") { - x.type = "text"; - } else { - x.type = "password"; - } + var x = document.getElementById(id); + if (x.type === "password") { + x.type = "text"; + } else { + x.type = "password"; + } } function validatePassword(id) { - var x = document.getElementById(id); - if (x.value == "") { - alert("Please enter a password to continue."); - return false; - } - return true; + var x = document.getElementById(id); + if (x.value == "") { + alert("Please enter a password to continue."); + return false; + } + return true; +} + +// Function to update the displayed number +function updateNumber(opt, parent) { + var parentElement = parent.parentElement; + var numDisplay = parentElement.querySelector(".numDisplay"); + var number = parseInt(numDisplay.textContent); + if (opt == "up") number += 1; + if (opt == "dn") number -= 1; + numDisplay.textContent = number; } function increaseNumber(id) { - var x = document.getElementById(id); - var val = Number(x.value); - var max = x.max; - if (max > val) { - val += 1; - x.value = val; - } + var x = document.getElementById(id); + var val = Number(x.value); + var max = x.max; + if (max > val) { + val += 1; + x.value = val; + } + var opt = "up"; + updateNumber(opt, x); } function decreaseNumber(id) { - var x = document.getElementById(id); - var val = Number(x.value); - var min = x.min; - if (min < val) { - val -= 1; - x.value = val; - } + var x = document.getElementById(id); + var val = Number(x.value); + var min = x.min; + if (min < val) { + val -= 1; + x.value = val; + } + var opt = "dn"; + updateNumber(opt, x); } -function buttonConfirm(elem, text='', timeout=3, reset=true) -{ - var originalText = elem.innerHTML; +function buttonConfirm(elem, text = "", timeout = 3, reset = true) { + var originalText = elem.innerHTML; - elem.innerHTML = (text == '' ? '✓' : text); - elem.disabled = true; + elem.innerHTML = text == "" ? "✓" : text; + elem.disabled = true; - if (reset) - { - setTimeout(function(){ - elem.innerHTML = originalText; - elem.disabled = false; - }, timeout*1000); - } + if (reset) { + setTimeout(function () { + elem.innerHTML = originalText; + elem.disabled = false; + }, timeout * 1000); + } } diff --git a/Code/data/hwconfig.html b/Code/data/hwconfig.html index 138623c8..409108dd 100644 --- a/Code/data/hwconfig.html +++ b/Code/data/hwconfig.html @@ -3,18 +3,26 @@ Lay-Z-Spa Module | Hardware Config - + + - - + +
- Hardware Config + +
+ +
+
@@ -68,7 +76,7 @@

Hardware:

Select your DSP (display) model: - ?

+ ?


@@ -349,5 +357,8 @@

Hardware:

} + + + diff --git a/Code/data/hwtestinfo.html b/Code/data/hwtestinfo.html index 541665a5..11b73123 100644 --- a/Code/data/hwtestinfo.html +++ b/Code/data/hwtestinfo.html @@ -3,18 +3,25 @@ Lay-Z-Spa Module | Hardware test - + + - - + +
- Hardware test +
+ +
+
@@ -44,6 +51,8 @@
+ + diff --git a/Code/data/index.html b/Code/data/index.html index 0cfd2e40..f47e785a 100644 --- a/Code/data/index.html +++ b/Code/data/index.html @@ -3,19 +3,27 @@ Lay-Z-Spa Module - + + - - + +
- Lay-Z-Spa Module + +
+ +
+
@@ -34,9 +42,17 @@ Restart ESP
+
+ + + + +
[DSP]
+
+
-

Temperature:

+

Temperature

@@ -53,16 +69,8 @@

Temperature:

Actual:
-
- - - - -
[DSP]
-
-
-

Control:

+

Control

@@ -91,21 +99,27 @@

Control:

@@ -114,7 +128,7 @@

Control:

-

Buttons:

+

Buttons

Temperature
- ° + +
+ +
- ° + +
+ +
- +
@@ -168,7 +182,7 @@

Buttons:

-

Timer:

+

Timer

Bubbles
@@ -182,7 +196,7 @@

Timer:

-

Totals:

+

Totals

Last chlorine add was n/a ago.
@@ -256,8 +270,8 @@

Totals:

function setWebConfig() { - document.getElementById('sectionTemperature').style.display = (localStorage.getItem('showSectionTemperature') !== 'false' ? 'block' : 'none'); document.getElementById('sectionDisplay').style.display = (localStorage.getItem('showSectionDisplay') !== 'false' ? 'block' : 'none'); + document.getElementById('sectionTemperature').style.display = (localStorage.getItem('showSectionTemperature') !== 'false' ? 'block' : 'none'); document.getElementById('sectionControl').style.display = (localStorage.getItem('showSectionControl') !== 'false' ? 'block' : 'none'); document.getElementById('sectionButtons').style.display = (localStorage.getItem('showSectionButtons') !== 'false' ? 'block' : 'none'); document.getElementById('sectionTimer').style.display = (localStorage.getItem('showSectionTimer') !== 'false' ? 'block' : 'none'); @@ -297,6 +311,12 @@

Totals:

sendCommand('resetTotals'); } } + + + + + + diff --git a/Code/data/index.js b/Code/data/index.js index b474c911..e8177288 100644 --- a/Code/data/index.js +++ b/Code/data/index.js @@ -3,41 +3,41 @@ var connection; // command mapping const cmdMap = { - setTarget: 0, - setTargetSelector: 0, - toggleUnit: 1, - toggleBubbles: 2, - toggleHeater: 3, - togglePump: 4, - //resetq: 5, - restartEsp: 6, - //gettarget: 7, - resetTotals: 8, - resetTimerChlorine: 9, - resetTimerFilter: 10, - toggleHydroJets: 11, - setBrightness: 12, - setBrightnessSelector: 12, - setBeep: 13, - setAmbient: 15, - setAmbientSelector: 15, - setAmbientF: 14, - setAmbientC: 15, - resetDaily: 16, - toggleGodmode: 17, - setFullpower:18, - printText: 19, - setReady: 20 + setTarget: 0, + setTargetSelector: 0, + toggleUnit: 1, + toggleBubbles: 2, + toggleHeater: 3, + togglePump: 4, + //resetq: 5, + restartEsp: 6, + //gettarget: 7, + resetTotals: 8, + resetTimerChlorine: 9, + resetTimerFilter: 10, + toggleHydroJets: 11, + setBrightness: 12, + setBrightnessSelector: 12, + setBeep: 13, + setAmbient: 15, + setAmbientSelector: 15, + setAmbientF: 14, + setAmbientC: 15, + resetDaily: 16, + toggleGodmode: 17, + setFullpower: 18, + printText: 19, + setReady: 20, }; // button element ID mapping const btnMap = { - toggleUnit: 'UNT', - toggleBubbles: 'AIR', - toggleHeater: 'HTR', - togglePump: 'FLT', - toggleHydroJets: 'HJT', - toggleGodmode: 'GOD' + toggleUnit: "UNT", + toggleBubbles: "AIR", + toggleHeater: "HTR", + togglePump: "FLT", + toggleHydroJets: "HJT", + toggleGodmode: "GOD", }; // to be used for setting the control values once after loading original values from the web socket @@ -54,286 +54,268 @@ updateBrtState = false; // initial connect to the web socket // connect(); -function connect() -{ - connection = new WebSocket('ws://'+location.hostname+':81/', ['arduino']); - - connection.onopen = function() - { - document.getElementById('header').style = "background-color: #00508F"; - initControlValues = true; - }; - - connection.onerror = function(error) - { - console.log('WebSocket Error ', error); - document.getElementById('header').style = "background-color: #FF0000"; - connection.close(); - }; - - connection.onclose = function() - { - console.log('WebSocket connection closed, reconnecting in 5 s'); - document.getElementById('header').style = "background-color: #FF0000"; - setTimeout(function(){connect()}, 5000); - }; - - connection.onmessage = function(e) - { - handlemsg(e); - } +function connect() { + connection = new WebSocket("ws://" + location.hostname + ":81/", ["arduino"]); + + connection.onopen = function () { + document.body.classList.add("connected"); + initControlValues = true; + }; + + connection.onerror = function (error) { + console.log("WebSocket Error ", error); + document.body.classList.add("error"); + connection.close(); + }; + + connection.onclose = function () { + console.log("WebSocket connection closed, reconnecting in 5 s"); + document.body.classList.add("error"); + setTimeout(function () { + connect(); + }, 5000); + }; + + connection.onmessage = function (e) { + handlemsg(e); + }; } -String.prototype.pad = function(String, len) -{ - var str = this; - while (str.length < len) - { - str = String + str; - } - return str; +String.prototype.pad = function (String, len) { + var str = this; + while (str.length < len) { + str = String + str; + } + return str; +}; + +function tryParseJSONObject(jsonString) { + try { + var o = JSON.parse(jsonString); + + // Handle non-exception-throwing cases: + // Neither JSON.parse(false) or JSON.parse(1234) throw errors, hence the type-checking, + // but... JSON.parse(null) returns null, and typeof null === "object", + // so we must check for that, too. Thankfully, null is falsey, so this suffices: + if (o && typeof o === "object") { + return o; + } + } catch (e) {} + + return false; } -function tryParseJSONObject (jsonString){ - try { - var o = JSON.parse(jsonString); - - // Handle non-exception-throwing cases: - // Neither JSON.parse(false) or JSON.parse(1234) throw errors, hence the type-checking, - // but... JSON.parse(null) returns null, and typeof null === "object", - // so we must check for that, too. Thankfully, null is falsey, so this suffices: - if (o && typeof o === "object") { - return o; - } +function handlemsg(e) { + console.log(e.data); + var msgobj = tryParseJSONObject(e.data); + if (!msgobj) return; + console.log(msgobj); + + if (msgobj.CONTENT == "OTHER") { + // MQTT status + mqtt_states = [ + "CONNECTION_TIMEOUT", // -4 / the server didn't respond within the keepalive time + "CONNECTION_LOST", // -3 / the network connection was broken + "CONNECT_FAILED", // -2 / the network connection failed + "DISCONNECTED", // -1 / the client is disconnected cleanly + "CONNECTED", // 0 / the client is connected + "CONNECT_BAD_PROTOCOL", // 1 / the server doesn't support the requested version of MQTT + "CONNECT_BAD_CLIENT_ID", // 2 / the server rejected the client identifier + "CONNECT_UNAVAILABLE", // 3 / the server was unable to accept the connection + "CONNECT_BAD_CREDENTIALS", // 4 / the username/password were rejected + "CONNECT_UNAUTHORIZED", // 5 / the client was not authorized to connect + ]; + document.getElementById("mqtt").innerHTML = "MQTT: " + mqtt_states[msgobj.MQTT + 4]; + document.getElementById("fw").innerHTML = "Firmware: " + msgobj.FW; + document.getElementById("model").innerHTML = "Model: " + msgobj.MODEL; + document.getElementById("rssi").innerHTML = "RSSI: " + msgobj.RSSI; + + // hydro jets available + document.getElementById("jets").style.display = msgobj.HASJETS ? "table-cell" : "none"; + document.getElementById("jetsswitch").style.display = msgobj.HASJETS ? "table-cell" : "none"; + document.getElementById("jetstotals").style.display = msgobj.HASJETS ? "table-cell" : "none"; + // godmode available + document.getElementById("god").style.display = msgobj.HASGOD ? "table-cell" : "none"; + document.getElementById("godswitch").style.display = msgobj.HASGOD ? "table-cell" : "none"; + } + + if (msgobj.CONTENT == "STATES") { + // temperature + document.getElementById("atlabel").innerHTML = msgobj.TMP.toString(); + document.getElementById("vtlabel").innerHTML = msgobj.VTM.toFixed(2).toString(); + document.getElementById("ttlabel").innerHTML = msgobj.TGT.toString(); + + // buttons + document.getElementById("AIR").checked = msgobj.AIR; + if (document.getElementById("UNT").checked != msgobj.UNT) { + document.getElementById("UNT").checked = msgobj.UNT; + initControlValues = true; + } + document.getElementById("FLT").checked = msgobj.FLT; + document.getElementById("HJT").checked = msgobj.HJT; + document.getElementById("GOD").checked = msgobj.GOD; + document.getElementById("HTR").checked = msgobj.RED || msgobj.GRN; + + // heater button color + document.getElementById("htrspan").classList.remove("heateron"); + document.getElementById("htrspan").classList.remove("heateroff"); + if (msgobj.RED || msgobj.GRN) { + document.getElementById("htrspan").classList.add(msgobj.RED ? "heateron" : msgobj.GRN ? "heateroff" : "n-o-n-e"); } - catch (e) { } - return false; -}; + // display + document.getElementById("display").innerHTML = "[" + String.fromCharCode(msgobj.CH1, msgobj.CH2, msgobj.CH3) + "]"; + document.getElementById("display").style.color = rgb(255 - dspBrtMultiplier * 8 + dspBrtMultiplier * (parseInt(msgobj.BRT) + 1), 0, 0); + + // set control values (once) + if (initControlValues) { + var minTemp = msgobj.UNT ? 20 : 68; + var maxTemp = msgobj.UNT ? 40 : 104; + var minAmb = msgobj.UNT ? -40 : -40; + var maxAmb = msgobj.UNT ? 60 : 140; + document.getElementById("temp").min = minTemp; + document.getElementById("temp").max = maxTemp; + document.getElementById("selectorTemp").min = minTemp; + document.getElementById("selectorTemp").max = maxTemp; + document.getElementById("amb").min = minAmb; + document.getElementById("amb").max = maxAmb; + document.getElementById("selectorAmb").min = minAmb; + document.getElementById("selectorAmb").max = maxAmb; + + document.getElementById("temp").value = msgobj.TGT; + document.getElementById("amb").value = msgobj.AMB; + document.getElementById("brt").value = msgobj.BRT; + + initControlValues = false; + } -function handlemsg(e) -{ - console.log(e.data); - var msgobj = tryParseJSONObject(e.data); - if(!msgobj) return; - console.log(msgobj); - - if (msgobj.CONTENT == "OTHER") - { - // MQTT status - mqtt_states = [ - "CONNECTION_TIMEOUT", // -4 / the server didn't respond within the keepalive time - "CONNECTION_LOST", // -3 / the network connection was broken - "CONNECT_FAILED", // -2 / the network connection failed - "DISCONNECTED", // -1 / the client is disconnected cleanly - "CONNECTED", // 0 / the client is connected - "CONNECT_BAD_PROTOCOL", // 1 / the server doesn't support the requested version of MQTT - "CONNECT_BAD_CLIENT_ID", // 2 / the server rejected the client identifier - "CONNECT_UNAVAILABLE", // 3 / the server was unable to accept the connection - "CONNECT_BAD_CREDENTIALS", // 4 / the username/password were rejected - "CONNECT_UNAUTHORIZED" // 5 / the client was not authorized to connect - ] - document.getElementById('mqtt').innerHTML = "MQTT: " + mqtt_states[msgobj.MQTT + 4]; - document.getElementById('fw').innerHTML = "Firmware: " + msgobj.FW; - document.getElementById('model').innerHTML = "Model: " + msgobj.MODEL; - document.getElementById('rssi').innerHTML = "RSSI: " + msgobj.RSSI; - - // hydro jets available - document.getElementById('jets').style.display = (msgobj.HASJETS ? 'table-cell' : 'none'); - document.getElementById('jetsswitch').style.display = (msgobj.HASJETS ? 'table-cell' : 'none'); - document.getElementById('jetstotals').style.display = (msgobj.HASJETS ? 'table-cell' : 'none'); - // godmode available - document.getElementById('god').style.display = (msgobj.HASGOD ? 'table-cell' : 'none'); - document.getElementById('godswitch').style.display = (msgobj.HASGOD ? 'table-cell' : 'none'); - } - - if (msgobj.CONTENT == "STATES") - { - // temperature - document.getElementById('atlabel').innerHTML = msgobj.TMP.toString(); - document.getElementById('vtlabel').innerHTML = msgobj.VTM.toFixed(2).toString(); - document.getElementById('ttlabel').innerHTML = msgobj.TGT.toString(); - - // buttons - document.getElementById('AIR').checked = msgobj.AIR; - if (document.getElementById('UNT').checked != msgobj.UNT) - { - document.getElementById('UNT').checked = msgobj.UNT; - initControlValues = true; - } - document.getElementById('FLT').checked = msgobj.FLT; - document.getElementById('HJT').checked = msgobj.HJT; - document.getElementById('GOD').checked = msgobj.GOD; - document.getElementById('HTR').checked = msgobj.RED || msgobj.GRN; - - // heater button color - document.getElementById('htrspan').classList.remove('heateron'); - document.getElementById('htrspan').classList.remove('heateroff'); - if (msgobj.RED || msgobj.GRN) - { - document.getElementById('htrspan').classList.add((msgobj.RED) ? 'heateron' : ((msgobj.GRN) ? 'heateroff' : 'n-o-n-e')); - } - - // display - document.getElementById('display').innerHTML = "[" + String.fromCharCode(msgobj.CH1,msgobj.CH2,msgobj.CH3)+ "]"; - document.getElementById('display').style.color = rgb((255-(dspBrtMultiplier*8))+(dspBrtMultiplier*(parseInt(msgobj.BRT)+1)), 0, 0); - - // set control values (once) - if (initControlValues) - { - var minTemp = (msgobj.UNT ? 20 : 68); - var maxTemp = (msgobj.UNT ? 40 : 104); - var minAmb = (msgobj.UNT ? -40 : -40); - var maxAmb = (msgobj.UNT ? 60 : 140); - document.getElementById('temp').min = minTemp; - document.getElementById('temp').max = maxTemp; - document.getElementById('selectorTemp').min = minTemp; - document.getElementById('selectorTemp').max = maxTemp; - document.getElementById('amb').min = minAmb; - document.getElementById('amb').max = maxAmb; - document.getElementById('selectorAmb').min = minAmb; - document.getElementById('selectorAmb').max = maxAmb; - - document.getElementById('temp').value = msgobj.TGT; - document.getElementById('amb').value = msgobj.AMB; - document.getElementById('brt').value = msgobj.BRT; - - initControlValues = false; - } - - document.getElementById('sliderTempVal').innerHTML = msgobj.TGT; - document.getElementById('sliderAmbVal').innerHTML = msgobj.AMB; - document.getElementById('sliderBrtVal').innerHTML = msgobj.BRT; - - // get selector elements - var elemSelectorTemp = document.getElementById('selectorTemp'); - var elemSelectorAmb = document.getElementById('selectorAmb'); - var elemSelectorBrt = document.getElementById('selectorBrt'); - - // change values only if element is not active (selected for input) - // also change only if an update is not in progress - if (document.activeElement !== elemSelectorTemp && !updateTempState) elemSelectorTemp.value = msgobj.TGT; - if (document.activeElement !== elemSelectorAmb && !updateAmbState) elemSelectorAmb.value = msgobj.AMB; - if (document.activeElement !== elemSelectorBrt && !updateBrtState) elemSelectorBrt.value = msgobj.BRT; - - // reset update states when the set target matches the input - if (elemSelectorTemp.value == msgobj.TGT) updateTempState = false; - if (elemSelectorAmb.value == msgobj.AMB) updateAmbState = false; - if (elemSelectorBrt.value == msgobj.BRT) updateBrtState = false; - } - - if (msgobj.CONTENT == "TIMES") - { - var date = new Date(msgobj.TIME * 1000); - document.getElementById('time').innerHTML = date.toLocaleString(); - - // chlorine add reset timer - var clDate = (Date.now()/1000-msgobj.CLTIME)/(24*3600.0); - var clDateRound = Math.round(clDate); - document.getElementById('cltimer').innerHTML = clDateRound + ' day' + (clDateRound != 1 ? 's' : ''); - document.getElementById('cltimerbtn').className = (clDate > msgobj.CLINT ? 'button_red' : 'button'); - - // filter change reset timer - var fDate = (Date.now()/1000-msgobj.FTIME)/(24*3600.0); - var fDateRound = Math.round(fDate); - document.getElementById('ftimer').innerHTML = fDateRound + ' day' + (fDateRound != 1 ? 's' : ''); - document.getElementById('ftimerbtn').className = (fDate > msgobj.FINT ? 'button_red' : 'button'); - - // statistics - document.getElementById('heatingtime').innerHTML = s2dhms(msgobj.HEATINGTIME); - document.getElementById('uptime').innerHTML = s2dhms(msgobj.UPTIME); - document.getElementById('airtime').innerHTML = s2dhms(msgobj.AIRTIME); - document.getElementById('filtertime').innerHTML = s2dhms(msgobj.PUMPTIME); - document.getElementById('jettime').innerHTML = s2dhms(msgobj.JETTIME); - document.getElementById('cost').innerHTML = (msgobj.COST).toFixed(2); - document.getElementById('t2r').innerHTML = s2dhms(msgobj.T2R * 3600) + " (" + msgobj.RS + ")"; - } -}; + document.getElementById("sliderTempVal").innerHTML = msgobj.TGT; + document.getElementById("sliderAmbVal").innerHTML = msgobj.AMB; + document.getElementById("sliderBrtVal").innerHTML = msgobj.BRT; + + // get selector elements + var elemSelectorTemp = document.getElementById("selectorTemp"); + var elemSelectorAmb = document.getElementById("selectorAmb"); + var elemSelectorBrt = document.getElementById("selectorBrt"); + + // change values only if element is not active (selected for input) + // also change only if an update is not in progress + if (document.activeElement !== elemSelectorTemp && !updateTempState) { + elemSelectorTemp.value = msgobj.TGT; + elemSelectorTemp.parentElement.querySelector(".numDisplay").textContent = msgobj.TGT; + } + if (document.activeElement !== elemSelectorAmb && !updateAmbState) { + elemSelectorAmb.value = msgobj.AMB; + elemSelectorAmb.parentElement.querySelector(".numDisplay").textContent = msgobj.AMB; + } + if (document.activeElement !== elemSelectorBrt && !updateBrtState) elemSelectorBrt.value = msgobj.BRT; + + // reset update states when the set target matches the input + if (elemSelectorTemp.value == msgobj.TGT) updateTempState = false; + if (elemSelectorAmb.value == msgobj.AMB) updateAmbState = false; + if (elemSelectorBrt.value == msgobj.BRT) updateBrtState = false; + } + + if (msgobj.CONTENT == "TIMES") { + var date = new Date(msgobj.TIME * 1000); + document.getElementById("time").innerHTML = date.toLocaleString(); + + // chlorine add reset timer + var clDate = (Date.now() / 1000 - msgobj.CLTIME) / (24 * 3600.0); + var clDateRound = Math.round(clDate); + document.getElementById("cltimer").innerHTML = clDateRound + " day" + (clDateRound != 1 ? "s" : ""); + document.getElementById("cltimerbtn").className = clDate > msgobj.CLINT ? "button_red" : "button"; + + // filter change reset timer + var fDate = (Date.now() / 1000 - msgobj.FTIME) / (24 * 3600.0); + var fDateRound = Math.round(fDate); + document.getElementById("ftimer").innerHTML = fDateRound + " day" + (fDateRound != 1 ? "s" : ""); + document.getElementById("ftimerbtn").className = fDate > msgobj.FINT ? "button_red" : "button"; + + // statistics + document.getElementById("heatingtime").innerHTML = s2dhms(msgobj.HEATINGTIME); + document.getElementById("uptime").innerHTML = s2dhms(msgobj.UPTIME); + document.getElementById("airtime").innerHTML = s2dhms(msgobj.AIRTIME); + document.getElementById("filtertime").innerHTML = s2dhms(msgobj.PUMPTIME); + document.getElementById("jettime").innerHTML = s2dhms(msgobj.JETTIME); + document.getElementById("cost").innerHTML = msgobj.COST.toFixed(2); + document.getElementById("t2r").innerHTML = s2dhms(msgobj.T2R * 3600) + " (" + msgobj.RS + ")"; + } +} -function s2dhms(val) -{ - var day = 3600*24; - var hour = 3600; - var minute = 60; - var rem; - var days = Math.floor(val/day); - rem = val % day; - var hours = Math.floor(rem/hour); - rem = val % hour; - var minutes = Math.floor(rem/minute); - rem = val % minute; - var seconds = Math.floor(rem); - return days + "d " + hours.toString().pad("0", 2) + ":" + minutes.toString().pad("0", 2) + ":" + seconds.toString().pad("0", 2); +function s2dhms(val) { + var day = 3600 * 24; + var hour = 3600; + var minute = 60; + var rem; + var days = Math.floor(val / day); + rem = val % day; + var hours = Math.floor(rem / hour); + rem = val % hour; + var minutes = Math.floor(rem / minute); + rem = val % minute; + var seconds = Math.floor(rem); + return days + "d " + hours.toString().pad("0", 2) + ":" + minutes.toString().pad("0", 2) + ":" + seconds.toString().pad("0", 2); } -function sendCommand(cmd) -{ - console.log(cmd); - console.log(typeof(cmdMap[cmd])); - // check command - if (typeof(cmdMap[cmd]) == 'undefined') - { - console.log("invalid command"); - return; - } - - // get the current unit (true=C, false=F) - var unit = (document.getElementById('UNT').checked); - - // get and set value - var value = 0; - if (cmd == 'setTarget' || cmd == 'setTargetSelector') - { - value = parseInt(document.getElementById((cmd == 'setTarget') ? 'temp' : 'selectorTemp').value); - value = getProperValue(value, (unit ? 20 : 68), (unit ? 40 : 104)); - document.getElementById('sliderTempVal').innerHTML = value.toString(); - document.getElementById('selectorTemp').value = value.toString(); - updateTempState = true; - } - else if (cmd == 'setAmbient' || cmd == 'setAmbientSelector') - { - value = parseInt(document.getElementById((cmd == 'setAmbient') ? 'amb' : 'selectorAmb').value); - value = getProperValue(value, (unit ? -40 : -40), (unit ? 60 : 140)); - document.getElementById('sliderAmbVal').innerHTML = value.toString(); - document.getElementById('selectorAmb').value = value.toString(); - cmd = 'setAmbient' + (unit ? 'C' : 'F'); - updateAmbState = true; - } - else if (cmd == 'setBrightness' || cmd == 'setBrightnessSelector') - { - value = parseInt(document.getElementById((cmd == 'setBrightness') ? 'brt' : 'selectorBrt').value); - value = getProperValue(value, 0, 8); - document.getElementById("sliderBrtVal").innerHTML = value.toString(); - document.getElementById('selectorBrt').value = value.toString(); - document.getElementById("display").style.color = rgb((255-(dspBrtMultiplier*8))+(dspBrtMultiplier*(value+1)), 0, 0); - updateBrtState = true; - } - else if (btnMap[cmd] && (cmd == 'toggleUnit' || cmd == 'toggleBubbles' || cmd == 'toggleHeater' || cmd == 'togglePump' || cmd == 'toggleHydroJets' || cmd == 'toggleGodmode')) - { - value = document.getElementById(btnMap[cmd]).checked; - initControlValues = true; - } - - var obj = {}; - obj["CMD"] = cmdMap[cmd]; - obj["VALUE"] = value; - obj["XTIME"] = Math.floor(Date.now()/1000); - obj["INTERVAL"] = 0; - obj["TXT"] = ""; - var json = JSON.stringify(obj); - connection.send(json); - console.log(json); +function sendCommand(cmd) { + console.log(cmd); + console.log(typeof cmdMap[cmd]); + // check command + if (typeof cmdMap[cmd] == "undefined") { + console.log("invalid command"); + return; + } + + // get the current unit (true=C, false=F) + var unit = document.getElementById("UNT").checked; + + // get and set value + var value = 0; + if (cmd == "setTarget" || cmd == "setTargetSelector") { + value = parseInt(document.getElementById(cmd == "setTarget" ? "temp" : "selectorTemp").value); + value = getProperValue(value, unit ? 20 : 68, unit ? 40 : 104); + document.getElementById("sliderTempVal").innerHTML = value.toString(); + document.getElementById("selectorTemp").value = value.toString(); + document.getElementById("selectorTemp").setAttribute("value", value.toString()); + updateTempState = true; + } else if (cmd == "setAmbient" || cmd == "setAmbientSelector") { + value = parseInt(document.getElementById(cmd == "setAmbient" ? "amb" : "selectorAmb").value); + value = getProperValue(value, unit ? -40 : -40, unit ? 60 : 140); + document.getElementById("sliderAmbVal").innerHTML = value.toString(); + document.getElementById("selectorAmb").value = value.toString(); + cmd = "setAmbient" + (unit ? "C" : "F"); + updateAmbState = true; + } else if (cmd == "setBrightness" || cmd == "setBrightnessSelector") { + value = parseInt(document.getElementById(cmd == "setBrightness" ? "brt" : "selectorBrt").value); + value = getProperValue(value, 0, 8); + document.getElementById("sliderBrtVal").innerHTML = value.toString(); + document.getElementById("selectorBrt").value = value.toString(); + document.getElementById("display").style.color = rgb(255 - dspBrtMultiplier * 8 + dspBrtMultiplier * (value + 1), 0, 0); + updateBrtState = true; + } else if (btnMap[cmd] && (cmd == "toggleUnit" || cmd == "toggleBubbles" || cmd == "toggleHeater" || cmd == "togglePump" || cmd == "toggleHydroJets" || cmd == "toggleGodmode")) { + value = document.getElementById(btnMap[cmd]).checked; + initControlValues = true; + } + + var obj = {}; + obj["CMD"] = cmdMap[cmd]; + obj["VALUE"] = value; + obj["XTIME"] = Math.floor(Date.now() / 1000); + obj["INTERVAL"] = 0; + obj["TXT"] = ""; + var json = JSON.stringify(obj); + connection.send(json); + console.log(json); } -function getProperValue(val, min, max) -{ - return (val < min ? min : (val > max ? max : val)); +function getProperValue(val, min, max) { + return val < min ? min : val > max ? max : val; } -function rgb(r, g, b) -{ - r = Math.floor(r); - g = Math.floor(g); - b = Math.floor(b); - return ["rgb(",r,",",g,",",b,")"].join(""); +function rgb(r, g, b) { + r = Math.floor(r); + g = Math.floor(g); + b = Math.floor(b); + return ["rgb(", r, ",", g, ",", b, ")"].join(""); } diff --git a/Code/data/main.css b/Code/data/main.css index 52ecb499..bb206ee0 100644 --- a/Code/data/main.css +++ b/Code/data/main.css @@ -1,454 +1,732 @@ - +:root { + --accent-color: #0f4677; + --action-btn-color-dark: #cfcfcf9c; + --action-btn-color: #9898989c; + --btn-round-color: #545454; +} body { - font-family: 'Roboto', sans-serif; - color: #444; + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", + "Segoe UI Symbol", "Noto Color Emoji"; + color: #444; + margin: 0; } - -div#site { - width: 420px; - max-width: 100%; - margin: 0px auto; - box-shadow: 0 0 3px #555; +.darkmode body { + color: #b0b0b0; } - -header, section, footer { - position: relative; - padding: 6px; +div#site { + width: 420px; + max-width: 100%; + margin: 0px auto; + box-sizing: border-box; + background: #dfdfdf; + overflow: hidden; + position: relative; + min-height: 100vh; +} +.darkmode #site { + background: #333; +} +header, +section, +footer { + position: relative; + padding: 6px; } header { - text-align: center; - background-color: #00508F; - padding: 14px 6px + text-align: center; + background-color: var(--accent-color); + padding: 15px 20px; + display: flex; + justify-content: space-between; + align-items: center; + width: calc(100% + 20px); + box-sizing: border-box; + left: -10px; + z-index: 12; + height: 66px; +} +#header { + font-size: 1em; + position: relative; + font-weight: normal; + color: #fff; + display: flex; + flex-direction: column; + align-items: flex-start; +} +#header span:nth-child(1) { + font-size: 0.9em; + text-transform: uppercase; +} +#header span:nth-child(2) { + font-size: 0.8em; + text-transform: lowercase; +} +#header::before { + content: ""; + background-image: url("./visualapproach.png"); + width: 38px; + height: 38px; + background-size: contain; + background-repeat: no-repeat; + position: absolute; + left: -43px; + filter: grayscale(0.2); + top: -1px; } -header span { - font-size: 1.4em; - font-weight: bold; - color: #FFF; -} - section { - border-bottom: 1px dashed #00508F; -} - -footer { - font-size: 0.6em; + padding: 1em 1em; + background: #e2e2e2; +} +section:nth-of-type(2n + 1) { + background: #ededed; +} +.darkmode section { + background: #4d4d4d; +} +.darkmode section:nth-of-type(2n + 1) { + background: #3a3a3a; +} +#sectionDisplay { + padding: 0; + width: calc(100% + 20px); + left: -10px; + box-sizing: border-box; + height: 47px; + background: #000; + display: flex; + align-items: center; + justify-content: center; + border: none; } - div#display { - width: 100px; - color: red; - font-size: 1.8em; - text-align: center; - border-radius: 34px; - background-color: black; - margin: 4px auto; + color: red; + font-size: 1.8em; + text-align: center; + background-color: black; + width: 100%; + height: 100%; +} +.darkmode #display { + color: #b23434; } - h2 { - margin: 4px 0 10px; - font-size: 1.1em; + margin: 4px 0 10px; + font-size: 1.1em; + text-transform: capitalize; + font-weight: 200; + letter-spacing: 0.6px; + color: #6b6b6b; +} +.darkmode h2 { + color: #848484; } - p { - padding: 3px 0; - margin: 0; + padding: 3px 0; + margin: 0; } table { - width: 100%; + width: 100%; } table.fixed { - table-layout: fixed; + table-layout: fixed; } .center { - text-align: center; + text-align: center; } .right { - text-align: right; + text-align: right; } .small { - font-size: 0.6em; + font-size: 0.6em; } .smaller { - font-size: smaller; + font-size: smaller; } .larger { - font-size: larger; -} - -.button, .button_red { - text-decoration: none; - border: none; - color: white; - background-color: #00508F; - padding: 6px 24px; - font-size: 16px; - cursor: pointer; - border: 2px solid white; - border-radius: 34px; - box-shadow: 1px 1px 2px #444; - outline: none; - margin: 4px auto; - display: inline-block; + font-size: larger; } +.button, .button_red { - background-color: #bf0000; + text-decoration: none; + border: none; + color: white; + background-color: var(--accent-color); + padding: 8px 24px; + font-size: 0.8em; + cursor: pointer; + border: 1px solid #d6d6d6; + border-radius: 34px; + outline: none; + margin: 4px auto; + display: inline-block; + text-transform: uppercase; + letter-spacing: 1.1px; } -.button:hover, .button_red:hover { - background-color: #0076d1; - box-shadow: 1px 1px 3px #444; +.button_red { + background-color: #bf0000; +} +.button:hover, +.button_red:hover { + background-color: #0076d1; + box-shadow: 1px 1px 3px #444; } .button_red:hover { - background-color: #ec0000; + background-color: #ec0000; } -.button:disabled, .button_red:disabled { - background-color: green; - box-shadow: none; - cursor: default; +.button:disabled, +.button_red:disabled { + background-color: green; + box-shadow: none; + cursor: default; } div.visualapproach { - position: absolute; - right: 0px; - top: 10px; - width: 128px; - height: 128px; - opacity: 0.4; - background-image: url('./visualapproach.png'); - background-repeat: no-repeat; + position: absolute; + right: 0px; + top: 10px; + width: 100vw; + height: 100vh; + opacity: 0.09; + background-image: url("./visualapproach.png"); + background-repeat: no-repeat; + display: none; + filter: grayscale(1); + background-size: contain; } /* input range slider */ input[type="range"] { - -webkit-appearance: none; - width: 100%; - height: 34px; - background: #ccc; - border-radius: 34px; - padding: 0 4px; - margin: 0; + -webkit-appearance: none; + width: 100%; + height: 34px; + background: #ccc; + border-radius: 34px; + padding: 0 4px; + margin: 0; +} +.darkmode input[type="range"] { + background: var(--action-btn-color-dark); } - input[type="range"]::-webkit-slider-thumb { - -webkit-appearance: none; - height: 22px; - width: 22px; - border-radius: 50%; - background: #00508F; - cursor: ew-resize; - border: 2px solid white; - transition: background .3s ease-in-out; + -webkit-appearance: none; + height: 22px; + width: 22px; + border-radius: 50%; + background: var(--accent-color); + cursor: ew-resize; + border: 2px solid white; + transition: background 0.3s ease-in-out; } input[type="range"]::-moz-range-thumb { - -webkit-appearance: none; - height: 22px; - width: 22px; - border-radius: 50%; - background: #00508F; - cursor: ew-resize; - border: 2px solid white; - transition: background .3s ease-in-out; + -webkit-appearance: none; + height: 25px; + width: 25px; + border-radius: 50%; + background: var(--btn-round-color); + cursor: ew-resize; + border: none; + transition: background 0.3s ease-in-out; } input[type="range"]::-ms-thumb { - -webkit-appearance: none; - height: 22px; - width: 22px; - border-radius: 50%; - background: #00508F; - cursor: ew-resize; - border: 2px solid white; - transition: background .3s ease-in-out; + -webkit-appearance: none; + height: 22px; + width: 22px; + border-radius: 50%; + background: var(--accent-color); + cursor: ew-resize; + border: 2px solid white; + transition: background 0.3s ease-in-out; } input[type="range"]::-webkit-slider-thumb:hover { - background: #00508F; + background: var(--accent-color); } input[type="range"]::-moz-range-thumb:hover { - background: #00508F; + background: var(--accent-color); } input[type="range"]::-ms-thumb:hover { - background: #00508F; + background: var(--accent-color); } /* Input Track */ -input[type=range]::-webkit-slider-runnable-track { - -webkit-appearance: none; - box-shadow: none; - border: none; - background: transparent; +input[type="range"]::-webkit-slider-runnable-track { + -webkit-appearance: none; + box-shadow: none; + border: none; + background: transparent; } -input[type=range]::-moz-range-track { - -webkit-appearance: none; - box-shadow: none; - border: none; - background: transparent; +input[type="range"]::-moz-range-track { + -webkit-appearance: none; + box-shadow: none; + border: none; + background: transparent; } input[type="range"]::-ms-track { - -webkit-appearance: none; - box-shadow: none; - border: none; - background: transparent; + -webkit-appearance: none; + box-shadow: none; + border: none; + background: transparent; } /* The switch - the box around the slider */ .switch { - position: relative; - display: inline-block; - width: 60px; - height: 34px; + position: relative; + display: inline-block; + width: 60px; + height: 34px; } /* Hide default HTML checkbox */ .switch input { - opacity: 0; - width: 0; - height: 0; + opacity: 0; + width: 0; + height: 0; } /* The slider */ .slider { - position: absolute; - cursor: pointer; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: #ccc; - -webkit-transition: .4s; - transition: .4s; + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: 0.4s; + transition: 0.4s; } .slider:before { - position: absolute; - content: ""; - height: 22px; - width: 22px; - left: 4px; - bottom: 4px; - background-color: #00508F; - border: 2px solid white; - -webkit-transition: .4s; - transition: .4s; + position: absolute; + content: ""; + height: 22px; + width: 22px; + left: 4px; + bottom: 4px; + background-color: var(--accent-color); + border: 2px solid white; + -webkit-transition: 0.4s; + transition: 0.4s; } input:checked + .slider { - background-color: #00508F; + background-color: var(--accent-color); } input:focus + .slider { - box-shadow: 0 0 1px #2196F3; + box-shadow: 0 0 1px #2196f3; } input:checked + .slider:before { - -webkit-transform: translateX(26px); - -ms-transform: translateX(26px); - transform: translateX(26px); - background-color: #ccc; -} - -input:checked + .heateron:before { - background-color: red; + -webkit-transform: translateX(16px); + -ms-transform: translateX(16px); + transform: translateX(16px); + background-color: #ccc; +} +.sun::after { + content: ""; + width: 8px; + height: 8px; + border: solid 1px #797b40; + border-radius: 50%; + box-shadow: 8px 0 0 -4px #797b40, -8px 0 0 -4px #797b40, 0 8px 0 -4px #797b40, 0 -8px 0 -4px #797b40, 6px -6px 0 -4px #797b40, -6px -6px 0 -4px #797b40, -6px 6px 0 -4px #797b40, + 6px 6px 0 -4px #797b40; + position: absolute; + left: 28px; + top: 11px; + -webkit-transition: 0.4s; + transition: 0.4s; +} +.moon::after { + content: ""; + width: 1.1rem; + height: 1.1rem; + border-radius: 50%; + box-shadow: 0.3rem 0.2rem 0 0 #909090; + position: absolute; + left: 2px; + top: 4px; + -webkit-transition: 0.4s; + transition: 0.4s; +} +input:checked + .slider.moon::after { + -webkit-transform: translateX(16px); + -ms-transform: translateX(16px); + transform: translateX(16px); +} +input:checked + .heateron::before { + border: 3px solid #bc3434; + box-sizing: border-box; + content: "On"; + font-size: 0.6em; + display: flex; + justify-content: center; + align-items: center; } input:checked + .heateroff:before { - background-color: green; + border: 3px solid green; + box-sizing: border-box; + content: "Off"; + font-size: 0.6em; + display: flex; + justify-content: center; + align-items: center; } /* Rounded sliders */ .slider.round { - border-radius: 34px; + border-radius: 34px; } .slider.round:before { - border-radius: 50%; + border-radius: 50%; } /* navigation */ a.topnavicon { - float: right; - display: block; - width: 26px; - height: 26px; - background-image: url('./menu.png'); - background-repeat: no-repeat; - background-size: 26px 26px; + width: 30px; + height: 30px; + text-decoration: none; + color: #fff; + font-size: 2.2em; + display: flex; + justify-content: center; + align-items: center; +} +.darkmode .topnavicon { + color: #b9b9b9; +} +.topnavicon::before { + content: "\00D7"; + line-height: 0.8; + top: 13px; + position: absolute; + opacity: 0; + -webkit-transition: 0.2s; + transition: 0.2s; +} +.show-before.topnavicon::before { + opacity: 1; +} +.topnavicon::after { + content: "\2630"; + line-height: 0.8; + top: 13px; + position: absolute; + -webkit-transition: 0.2s; + transition: 0.2s; + opacity: 1; + display: inline-block; +} +.show-before.topnavicon::after { + opacity: 0; } - .topnav { - overflow: hidden; - background-color: #ccc; + overflow: hidden; + background-color: #ccc; + width: calc(100% + 20px); + left: -10px; + position: absolute; + top: -100vh; + z-index: 10; + height: calc(100vh - 66px); + display: flex; + flex-direction: column; + justify-content: flex-start; + transition: 0.3s all; + gap: 0.7em; + padding-top: 1em; +} +.darkmode .topnav { + background: linear-gradient(180deg, rgb(65, 65, 65) 0%, rgb(44, 44, 44) 100%); } - .topnav a { - float: left; - display: none; - color: #444; - text-align: center; - padding: 6px 16px; - text-decoration: none; - font-size: 1.1em; + float: left; + display: none; + color: inherit; + text-align: center; + padding: 12px 16px; + text-decoration: none; + font-size: 1em; + font-weight: 300; + letter-spacing: 0.04em; +} +.darkmode .topnav a { + background: #454545; + box-shadow: 0px 1px 2px #0000004d; } - .topnav a.bgred { - color: white; - background-color: #bf0000; + color: white; + background-color: #bf0000; } .topnav a:hover { - color: #444; - background-color: #ddd; + color: #444; + background-color: #ddd; } .topnav a.active { - background-color: #04AA6D; - color: white; + background-color: #2a5e8b99; + color: white; } -.topnav.responsive {position: relative;} +.topnav.responsive { + transition: 0.3s all; + top: 66px; +} -.topnav.responsive a { - float: none; - display: block; - text-align: center; +.topnav a { + float: none; + display: block; + text-align: center; } /* tooltips */ .tooltip { - position: relative; - display: inline-block; - width: 1.2em; - height: 1.2em; - font-size: 1em; - text-align: center; - text-decoration: none; - color: #fff; - background-color: #00508F; - border-radius: 34%; - -webkit-box-shadow: inset -1px -1px 1px 0px rgba(0,0,0,0.25); - -moz-box-shadow: inset -1px -1px 1px 0px rgba(0,0,0,0.25); - box-shadow: inset -1px -1px 1px 0px rgba(0,0,0,0.25); + position: relative; + display: inline-block; + width: 1.2em; + height: 1.2em; + font-size: 1em; + text-align: center; + text-decoration: none; + color: #fff; + background-color: var(--accent-color); + border-radius: 34%; + -webkit-box-shadow: inset -1px -1px 1px 0px rgba(0, 0, 0, 0.25); + -moz-box-shadow: inset -1px -1px 1px 0px rgba(0, 0, 0, 0.25); + box-shadow: inset -1px -1px 1px 0px rgba(0, 0, 0, 0.25); } .tooltip:before { - content: attr(data-text); - position: absolute; - top: 30%; - transform: translateY(-30%); - left: 100%; - margin-left: 15px; - width: 200px; - color: #444; - background: #ccc; - text-align: left; - padding: 6px; - border-radius: 14px; - border: 2px solid #00508F; - z-index: 1; - /* display: none; */ - visibility: hidden; - /* opacity: 0; + content: attr(data-text); + position: absolute; + top: 30%; + transform: translateY(-30%); + left: 100%; + margin-left: 15px; + width: 200px; + color: #444; + background: #ccc; + text-align: left; + padding: 6px; + border-radius: 14px; + border: 2px solid var(--accent-color); + z-index: 1; + /* display: none; */ + visibility: hidden; + /* opacity: 0; transition: .3s opacity; */ } .tooltip.left:before { - left: initial; - margin: initial; - right: 100%; - margin-right: 15px; + left: initial; + margin: initial; + right: 100%; + margin-right: 15px; } .tooltip:after { - content: ""; - position: absolute; - top: 30%; - transform: translateY(-30%); - left: 100%; - margin-left: -5px; - border: 10px solid #00508F; - border-color: transparent #00508F transparent transparent; - /* display: none; */ - visibility: hidden; - /* opacity: 0; + content: ""; + position: absolute; + top: 30%; + transform: translateY(-30%); + left: 100%; + margin-left: -5px; + border: 10px solid var(--accent-color); + border-color: transparent var(--accent-color) transparent transparent; + /* display: none; */ + visibility: hidden; + /* opacity: 0; transition: .3s opacity; */ } .tooltip.left:after { - left: initial; - margin: initial; - right: 100%; - margin-right: -5px; - border-color: transparent transparent transparent #00508F; + left: initial; + margin: initial; + right: 100%; + margin-right: -5px; + border-color: transparent transparent transparent var(--accent-color); } -.tooltip:hover:before, .tooltip:hover:after { - /* display: block; */ - visibility: visible; - /* opacity: 1; */ +.tooltip:hover:before, +.tooltip:hover:after { + /* display: block; */ + visibility: visible; + /* opacity: 1; */ } - /********************** * temperture selector \*/ table#tableSelector { - display: none; + display: none; } .selector { - position: relative; - width: 100px; - background-color: #ccc; - border-radius: 6px; - margin: auto; + position: relative; + width: 100px; + border-radius: 6px; + margin: auto; } .selector .selectorbutton { - width: 100%; - height: 30px; - color: #fff; - font-size: 20px; - background-color: #00508F; - border: none; - cursor: pointer; + width: 100%; + height: 30px; + color: #fff; + font-size: 20px; + background-color: var(--accent-color); + border: none; + cursor: pointer; } .selector .selectorbutton:hover { - background-color: #0076d1; - box-shadow: 0 0 3px #444; + background-color: #0076d1; } .selector .selectorbutton.selectortop { - border-radius: 6px 6px 0 0; + border-radius: 10px 10px 0 0; } .selector .selectorbutton.selectorbottom { - border-radius: 0 0 6px 6px; + border-radius: 0 0 10px 10px; +} +.selector .wrapper { + background: #ccc; + display: flex; + justify-content: center; + align-items: center; +} +.selector .numDisplay { + width: fit-content; + height: 50px; + border: none; + color: var(--accent-color); + font-size: 34px; + text-align: center; + padding: 0; + border-radius: 0; + appearance: textfield; + pointer-events: none; + display: flex; + justify-content: center; + align-items: center; + position: relative; + left: -2px; +} +.selector .numDisplay::after { + content: "\00B0"; + position: absolute; + right: -14px; + top: 0; + font-weight: 200; } - .selector .selectorvalue { - width: 100%; - height: 50px; - border: none; - color: #00508F; - font-size: 34px; - text-align: center; - background-color: #ccc; - padding: 0; + width: 100%; + height: 50px; + border: none; + color: var(--accent-color); + font-size: 34px; + text-align: center; + background-color: #ccc; + padding: 0; + border-radius: 0; + appearance: textfield; + pointer-events: none; } .selector .degree { - position: absolute; - top: 32px; - right: 20px; - font-size: 24px; - color: #00508F; + position: absolute; + top: 32px; + right: 20px; + font-size: 24px; + color: var(--accent-color); +} + +/* dark mode */ +html.darkmode { + background: #282828; +} +/* toggle button */ +.switch { + position: relative; + width: 50px; + height: 30px; + float: left; +} + +.toggle-switch input { + opacity: 0; + width: 0; + height: 0; +} + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #a0a0a09c; + -webkit-transition: 0.4s; + transition: 0.4s; + border-radius: 34px; +} +.darkmode .slider { + background-color: var(--action-btn-color-dark); +} +.slider:before { + position: absolute; + content: ""; + height: 25px; + width: 25px; + left: 4px; + bottom: 2px; + background-color: var(--btn-round-color); + -webkit-transition: 0.4s; + transition: 0.4s; + border-radius: 50%; + border: none; +} + +input:checked + .slider { + background-color: #c3c3c3a8; +} + +input:focus + .slider { + box-shadow: 0 0 1px #2196f3; +} + +input:checked + .slider:before { + -webkit-transform: translateX(16px); + -ms-transform: translateX(26px); + transform: translateX(16px); + background-color: #3c3c3c; +} + +/* error handling css */ +.error header { + background-color: rgb(132, 39, 39); + animation: colorfade 1s linear 0s infinite alternate-reverse; +} +@keyframes colorfade { + 0% { + background-color: rgb(132, 39, 39); + } + 100% { + background-color: rgb(172, 82, 82); + } } diff --git a/Code/data/manifest.json b/Code/data/manifest.json index b5ecc3ad..bc5dff30 100644 --- a/Code/data/manifest.json +++ b/Code/data/manifest.json @@ -1,11 +1,17 @@ { - "name": "Lay-Z-Spa Module", - "icons": [ - { - "src": "\/favicon.png", - "sizes": "48x48", - "type": "image\/png", - "density": "3.0" - } - ] -} + "name": "Lay-Z-Spa Module", + "short_name": "Hot Tub WiFi", + "display": "fullscreen", + "theme_color": "#0f4677", + "background_color": "#fff", + "orientation": "portrait", + "description": "An app to access the WiFi module of your Lay-Z-Spa Hot Tub", + "icons": [ + { + "src": "\/favicon.png", + "sizes": "180x180", + "type": "image\/png", + "density": "3.0" + } + ] +} \ No newline at end of file diff --git a/Code/data/mqtt.html b/Code/data/mqtt.html index 570e3539..b001799e 100644 --- a/Code/data/mqtt.html +++ b/Code/data/mqtt.html @@ -3,18 +3,25 @@ Lay-Z-Spa Module | MQTT Config - + + - - + +
- MQTT Config +
+ + +
@@ -138,5 +145,7 @@ console.log(json); } + + diff --git a/Code/data/remove.html b/Code/data/remove.html index 2f09ccf9..e235f343 100644 --- a/Code/data/remove.html +++ b/Code/data/remove.html @@ -3,18 +3,25 @@ Lay-Z-Spa Module | File Remover - + + - - + +
- File Remover +
+ + +
@@ -44,5 +51,7 @@
+ + diff --git a/Code/data/success.html b/Code/data/success.html index 4a856a88..d80e2c57 100644 --- a/Code/data/success.html +++ b/Code/data/success.html @@ -3,18 +3,25 @@ Lay-Z-Spa Module | Success! - + + - - + +
- Success! +
+ + +
@@ -42,5 +49,7 @@

Remove a file

+ + diff --git a/Code/data/upload.html b/Code/data/upload.html index 6e865d9d..2a046dd7 100644 --- a/Code/data/upload.html +++ b/Code/data/upload.html @@ -3,18 +3,25 @@ Lay-Z-Spa Module | File Uploader - + + - - + +
- File Uploader +
+ + +
@@ -40,10 +47,14 @@
- + + +
+ + diff --git a/Code/data/visualapproach.png b/Code/data/visualapproach.png index c06ed3c9..4319359c 100644 Binary files a/Code/data/visualapproach.png and b/Code/data/visualapproach.png differ diff --git a/Code/data/webconfig.html b/Code/data/webconfig.html index 0833bbcc..eca88f27 100644 --- a/Code/data/webconfig.html +++ b/Code/data/webconfig.html @@ -3,18 +3,25 @@ Lay-Z-Spa Module | Web Config - + + - - + +
- Web Config +
+ + +
@@ -147,5 +154,7 @@ console.log(json); } + + diff --git a/Code/data/wifi.html b/Code/data/wifi.html index 460f235f..1d951530 100644 --- a/Code/data/wifi.html +++ b/Code/data/wifi.html @@ -3,18 +3,25 @@ Lay-Z-Spa Module | Network Config - + + - - + +
- Network Config +
+ + +
@@ -207,5 +214,7 @@

Reset WiFi Config:

} } + +
Time: ?