From 2146a7ed78e27f3e5b36f27e332236c5ec7a039d Mon Sep 17 00:00:00 2001 From: Mario Mariete <11509521+melkati@users.noreply.github.com> Date: Fri, 31 May 2024 09:00:16 +0200 Subject: [PATCH 01/15] Update: now calling the /savePreferences endpoint restart the countdown timer to deactivate the captive portal --- CO2_Gadget_WIFI.h | 1 + 1 file changed, 1 insertion(+) diff --git a/CO2_Gadget_WIFI.h b/CO2_Gadget_WIFI.h index 078ac9b4..c65cefdd 100644 --- a/CO2_Gadget_WIFI.h +++ b/CO2_Gadget_WIFI.h @@ -1274,6 +1274,7 @@ void initWebServer() { Serial.println(response); #endif handleSavePreferencesFromJSON(response); + timeCaptivePortalStarted = millis(); } else { Serial.println("---> [WiFi] Error: request is null"); } From 60dfe4b37320a188b5b48a563089aca676335c32 Mon Sep 17 00:00:00 2001 From: Mario Mariete <11509521+melkati@users.noreply.github.com> Date: Fri, 31 May 2024 10:07:28 +0200 Subject: [PATCH 02/15] refactor: Add conditional check to prevent drawing battery icon when displayShowBattery is false --- CO2_Gadget_EINK.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CO2_Gadget_EINK.h b/CO2_Gadget_EINK.h index 5a763e5c..77e23a66 100644 --- a/CO2_Gadget_EINK.h +++ b/CO2_Gadget_EINK.h @@ -475,8 +475,8 @@ void initDisplay(bool fastMode = false) { void showBatteryIcon(int32_t posX, int32_t posY, bool forceRedraw) { publishMQTTLogData("-->[EINK] Battery Level: " + String(batteryLevel) + "% Battery voltage: " + String(batteryVoltage) + "V"); - // Serial.println("-->[EINK] Drawn battery icon at " + String(posX) + ", " + String(posY) + " with level " + String(batteryLevel) + "% and voltage " + String(batteryVoltage) + "V"); display.fillRect(posX, posY, display.width() - posX, 16, GxEPD_WHITE); + if (!displayShowBattery) return; if (workingOnExternalPower) { // display.drawRoundRect(posX + 8, posY, 16 + 6, 16 + 6, 2, GxEPD_BLACK); display.drawBitmap(posX + 16, posY, iconUSB, 16, 16, GxEPD_WHITE, GxEPD_BLACK); From d1106bce11a5867d8ebf82ca62bb1311a099d138 Mon Sep 17 00:00:00 2001 From: Mario Mariete <11509521+melkati@users.noreply.github.com> Date: Sat, 1 Jun 2024 00:11:12 +0200 Subject: [PATCH 03/15] Fix: Server status dot red. Now shows correctly if connection is lost Fix: Close button in floating debug window (development aid) on web server was not working. Update: Renamed url parameter testCaptivePortal to forcedCaptivePortal (development aid) Fix: Many fixes to web server and Captive Server Update: Many general improvements to web server and Captive Server HTML, CSS, and JavaScript files Update: Only shows captivePortalSettings (development aid) when debug is set --- data/captiveportal.js | 306 ++++++++++++++++++++++++--------------- data/captiveportal.js.gz | Bin 3052 -> 3249 bytes data/index.html.gz | Bin 984 -> 984 bytes data/main.js.gz | Bin 569 -> 569 bytes data/ota.html.gz | Bin 797 -> 797 bytes data/preferences.html | 24 +-- data/preferences.html.gz | Bin 4789 -> 4805 bytes data/preferences.js.gz | Bin 3237 -> 3237 bytes data/status.html.gz | Bin 2948 -> 2948 bytes data/style.css | 97 +++++-------- data/style.css.gz | Bin 2480 -> 2412 bytes 11 files changed, 242 insertions(+), 185 deletions(-) diff --git a/data/captiveportal.js b/data/captiveportal.js index 14033eab..e118b10b 100644 --- a/data/captiveportal.js +++ b/data/captiveportal.js @@ -1,14 +1,16 @@ var captivePortalActive = false; -var testCaptivePortal = false; +var forcedCaptivePortal = false; var captivePortalStatusBarActive = false; var captivePortalDebug = false; +var forcedCaptivePortalDebug = false; var relaxedSecurity = false; var debugWindowActive = false; +var canPingServer = false; // Global variables to store the previous state var previousData = { captivePortalActive: false, - testCaptivePortal: false, + forcedCaptivePortal: false, captivePortalNoTimeout: false }; @@ -56,11 +58,11 @@ function updateStatusBar(content) { * @param {boolean} isInitialSetup - Whether this is the initial setup. */ function setCaptivePortalSettings(timeToWait, isInitialSetup = false) { - const disableTimeoutCheckbox = document.getElementById('disableTimeoutCheckbox'); + const disableTimeoutCheckbox = document.getElementById('cpNoTimeout'); let captivePortalNoTimeout = disableTimeoutCheckbox ? disableTimeoutCheckbox.checked : false; if (!disableTimeoutCheckbox && !isInitialSetup) { - console.error('Element with ID "disableTimeoutCheckbox" not found.'); + console.error('Element with ID "cpNoTimeout" not found.'); return; } @@ -81,7 +83,7 @@ function setCaptivePortalSettings(timeToWait, isInitialSetup = false) { timeToWaitForCaptivePortal: timeToWait, captivePortalActive, captivePortalNoTimeout, - testCaptivePortal, + forcedCaptivePortal, captivePortalDebug, relaxedSecurity: relaxedSecurity ? true : undefined }) @@ -92,7 +94,7 @@ function setCaptivePortalSettings(timeToWait, isInitialSetup = false) { } if (captivePortalDebug) { console.log('Captive Portal settings updated on server successfully:', { - testCaptivePortal, + forcedCaptivePortal, captivePortalActive, captivePortalNoTimeout, timeToWaitForCaptivePortal: timeToWait, @@ -112,7 +114,7 @@ function updateDebugWindow(data) { if (debugWindowActive) { const debugContent = document.getElementById('debug-content'); if (debugContent) { - if (captivePortalDebug) console.log('Updating debug window with data:', data); + // if (captivePortalDebug) console.log('Updating debug window with data:', data); let content = `
captivePortalActive: ${data.captivePortalActive}
captivePortalNoTimeout: ${data.captivePortalNoTimeout}
@@ -120,19 +122,24 @@ function updateDebugWindow(data) {
captivePortalTimeLeft: ${data.captivePortalTimeLeft}
captivePortalDebug: ${data.captivePortalDebug}
`; - if (data.testCaptivePortal !== undefined) { - if (captivePortalDebug) console.log('Adding testCaptivePortal to debug window:', data.testCaptivePortal); - content += `
testCaptivePortal: ${data.testCaptivePortal}
`; + + if (relaxedSecurity) { + // if (captivePortalDebug) console.log('Adding relaxedSecurity to debug window:', relaxedSecurity); + content += `
relaxedSecurity: ${relaxedSecurity}
`; + } + if (forcedCaptivePortal) { + // if (captivePortalDebug) console.log('Adding forcedCaptivePortal to debug window:', forcedCaptivePortal); + content += `
forcedCaptivePortal: ${forcedCaptivePortal}
`; } - if (data.relaxedSecurity !== undefined) { - if (captivePortalDebug) console.log('Adding relaxedSecurity to debug window:', data.relaxedSecurity); - content += `
relaxedSecurity: ${data.relaxedSecurity}
`; + if (forcedCaptivePortalDebug) { + // if (captivePortalDebug) console.log('Adding forcedCaptivePortalDebug to debug window:', forcedCaptivePortalDebug); + content += `
forcedCaptivePortalDebug: ${forcedCaptivePortalDebug}
`; } debugContent.innerHTML = content; - debugWindow.style.display = 'block'; + showDebugWindow(true); } } else { - debugWindow.style.display = 'none'; + showDebugWindow(false); } } @@ -141,91 +148,117 @@ function updateDebugWindow(data) { * @param {Object} data - The data received from the server. */ function handleCaptivePortalData(data) { - const changes = {}; - const propertiesToCheck = ['captivePortalActive', 'forceCaptivePortalActive', 'captivePortalNoTimeout', 'captivePortalDebug', 'relaxedSecurity']; - - if (captivePortalDebug) console.log('Received captive portal data from server:', data); - - // Check for changes in properties - propertiesToCheck.forEach(key => { - if (data[key] !== previousData[key]) { - changes[key] = { previous: previousData[key], current: data[key] }; - previousData[key] = data[key]; - } - }); - - if (Object.keys(changes).length > 0 && captivePortalDebug) { - console.log('Detected changes in captive portal data:', changes); - } + try { + const changes = {}; + const propertiesToCheck = ['captivePortalActive', 'forceCaptivePortalActive', 'captivePortalNoTimeout', 'captivePortalDebug', 'relaxedSecurity']; - // Update debug mode if present in data - if (data.captivePortalDebug !== undefined) { - captivePortalDebug = data.captivePortalDebug; - debugWindowActive = captivePortalDebug; - console.log('Captive portal debug mode set to:', captivePortalDebug); - } + console.log('Received captive portal data from server:', data); - // Update active states - forceCaptivePortalActive = data.forceCaptivePortalActive || false; - captivePortalActive = (data.captivePortalActive || false) || forceCaptivePortalActive; + // Check for changes in properties + propertiesToCheck.forEach(key => { + if (data[key] !== previousData[key]) { + changes[key] = { previous: previousData[key], current: data[key] }; + previousData[key] = data[key]; + } + }); - if (captivePortalActive) { - showCaptivePortalStatusBar(true); + if (Object.keys(changes).length > 0) { + console.log('Detected changes in captive portal data:', changes); + } - const newStatusContent = forceCaptivePortalActive ? 'Captive portal active (test mode)' : 'Captive portal active'; - updateStatusBar(newStatusContent); + // Update debug mode if present in data + if (data.captivePortalDebug !== undefined) { + captivePortalDebug = data.captivePortalDebug; + // debugWindowActive = captivePortalDebug; + console.log('Captive portal debug mode set to:', captivePortalDebug); + } - const statusContentElement = document.getElementById('status-content'); - if (statusContentElement) { - if (data.captivePortalNoTimeout) { - statusContentElement.innerHTML = ` - Timeout Disabled - - `; + // Update active states + forcedCaptivePortal = data.forceCaptivePortalActive || false; + captivePortalActive = (data.captivePortalActive || false) || forcedCaptivePortal; + + if (captivePortalActive) { + showCaptivePortalStatusBar(true); + + const newStatusContent = forcedCaptivePortal ? 'Captive portal active (test mode)' : 'Captive portal active'; + updateStatusBar(newStatusContent); + + const statusContentElement = document.getElementById('status-content'); + if (statusContentElement) { + if (data.captivePortalNoTimeout) { + statusContentElement.innerHTML = ` + Timeout Disabled + + `; + } else { + statusContentElement.innerHTML = ` + Timeout: ${data.captivePortalTimeLeft}s + + `; + } } else { - statusContentElement.innerHTML = ` - Timeout: ${data.captivePortalTimeLeft}s - - `; + console.error('Element with ID "status-content" not found.'); } - } else { - console.error('Element with ID "status-content" not found.'); - } - const disableTimeoutCheckbox = document.getElementById('disableTimeoutCheckbox'); - if (disableTimeoutCheckbox) { - disableTimeoutCheckbox.addEventListener('change', function () { - const timeToWait = this.checked ? 0 : data.timeToWaitForCaptivePortal; - console.log('Setting time to wait:', timeToWait); - setCaptivePortalSettings(timeToWait); - }); + const disableTimeoutCheckbox = document.getElementById('disableTimeoutCheckbox'); + if (disableTimeoutCheckbox) { + disableTimeoutCheckbox.addEventListener('change', function () { + const timeToWait = this.checked ? 0 : data.timeToWaitForCaptivePortal; + console.log('Setting time to wait:', timeToWait); + setCaptivePortalSettings(timeToWait); + }); + } + } else { + showCaptivePortalStatusBar(false); } - } else { - showCaptivePortalStatusBar(false); - } - updateServerStatusDot(); // Ensure the status dot is updated after handling captive portal data + updateServerStatusDot(); // Ensure the status dot is updated after handling captive portal data - if (debugWindowActive) { - updateDebugWindow(data); + if (debugWindowActive) { + updateDebugWindow(data); + } + } catch (error) { + console.error('Error in handleCaptivePortalData function:', error); + throw error; // Re-throw the error to be caught in the fetch catch block } } + /** * Fetches the captive portal settings from the server. */ function getCaptivePortalSettings() { - fetch('/getCaptivePortalStatusAsJson') - .then(response => response.json()) + fetch("/getCaptivePortalStatusAsJson") + .then(response => { + console.log("Received response:", response); + + // Check if the response status is OK + if (!response.ok) { + console.error("Response not OK:", response.status, response.statusText); + throw new Error("Network response was not ok " + response.statusText); + } + + // Convert the response body to JSON + return response.json(); + }) .then(captivePortalSettings => { - handleCaptivePortalData(captivePortalSettings); + console.log("Received JSON:", captivePortalSettings); + + // Handle the JSON data + try { + handleCaptivePortalData(captivePortalSettings); + } catch (e) { + console.error("Error in handleCaptivePortalData:", e); + throw e; // Re-throw the error to be caught in the catch block + } }) .catch(error => { - console.error('Error fetching captive portal status:', error); - showServerStatusDot(true, 'status-dot-red', 'Connection to server lost'); + console.error("Error fetching captive portal status:", error); + // showServerStatusDot(true, "status-dot-red", "Connection to server lost"); }); } + let previousServerStatusDotState = { show: null, colorClass: '', @@ -238,7 +271,7 @@ let previousServerStatusDotState = { * @param {string} [colorClass] - The color class to apply. * @param {string} [title] - The title to apply for hover text. */ -function showServerStatusDot(show, colorClass = 'status-dot-white', title = '') { +function displayServerStatusDot(show, colorClass = 'status-dot-cyan', title = '') { const serverStatusDot = document.getElementById('server-status-dot'); if (serverStatusDot) { const hasStateChanged = @@ -272,16 +305,15 @@ function checkServerConnection() { fetch('/pingServer') .then(response => { if (response.ok) { - updateServerStatusDot(); // Update status dot based on current state + canPingServer = true; } else { + canPingServer = false; captivePortalActive = false; // Mark captive portal as inactive - updateServerStatusDot(); // Update status dot to reflect the lost connection } }) .catch(error => { console.error('Error pinging server:', error); captivePortalActive = false; // Mark captive portal as inactive - updateServerStatusDot(); // Update status dot to reflect the lost connection }); } @@ -291,26 +323,30 @@ function checkServerConnection() { function updateServerStatusDot() { let show, colorClass, title; - if (!captivePortalActive) { + if (!canPingServer) { + show = true; + colorClass = 'status-dot-red'; + title = 'Connection to server lost'; + } else if (!captivePortalActive) { show = false; colorClass = 'status-dot-hidden'; title = 'Captive portal inactive'; - } else if (forceCaptivePortalActive) { + } else if (forcedCaptivePortal) { show = true; colorClass = 'status-dot-blue'; title = 'Captive portal active (test mode)'; } else { show = true; - colorClass = 'status-dot-white'; + colorClass = 'status-dot-cyan'; title = 'Captive portal active'; } - // Call showServerStatusDot with the new state - showServerStatusDot(show, colorClass, title); + // Call displayServerStatusDot with the new state + displayServerStatusDot(show, colorClass, title); if (captivePortalDebug) { console.log('Updated server status dot based on captive portal status'); - console.log('captivePortalActive:', captivePortalActive, 'forceCaptivePortalActive:', forceCaptivePortalActive); + console.log('captivePortalActive:', captivePortalActive, 'forcedCaptivePortal:', forcedCaptivePortal); } } @@ -333,7 +369,7 @@ function initializeCaptivePortalStatusBar() { `; - if (testCaptivePortal) showCaptivePortalStatusBar(true); + if (forcedCaptivePortal) showCaptivePortalStatusBar(true); } else { console.error('Element with ID "captive-portal-status-bar" not found.'); } @@ -344,13 +380,14 @@ function initializeCaptivePortalStatusBar() { */ function setupInitialSettings() { if (window.location.href.includes("debugCaptivePortal")) { + forcedCaptivePortalDebug = true; captivePortalDebug = true; - console.log('Forcing captive portal debug mode to be active by parameter in URL'); + console.log('Forcing captive portal debug mode to be active by debugCaptivePortal parameter in URL'); } - if (window.location.href.includes("testCaptivePortal")) { - if (captivePortalDebug) console.log('Forcing captive portal to be active in test mode by parameter in URL'); - testCaptivePortal = true; + if (window.location.href.includes("forcedCaptivePortal")) { + if (captivePortalDebug) console.log('Forcing captive portal to be active in test mode by forcedCaptivePortal parameter in URL'); + forcedCaptivePortal = true; captivePortalActive = true; } @@ -463,7 +500,7 @@ function initializeThemeSwitch() { function initializeCaptivePortal() { if (captivePortalDebug) console.log('Document loaded. Initializing captive portal for preferences.html'); - getCaptivePortalSettings(); // Fetch initial settings + // getCaptivePortalSettings(); // Fetch initial settings setupInitialSettings(); initializeCaptivePortalStatusBar(); updateStatusBarContent(); @@ -474,24 +511,6 @@ function initializeCaptivePortal() { highlightCurrentPage(); // Highlight the current page in the navigation } -/** - * Create the debug window and its content. - * @param {HTMLElement} debugWindow - The debug window element. - */ -function createDebugWindow(debugWindow) { - const closeButton = document.createElement('button'); - closeButton.id = 'close-debug-window'; - closeButton.innerHTML = '[X]'; - closeButton.onclick = () => { - debugWindow.style.display = 'none'; - }; - debugWindow.appendChild(closeButton); - - const debugContent = document.createElement('div'); - debugContent.id = 'debug-content'; - debugWindow.appendChild(debugContent); -} - document.addEventListener("DOMContentLoaded", function () { const currentPage = window.location.pathname.split("/").pop(); @@ -505,8 +524,8 @@ document.addEventListener("DOMContentLoaded", function () { if (captivePortalDebug) console.log('Not on preferences.html, skipping debug window initialization'); } + const debugWindow = document.getElementById('debug-window'); if (captivePortalDebug) { - const debugWindow = document.getElementById('debug-window'); if (debugWindow) { createDebugWindow(debugWindow); debugWindowActive = true; @@ -515,7 +534,68 @@ document.addEventListener("DOMContentLoaded", function () { console.error('Element with ID "debug-window" not found.'); } } else { - debugWindow.style.display = 'none'; - debugWindowActive = true; + if (debugWindow) { + debugWindow.style.display = 'none'; + debugWindowActive = false; + } } }); + +/** + * Shows or hides the captivePortalSettings fieldset based on the provided flag. + * @param {boolean} show - Determines whether to show or hide the captivePortalSettings fieldset. + */ +function showCaptivePortalSettings(show) { + const captivePortalSettings = document.getElementById('captivePortalSettings'); + if (captivePortalSettings) { + if (show) { + captivePortalSettings.classList.remove('hidden'); + } else { + captivePortalSettings.classList.add('hidden'); + } + } else { + console.error('Element with ID "captivePortalSettings" not found.'); + } +} + +/** + * Shows or hides the debug window based on the provided flag. + * @param {boolean} show - Flag indicating whether to show or hide the debug window. + */ +function showDebugWindow(show) { + const debugWindow = document.getElementById('debug-window'); + if (debugWindow) { + if (show) { + debugWindow.classList.remove('hidden-debug-window'); + debugWindow.style.display = 'block'; + debugWindowActive = true; + if (captivePortalDebug) console.log('Showing debug window'); + } else { + debugWindow.classList.add('hidden-debug-window'); + debugWindow.style.display = 'none'; + debugWindowActive = false; + if (captivePortalDebug) console.log('Hiding debug window'); + } + } else { + console.error('Element with ID "debug-window" not found.'); + } + showCaptivePortalSettings(show); // Show captive portal settings when debug window is shown (for development) +} + +/** + * Creates a debug window with a close button and debug content. + */ +function createDebugWindow() { + const debugWindow = document.getElementById('debug-window'); + const closeButton = document.createElement('button'); + closeButton.id = 'close-debug-window'; + closeButton.innerHTML = '[X]'; + closeButton.onclick = () => { + showDebugWindow(false); + }; + debugWindow.appendChild(closeButton); + + const debugContent = document.createElement('div'); + debugContent.id = 'debug-content'; + debugWindow.appendChild(debugContent); +} diff --git a/data/captiveportal.js.gz b/data/captiveportal.js.gz index 5eefc57aad2c528c0ac3ebf2e9bab6b2acb46def..8ebc67e7c869bed131964d01e0efd1632778bdd2 100644 GIT binary patch delta 3247 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 delta 3048 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 diff --git a/data/index.html.gz b/data/index.html.gz index 45a900f730b0f356b2edc2aac4e30da9415fe626..8432c18d77019d07f9a675fc75c0f098310e54d7 100644 GIT binary patch delta 16 Xcmcb?euJG|zMF%i*)wV*`$c8|Elve8 delta 16 Xcmcb?euJG|zMF%i>0ZP}_KVB_FuMiB diff --git a/data/main.js.gz b/data/main.js.gz index 8f5722c57fa34a2718b302f88ca431746df57a17..820727826c130bb13a769af6bd6e62b7574d32dc 100644 GIT binary patch delta 16 XcmdnVvXg~fzMF%i*)wV*yAcxrC>aD! delta 16 XcmdnVvXg~fzMF%i>0ZP}b|WSLD~1H% diff --git a/data/ota.html.gz b/data/ota.html.gz index bc472bfa59dc7be5fefde43dbe1083cea7702dbf..b6022e98321babeb590dc44cd68cf009cb3cb7a9 100644 GIT binary patch delta 16 XcmbQsHkXZEzMF%i#WQLnyBIS7B-sQ2 delta 16 XcmbQsHkXZEzMF%i`Ci0Eb}?oEC`JU5 diff --git a/data/preferences.html b/data/preferences.html index d6ba4ce6..deefcb7b 100644 --- a/data/preferences.html +++ b/data/preferences.html @@ -24,7 +24,7 @@ - - + -
+ + + +
@@ -340,7 +339,8 @@

CO2 Gadget Preferences

diff --git a/data/preferences.html.gz b/data/preferences.html.gz index c7b6ac9979b82fabc9961f88c78c054d44cc8054..0bff6b72e542bf31bb209258b3419ba4a45bb6df 100644 GIT binary patch literal 4805 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 literal 4789 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 PfBW%oDuX{NHBtZo$)+oO diff --git a/data/preferences.js.gz b/data/preferences.js.gz index a05641fc0df463492a3baf3b9748b8be395eb75b..39e6ba2048d56f4ba20593ceb046901dc5ffd989 100644 GIT binary patch delta 16 XcmZ1~xm1!}zMF%i#WQLn`#c^1DE0)$ delta 16 XcmZ1~xm1!}zMF%i`Ci0E_IW%2EMo;( diff --git a/data/status.html.gz b/data/status.html.gz index 3ea87da1ee5b38283129211331f28608d1b67a53..d1f6c5224e8b9eb714a96caeb93d9ff3e330a7e1 100644 GIT binary patch delta 16 XcmZn>ZxLsg@8;lW@r>HYUds&tB%K6b delta 16 XcmZn>ZxLsg@8;lWz8A5Py_OpQC<+Ae diff --git a/data/style.css b/data/style.css index 36c97e02..639e1b50 100644 --- a/data/style.css +++ b/data/style.css @@ -15,7 +15,7 @@ --input-background-color: #fff; --button-background-color: #4caf50; --button-hover-background-color: #45a049; - --tag-dark-bg-color: #2c3e50; /* Dark theme tag background color */ + --tag-dark-bg-color: #2c3e50; --popup-bg-color: #333; --popup-text-color: #fff; --tooltip-bg-color: #555; @@ -26,7 +26,6 @@ --close-button-hover-color: darkred; --status-bar-bg-color: #fbef83; --status-bar-text-color: #333; - --navbar-height: 60px; } [theme='dark'] { @@ -57,16 +56,13 @@ --close-button-hover-color: #c0392b; --status-bar-bg-color: #34495e; --status-bar-text-color: #ecf0f1; - --navbar-height: 60px; } -/* Body Styles */ body { font-family: 'Arial', sans-serif; background-color: var(--bg-color); color: var(--font-color); margin: 0; - padding-top: var(--navbar-height); } h1, h2, h3, h4 { @@ -92,21 +88,24 @@ h4 { font-size: 1.25em; } -/* Navigation Bar Styles */ .navbar { - overflow: hidden; - position: fixed; - top: 0; + display: flex; + flex-direction: column; width: 100%; - color: var(--navbar-text-color); background-color: var(--navbar-background-color); - padding: 15px 10px; + padding: 0; border-bottom: 1px solid var(--navbar-border-color); box-shadow: var(--navbar-shadow); z-index: 1000; + position: relative; +} + +.nav-content { display: flex; justify-content: space-between; align-items: center; + width: 100%; + padding: 15px 10px; } .navbar a { @@ -125,13 +124,6 @@ h4 { pointer-events: none; } -.nav-content { - display: flex; - justify-content: space-between; - align-items: center; - width: 100%; -} - .nav-links-left, .nav-links-right { display: flex; @@ -143,19 +135,11 @@ h4 { } .iconDarkLight { - position: absolute; - top: 1.2%; - right: 2%; - background-color: transparent; - transition: transform 0.05s; - z-index: 1000; + margin-left: 10px; + margin-right: 1rem; cursor: pointer; width: 24px; height: 24px; - /* background-image: url('data:image/svg+xml;charset=UTF-8,%3Csvg%20style%3D%22width%3A24px%3Bheight%3A24px%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20fill%3D%22var(--icon-color)%22%20d%3D%22M12%2C2A7%2C7%200%200%2C0%205%2C9C5%2C11.38%206.19%2C13.47%208%2C14.74V17A1%2C1%200%200%2C0%209%2C18H15A1%2C1%200%200%2C0%2016%2C17V14.74C17.81%2C13.47%2019%2C11.38%2019%2C9A7%2C7%200%200%2C0%2012%2C2M9%2C21A1%2C1%200%200%2C0%2010%2C22H14A1%2C1%200%200%2C0%2015%2C21V20H9V21Z%22%20%2F%3E%3C%2Fsvg%3E'); */ - background-repeat: no-repeat; - background-position: center; - background-size: contain; } .iconDarkLight:hover { @@ -166,7 +150,6 @@ h4 { transform: scale(1.5); } -/* Styles for the tag class */ .tag { font-size: 1.2em; color: var(--bg-color); @@ -186,7 +169,6 @@ h4 { background-color: var(--tag-dark-bg-color); } -/* Form Styles */ #preferencesForm { background-color: var(--bg-color); border-radius: 8px; @@ -247,7 +229,7 @@ button { border-radius: 4px; cursor: pointer; font-size: 16px; - margin: 5px; /* Ajuste del margen para un espaciado uniforme */ + margin: 5px; transition: background-color 0.3s ease; } @@ -264,12 +246,12 @@ button:hover { .buttonGroup { display: flex; justify-content: space-between; - flex-wrap: nowrap; /* Mantener los botones en una fila en pantallas anchas */ + flex-wrap: nowrap; } .buttonsBackupRestore, .buttonsRestartSave { display: flex; - gap: 10px; /* Añadir espacio entre los botones */ + gap: 10px; } #backupButton, #restoreButton { @@ -282,21 +264,21 @@ button:hover { @media screen and (max-width: 600px) { .buttonGroup { - display: grid; /* Usar grid layout para pantallas estrechas */ - grid-template-columns: 1fr 1fr; /* Dos columnas de igual ancho */ - gap: 20px; /* Espacio entre columnas y filas */ + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; } .buttonsBackupRestore, .buttonsRestartSave { flex-direction: column; width: auto; margin-bottom: 0; - align-items: center; /* Centrar los botones en cada columna */ + align-items: center; } .buttonsBackupRestore button, .buttonsRestartSave button { - width: 100%; /* Asegurar que los botones ocupen todo el ancho de la columna */ - margin-bottom: 10px; /* Mayor separación vertical entre botones */ + width: 100%; + margin-bottom: 10px; } .buttonGroup button { @@ -319,19 +301,16 @@ button:hover { } } - .content { max-width: 600px; margin: 0 auto; - padding-top: var(--navbar-height); /* Add padding to ensure content is not hidden by navbar */ } -/* Circular Chart Styles */ .flex-wrapper { display: flex; flex-flow: row nowrap; justify-content: space-around; - align-items: center; /* Center items vertically */ + align-items: center; } .single-chart { @@ -339,7 +318,7 @@ button:hover { position: relative; display: flex; flex-direction: column; - align-items: center; /* Center items horizontally */ + align-items: center; } .circular-chart { @@ -415,7 +394,6 @@ button:hover { } } -/* Popup Styles */ #popup { display: none; position: fixed; @@ -480,24 +458,19 @@ button:hover { visibility: visible; } -/* Captive Portal Status Bar */ #captive-portal-status-bar { width: 100%; background-color: var(--status-bar-bg-color); color: var(--status-bar-text-color); - position: fixed; - top: 45px; - z-index: 999; + position: relative; + z-index: 1; display: flex; justify-content: space-between; align-items: center; font-size: 0.7em; -} - -@media (max-width: 600px) { - #captive-portal-status-bar { - top: 45px; - } + padding: 5px; + box-sizing: border-box; + overflow: hidden; } #hide-captive-portal-status-bar-button { @@ -506,6 +479,8 @@ button:hover { padding: 5px 10px; font-size: 0.7em; cursor: pointer; + display: flex; + align-items: center; } #hide-captive-portal-status-bar-button svg { @@ -524,7 +499,6 @@ button:hover { display: none !important; } -/* Status Dot Styles */ .status-dot { width: 10px; height: 10px; @@ -542,6 +516,10 @@ button:hover { background-color: white; } +.status-dot-cyan { + background-color: cyan; +} + .status-dot-blue { background-color: blue; } @@ -558,7 +536,6 @@ button:hover { font-weight: bold; } -/* Add a fade animation effect */ @keyframes fadeInOut { 0% { opacity: 0; @@ -583,21 +560,21 @@ button:hover { right: 10px; width: auto; height: auto; - pointer-events: none; + pointer-events: auto; /* Ensure interaction is possible */ font-size: 0.9em; background-color: var(--debug-bg-color); border: 1px solid var(--debug-border-color); border-radius: 10px; padding: 10px; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1); - display: none; + display: none; /* Default state is hidden */ overflow: auto; z-index: 9998; animation: fadeInOut 2s ease-in-out; } .hidden-debug-window { - display: none; + display: none !important; } #close-debug-window { diff --git a/data/style.css.gz b/data/style.css.gz index 2d4439d0f04ff03d10ddd14f2a387124418f0eef..c8b3db1f035eaba64a211a0750031fa13da0db55 100644 GIT binary patch literal 2412 zcmV-y36u68iwFpWNm^zE0CRMCY-KKOX>KlKb8`UIS#7VAFckhPs4==Y7GB2OSWJA2 z#>7OUiC>H#3Y4wNfV9l1g#Yeo3lv(I({JYH-m&&@dd}1HenVOmRc+g8kymzHWCacH zZ8n>|ZAXXhdzz;3y&_eHyQSR*V~k*x#6{k}hxrdI#)QU-7ARpYN8ckv?Fhx+4zxJu zNzZ+o?oy;ED*U6D#P@x*aV)-L+U+<_u!#nnQ9%>6iy!kJwabnuDK4R7t;-+Q27c)w zLJys#_*uCNpgdv(3?vTf^ln2xAW0Igd|sX_Z6}W7en%-ghB7)=Rgq6@uVa)3PER3K zo}vqov)=JbK?gfQ^xNRJyx7NrM_H8Tl5Z$=x>W_Ty56G5Dl()Gf}mfYdLzNn`Jm_p zq^=(}z=%xrP>%1|SmZhf^o zVNT8G>WEMH$pg;n!57W)ahy7-3k-BPb^CWw6z}5J{>N_{_V2IPyWpw|xT%-;iRl80 zspbV^G3LC$jlHe!-|7XMu@`6pbbB*rn7*Jizv2#lv_=7%^9ZHkpFP6eAZHtx9-+kO zY=7QWOxTtEo9xt<`QD6o_`BuCS-S4yIp0w1H7`(sZ5{jZH5a)TJO0j#bQdwWtKA9t zo2W>B*1`%>bRyZ$@WC@mQ1)QSP|j?IDM|OnC45Kpp>mzYez?kcb?YZY4T$r={Lz|NT91}IzP6H%Rwsm&8TuKf8UC^7f9!-%99N{sLy+Wr z%J&zNRL9VDocl94n$bd2B6Kta+z|qkF<}9({OIDqkg8o{mM9$3~WW*P`t9ygSVqP$f^5LhdpSr{>_jtPYejf~KYw+ltT zLNUjq?#iLv4!|N@FB^Xc{TP?Cg)1ZZH)dxzt(JrDlpKy4geFLh{sI6ZJwfmbUCOPF z=b)Dm^4@Za@jh2yE{ON#8~gZ78Hmj#RyX0Tf%b?wm3Y?pwg?F<(}JEt7NZP5bfJ3E zDO+qz(f~*yK93y-9PLXDfL~)te86>1!8lNzDWtr5{9c7DXNjNOA%!IQ|B5 z_NtVBXk+4Da(Q~ za&h9&Zfg+54A<5SpnGHNY`qiWtbnD~2PW(*p1N z_stT1e?xOJ#h=eK&KaWZ2_2VVdX%sTqf%gOo%`ZjS6K$)>e~rP1hpW^GFjBKiWc9n z2twl#%A(zBIr^(@$3VS3Ch=C=ZhIKn+ zHBZ0cpD9Hr%&d;EcJ9|L&jc|5fr9$H6?GaN9k2<{YDH|{;X|`s3bZ;Y_jQAI9mkJ% zChhTjA_lD=?=TLU)pwMgB_MKwJM7+7*FLc5)nj~W>*YK?LgYS?B*~y@Ip?JMD@Oh# zqb?E_dG>QI9Qr#R4#&>X`$KDv7T@@@&EJqhTK8#9(f7U|WgwJ~>Rf(c->Jyj5mO6z zB{*j|v3AaV3-Ni#{z6H`2MVVm0n5d-BGBsCG?Q=?l)&NA-p-}SAx(2n44XP}h4Csc zP00^PpH)$Y0r(A1D&j%4QsRl{hKDw6Ecu_=8b=&7DD{oK$)5Y;mPw8!maRBChU8 z61XG@f4_*cjFgNp5r|Y$x+zsp$v8|?-XJYVSm27m|6ilzfU8fKGJs$lE^jQD|2$wb z($PP6b0+8}$jz!7c=C3?9v7x+le4P76GkGEk?Lo6BQ5M(IDJkr$zjf7&1vg|*ct#E zt%iVKpwgtI5%91PU?AEe1+4T155A(Jg1+&=q zLwj+a(@)Lh^GrQs4)YgU$#Ub_(&U5$vj=EHO9}cs=TR=Q?vRt9$idcInxv?DJ5}ns z6Mda?e|j)x++?3C#U=EmhdR)fQV(St?~4Fswx4`?Oc?(}6KyJA9g=~d=LvRvbwmd3 z?PSl}5ea*$&332Xms2KvHZ`m```E)v&H|i~hZ3jp0`<*p9*@4c&WN;G$^R@}z~75Y zt(6zU`HFI-K~LD;uz1$y+G1_!WIKGC4qW=bz!v7zWPDx%QfH{-Zc3=( e|2+N}MDEEqyJlA_$(q%axBdn>2bo%2A^-s9XREdV literal 2480 zcmV;h2~YMPiwFpV-dJV?0CRMCY-KKOX>KlKb8`UISzE7^FcAJLs4=>6*>G7_myL*iJ2xM@{X zG`_Xl?GCP+9EZ<&p2O#cGzA`p_B)I*f>DxI<@g@vKQNdQnkre4gs~ERNf31tlzu+a zsx7lo`-6O+BPG$`Z=)q)7^;a=^##*m#`B_GGMP*&nyFcQpZ};?c0yV8HTIls{mt3I zFFhvcuD2FH8-E3oCyYRW#6hh-OqdH~S;n0&>$Wjw(li}sl(RaNN!v74IWxUYQ671t zgmigME<)aR&o>1f-3-yslido^5DOi3Rkt;tQ0@$)3S_mtRaG=(Dj!ABI6n7AhLiTF z?ETds)`O&Wj~kd46~n!&gA$w>6WX?OSEUGRP}4G3Y`qH91m_Pz zHG4grFgc!@c<0sMx)03>pYg*xT>qVq1|4vkd$|uUni!j;@KKWP)1DOi+ZHLp?RFns z<%ORIS(w?pP)ZGI=w>WY!%u?;Vfee$*y+?T3>fAX*02epJio$`FxjFAEzv}I_-mT@ zJLElxY?>%D20|?J$A(>z!d9oI?DrNZ6YkeLZ|(c{mk6WO8`MzQZkvYbHQwAxy>K5S zCU11@YWA1mHla>ZW#2C%G;(w%#rOEmV@gnQXU$N_T!tyh57y3nLG-TT)9NrC<+_IP zGonXQ#@?YuSw_kupj!(u!<1Eb^4F(eeG1p7?L}!*v1=<0zeb?i>d& z!K#8}POrm`@zr3_@J>{J0~;g|jQqfHfY042m<;%v`w<@twJ|ZY)rJB-wvql__$KBB z5cEW{43~$xVx%Ee8Rz61Btnjdclb)O<`ny$cYBJYoj6ttJ7Fgw!^_wQ|K_0bHK=A& z&I(@nHl&%UR6n*l{D4%D;cwgl?~oBz7wCJO7x>%ZQ_C8Xe+NZngUcpnHA*4Caq|@e z7N{V{(j^T(vp9umOb6H;bP-->IISoWc$HO&ucM>9^_+ma+E7&XXbS;n6EX)QhSf5$ zV3`(ic1m6;`3*`N@&#XpGHSnv9VOg!7k>d4n6OjG4^ChH36V)`NUa4aKQp($d9$8; zrobY7r8#uX7XW5SCZR9(DGywJ_&tMM`%0)B;}sQc(8(!0}qZ- zSarGy;xVu2+1Ypz)59ihDT6mTn9A#53%e*Ygm|8=L~)H#1910G)49Ru-tWnj07n5C$|cYUZYP02nAY|#ir;jq&40X`q}w0V`yeNi&V2u)ul21!+92NCdm za|ic^gy@TeIFdjxK2nUQC?hQsX>-D8&J_^cnuSBK8ktN=2pNdcqDntcw{v3sNU!zy zWYlDifW`9iIuiD|zv^z)6z3R1E5+8>$gwU%^jfxNOe}rZ+!cR9;h_dNvY$v~s7p$as3e=p5jB$$B-q z>;iUvdwAZN;J&?r%+%5JgwxMp=jbYGo9ch?u9NlW0)Y0BsFLU`-AM9(#m|`e~LYg*DOd!-HSND)-2%FU1My~S$-mU+n z3g9B{SBh#l-T1#j5qkobwDnuCuA+E7jJ}oDzC&NIzRD3aaQnJjiFi@^v%W>80{L;M z0K{o7#7+NIiO|nKI^&EWhouy(bx@f(chBgXE5m%}fynAI>-&+C?7;uHP?f+)Bl3TH zE?Mm7)PY}NGl%mJd=_wJgyK|$hSYdiB%L=?8O^We$ibOKhp9;xv#4zZ!0?7nwlR(# zKp4$|ugyn%p26~O)EW51RO>B>#4*aj)yoibH@P1-4f5M@-F2IxkTo`Wyxln*35Xb@R`-m zJ3wuLXurasWqN)J+$0_ftq0z1@oA`JBM}>YKPiD?kraiz->`;OpRs^XZ#b>`sa|8a zc`ztYZ|fq#=D{$)Px%yh)gxDuv%FH(v^qkxF~^s=)2-UZfpYdMq8aNRea7E&iq4oh zgW$!xed%>(hye%`+~-$tuQSnspb4Db#Qq)LciiV7Yl`w@V99RN^#0zKJ+)_I$%g43 z`@Xj?xBH#@_b;N_Z zYl$a*c-(biV$J_77C~a6g+`U?58B-l`#d+V4c^S9Au@U)ifTEO;xEhBz^_1Z4a7Lz zx?8ek_X;TbRNhKq`z(K4;_f z91AArhJ*#S82JAJWk=k+$CLpCdb<40iu%``N9(-$>+7Bkx(#x->D$BND}sw&xvEL7 z>hgs!l8}Nl--jLPVSfOtFF7VPEJdu{o1QV-0AObn85|I+siVU7cVcz|!cb}7Tl8H# zLMFTgClEh;fp)Y(#iUg|DWaBOQNpvxEHu7Xb4g_jOdj2Z{@uSdERD0Ir;|;gE8a_^ z1ZN;luxm*w*XQf;gH^$6Js>_{m}y+Oesf`meL41f6?kJ~PChS`F!r|{E2B#J&9!Be z2@4h%Fj}^fbjzBxT%2?ynL*HP&_So zT@a~Hes^E{bk`1g uyV*1U^@7*$qc`;W}&7R0AqH5I+HA!8$fzBLD!QliO1O From a5aa15dec3c18e28a119332006fe3dc0919d8cdd Mon Sep 17 00:00:00 2001 From: Mario Mariete <11509521+melkati@users.noreply.github.com> Date: Sat, 1 Jun 2024 00:16:15 +0200 Subject: [PATCH 04/15] CO2 Gadget Beta v0.12.083-development --- platformio.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platformio.ini b/platformio.ini index 67e9792c..de31705f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,7 +10,7 @@ extra_configs = platformio_extra_configs.ini [version] build_flags = -D CO2_GADGET_VERSION="\"0.12."\" - -D CO2_GADGET_REV="\"082-development"\" + -D CO2_GADGET_REV="\"083-development"\" ;**************************************************************************************** ;*** You can disable features by commenting the line with a semicolon at the beginning @@ -87,11 +87,11 @@ build_flags = build_flags = ; -DDEBUG_EINK ; -DDEBUG_NEOPIXEL - -DDEBUG_PREFERENCES ; Print preferences to serial on load and save + ; -DDEBUG_PREFERENCES ; Print preferences to serial on load and save ; -DDEBUG_WIFI_EVENTS ; -DDEBUG_BLE ; -DDEBUG_IMPROV_WIFI - -DDEBUG_CAPTIVE_PORTAL + ; -DDEBUG_CAPTIVE_PORTAL ; -DDEBUG_ARDUINOMENU ; -DMENU_DEBUG ; Needs streamFlow library -DWIFI_PRIVACY ; Comment to show WiFi & MQTT passwords in pain text in serial and the menu (if active no passwords will be shown) From b56af244d7a96f927e683feb2fc4b91eeb147f79 Mon Sep 17 00:00:00 2001 From: Mario Mariete <11509521+melkati@users.noreply.github.com> Date: Sat, 1 Jun 2024 21:17:06 +0200 Subject: [PATCH 05/15] Add Copyright (c) information to main cpp file --- CO2_Gadget.ino | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/CO2_Gadget.ino b/CO2_Gadget.ino index 1e76c411..633f70b3 100644 --- a/CO2_Gadget.ino +++ b/CO2_Gadget.ino @@ -1,3 +1,45 @@ +/*****************************************************************************************************/ + +// ▐▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▌ +// ▐ ____ ___ ____ ____ _ _ ▌ +// ▐ / ___/ _ \___ \ / ___| __ _ __| | __ _ ___| |_ ▌ +// ▐ | | | | | |__) | | | _ / _` |/ _` |/ _` |/ _ \ __| ▌ +// ▐ | |__| |_| / __/ | |_| | (_| | (_| | (_| | __/ |_ ▌ +// ▐ \____\___/_____| \____|\__,_|\__,_|\__, |\___|\__| ▌ +// ▐ |___/ ▌ +// ▐▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▌ + +// CO2 Gadget Advanced Firmware +// +// Copyright (C) 2021-2024 CO2 Gadget Contributors +// Contact: https://emariete.com +// +// This file is part of the CO2 Gadget firmware. +// +// The CO2 Gadget firmware is free software: you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// The emariete.com Website and Documentation is distributed in the hope that +// it will be useful, but WITHOUT ANY WARRANTY; without even the implied +// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the emariete.com Website and Documentation. If not, see +// + +// ▐▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▌ +// ▐ _ _ ▌ +// ▐ ___ _ __ ___ __ _ _ __(_) ___| |_ ___ ___ ___ _ __ ___ ▌ +// ▐ / _ \ '_ ` _ \ / _` | '__| |/ _ \ __/ _ \ / __/ _ \| '_ ` _ \ ▌ +// ▐ | __/ | | | | | (_| | | | | __/ || __/| (_| (_) | | | | | | ▌ +// ▐ \___|_| |_| |_|\__,_|_| |_|\___|\__\___(_)___\___/|_| |_| |_| ▌ +// ▐▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▌ + +/*****************************************************************************************************/ + // Functions and enum definitions void reverseButtons(bool reversed); // Defined in CO2_Gadget_Buttons.h void outputsLoop(); // Defined in CO2_Gadget_Main.h From 8458f91661aa54e9f231898a348ef160ab5db070 Mon Sep 17 00:00:00 2001 From: Mario Mariete <11509521+melkati@users.noreply.github.com> Date: Sat, 1 Jun 2024 23:55:58 +0200 Subject: [PATCH 06/15] Update README.md --- CO2_Gadget.ino | 2 +- README.md | 100 +++++++++++++++++++++++++++++++------------------ 2 files changed, 65 insertions(+), 37 deletions(-) diff --git a/CO2_Gadget.ino b/CO2_Gadget.ino index 633f70b3..7616b5bc 100644 --- a/CO2_Gadget.ino +++ b/CO2_Gadget.ino @@ -11,7 +11,7 @@ // CO2 Gadget Advanced Firmware // -// Copyright (C) 2021-2024 CO2 Gadget Contributors +// Copyright (C) 2021-2024 Mariete & CO2 Gadget Contributors // Contact: https://emariete.com // // This file is part of the CO2 Gadget firmware. diff --git a/README.md b/README.md index 39cef5e2..006bc501 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ [![PlatformIO](https://github.com/melkati/CO2-Gadget/workflows/PlatformIO/badge.svg)](https://github.com/melkati/CO2-Gadget/actions/) [![Telegram Group](https://img.shields.io/endpoint?color=neon&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Femariete_chat)](https://t.me/emariete_chat) ![Twitter Follow](https://img.shields.io/twitter/follow/e_mariete?style=social) +[![GitHub stars](https://img.shields.io/github/stars/melkati/CO2-Gadget.svg?style=social&label=Star)](https://github.com/melkati/CO2-Gadget/stargazers) +[![GitHub forks](https://img.shields.io/github/forks/melkati/CO2-Gadget.svg?style=social&label=Fork)](https://github.com/melkati/CO2-Gadget/network) @@ -21,7 +23,9 @@ # CO2-Gadget -An advanced fimware for CO2 Monitor/Meter. It's really flexible, you can use this firmware with **any supported CO2 Monitor/Meter** based on ESP32 (99,99% of them). +**CO2-Gadget** is an advanced firmware for CO2 Monitors/Meters based on ESP32. It's designed for flexibility, supporting a variety of popular sensors, displays, and communication protocols. With features like real-time visualization, data logging, and over-the-air updates, CO2-Gadget is perfect for both developers and end-users looking to monitor air quality effectively. + +It's really flexible, you can use this firmware with **any supported CO2 Monitor/Meter** based on ESP32 (99% of them). With cell phone App for real time visualization and charting of air quality data, datalogger, a variety of communication options (BLE, WIFI, MQTT, ESP-NOW) and many supported popular sensors. @@ -33,15 +37,17 @@ If you don't have a CO2 Monitor you will also find some complete tutorials to bu # Features -- Many popular CO2 sensors supported: Sensirion SCD30, Sensirion SCD4x (SCD40 and SCD41), Senseair S8 LP, Winsen MH-Z19 (A/B/C/D/E), Cubic CM1106 +- Many popular CO2 sensors supported: Sensirion SCD30, Sensirion SCD4x (SCD40 and SCD41), Senseair S8 LP, Winsen MH-Z19 (A/B/C/D/E), Cubic CM1106 & CM1106SL-NS (ultra low power) - Support for popular color and monochrome displays - Fully functional without display (display not needed) - Supports the Air Quality App Sensirion MyAmbiance for iOS and Android with real time visualization, charting and access to historycal data - Real time visualization on display, serial port and web page - Management and configuration via on screen menu, internal web page and console (USB/serial port) - Local data logger with upload to phone by BLE +- CO2 Sensor calibration via via on screen menu, internal web page, console (USB/serial port), Bluetooth or MQTT - WIFI connection -- Sending of data via MQTT +- Easy configuration of WIFI from your phone via Bluetooth or from the web page (Captive Portal) +- Sending of data via MQTT (measurement, battery, alarms, etc) - Receiving remote commands via MQTT - MQTT Discovery protocol for Home Assistant (and others supporting it as HomeSeer with mcsMQTT) - Easy installing via web browser @@ -64,6 +70,14 @@ As an example you can find a very detailed tutorial with step-by-step video on h For latest information on other hardware use (boards, sensors, displays, etc), please check options and GPIO to use at [my blog CO2 Gadget firmware page](https://emariete.com/en/co2-meter-gadget/) +## TFT Displays + +CO2 Gadget right now has support for a lot of different TFT displays. There are precompiled versions for TFT 240x135 and 320x170 pixels displays (included in ready made all in one boards as TTGO T-Display, T-Display S3 and others). + +## E-INK Displays + +You can use many different E-INK displays. There are precompiled versions for most popular E-INK displays and all in one boards with E-INK displays as TTGO T5. Check support for your e-ink display at [CO2 Gadget installation page](https://emariete.com/en/co2-meter-gadget/#Instalacion_de_CO2_Gadget_Advanced) + ## OLED Displays CO2 Gadget right now has support for many different OLED displays. There are precompiled versions for OLED I2C 1.3" 128x64 pixels display. @@ -145,53 +159,52 @@ NOTE: DHT22 is supported but is not recommended Full details on CanAirIO sensorlib [here](https://github.com/kike-canaries/canairio_sensorlib/) -# Building from repository +# Building from Repository + +## Using PlatformIO (Recommended) -## With PlatformIO (recommended) +If you're looking to compile and install CO2 Gadget from its repository, PlatformIO is the preferred method. However, if you only aim to install CO2 Gadget onto your board, you can do so directly from [here](https://emariete.com/en/meter-co2-gadget/) without compiling the firmware. -**Note:** If all you want is to install CO2 Gadget into your board go [here:](https://emariete.com/en/meter-co2-gadget/). You can install it from your web browser and don't need to compile the firmware. +### Installation Steps -### Install PlatformIO +You have two options for utilizing PlatformIO: either through its Command Line Interface (CLI) or via Visual Studio Code (VSCode) with PlatformIO's Graphical User Interface (GUI). Detailed installation instructions can be found [here](https://docs.platformio.org/en/latest/integration/ide/vscode.html). -You can use PlatfomIO CLI or VSCode with PlatformIO GUI (recomended). For installation see [PlatformIO installation instructions:](https://docs.platformio.org/en/latest/integration/ide/vscode.html). -### Prepare PlatformIO +### Preparing PlatformIO -First you must edit the file platformio.ini to setup your preferences. +Before proceeding, make sure to configure your preferences by editing the `platformio.ini` file, which is well-documented for easy customization. If required, adjust the upload and monitoring port configurations to match your setup. -If necessary adjust the upload and monitoring port configuration to match your situation. +```ini +upload_speed = 921600 +monitor_speed = 115200 +upload_port = COM13 +monitor_port = COM13 +``` - ``` - upload_speed = 921600 - monitor_speed = 115200 - upload_port = COM13 - monitor_port = COM13 - ``` - -Save the file platformio.ini +Save the changes made to the `platformio.ini` file. -#### Compiling and Installing +#### Compiling and Installation Process -I recommend PlatformIO because it is more easy than Arduino IDE. For this, please install first [PlatformIO](http://platformio.org/) and its command line tools (Windows, MacOs and Linux), **pio** command, then connect your compatible board to the USB and run the next command: +For ease of use, PlatformIO is recommended over the Arduino IDE. After installing PlatformIO and its command line tools on your system (compatible with Windows, MacOS, and Linux), connect your compatible board via USB and execute the following command: -```python -pio run pio run -e TTGO_TDISPLAY_SANDWICH --target upload +```bash +pio run -e TTGO_TDISPLAY_SANDWICH --target upload ``` -You must replace "TTGO_TDISPLAY_SANDWICH" with the flavour of CO2 Gadget you want compiled and uploaded (they are defined in platformio.ini or you can define your own). -If using PlatformIO **GUI**, to compile and upload CO2-Gadget into your board, press the "Alien head" -> Project tasks -> Choose flavour -> Upload and Monitor . +Ensure to replace "TTGO_TDISPLAY_SANDWICH" with the desired flavor of CO2 Gadget to be compiled and uploaded. You can find these flavors defined in the `platformio.ini` file or define your own. -## With Arduino +If using the PlatformIO GUI, follow these steps to compile and upload CO2-Gadget onto your board: press the "Alien head" icon, navigate to Project tasks, select your desired flavor, and choose Upload and Monitor. -**NOTE:** -Currently Arduino IDE is not supported. If you want to compile with the Arduino IDE, you will have to solve includes, dependencies and defines yourself. +## Using Arduino -I recommend that you use VS Code with PlatformIO. You have many tutorials on the internet, and it is not as difficult at all as it seems. +**Note:** +At present, Arduino IDE is not supported. Should you wish to compile using the Arduino IDE, you will need to address includes, dependencies, and defines independently. +It's recommended to utilize VS Code with PlatformIO instead. Numerous tutorials are available online, simplifying the process considerably. # Getting Involved Everyone is welcome to contribute to CO2 Gadget, regardless of their skill level or background. Whether you're tech-savvy, passionate about community development, or simply want to make a difference, there’s a role for you. -Contribute: See our Issues section to find current tasks or share your project ideas. Join us in creating the world's more advanced firmware for CO2 Meter/Monitor. +Contribute: See our Issues section to find current tasks or share your project ideas. Join us in creating the world's more advanced firmware for CO2 Monitoring. If you want to contribute to the code or documentation, consider posting a bug report, feature request or a pull request. @@ -206,12 +219,11 @@ When creating a pull request, we recommend that you do the following: - Document the PR description and code is a must - Target your pull request to be merged with the `development` branch -There are also private groups for committed and dedicated Alpha and Beta Testers +There are also private groups for committed and dedicated Alpha and Beta Testers. Read more about it [here](https://github.com/melkati/CO2-Gadget/issues/198) 🌟 We need testers! 🌟 # Supporting the project -Do you want to maka e acontribution to support the CO2 Gadget project? If so, **thank you!** -I don't want money but I will accept material donations so I can move forward with CO2 Gadget development. I always need some development board, some sensor or some tool. +Do you want to support the CO2-Gadget project? Thank you! While financial contributions are not requested, material donations are highly appreciated to aid further development. You can also help by spreading the word about CO2-Gadget and sharing your experiences with others. @@ -219,9 +231,7 @@ I don't want money but I will accept material donations so I can move forward wi # TO DO - [ ] [Implement low power functionality](https://github.com/melkati/CO2-Gadget/issues/188) -- [ ] [Add support for epaper displays (e-Ink or Electronic Ink)](https://github.com/melkati/CO2-Gadget/issues/190) - [ ] [Implement full support for PM 2.5](https://github.com/melkati/CO2-Gadget/issues/170) -- [ ] [Unify and improve the existing web pages](https://github.com/melkati/CO2-Gadget/issues/166) # Useful information @@ -248,7 +258,17 @@ All the [contributors that helped improving CO2 Gadget](https://github.com/melka --- ## License - Copyright (C) 2021-2024 CO2 Gadget Contributors + + ▐▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▌ + ▐ ____ ___ ____ ____ _ _ ▌ + ▐ / ___/ _ \___ \ / ___| __ _ __| | __ _ ___| |_ ▌ + ▐ | | | | | |__) | | | _ / _` |/ _` |/ _` |/ _ \ __| ▌ + ▐ | |__| |_| / __/ | |_| | (_| | (_| | (_| | __/ |_ ▌ + ▐ \____\___/_____| \____|\__,_|\__,_|\__, |\___|\__| ▌ + ▐ |___/ ▌ + ▐▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▌ + + Copyright (C) 2021-2024 Mariete & CO2 Gadget Contributors Contact: https://emariete.com This file is part of the CO2 Gadget firmware. @@ -266,3 +286,11 @@ All the [contributors that helped improving CO2 Gadget](https://github.com/melka You should have received a copy of the GNU Lesser General Public License along with the emariete.com Website and Documentation. If not, see . + + ▐▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▌ + ▐ _ _ ▌ + ▐ ___ _ __ ___ __ _ _ __(_) ___| |_ ___ ___ ___ _ __ ___ ▌ + ▐ / _ \ '_ ` _ \ / _` | '__| |/ _ \ __/ _ \ / __/ _ \| '_ ` _ \ ▌ + ▐ | __/ | | | | | (_| | | | | __/ || __/| (_| (_) | | | | | | ▌ + ▐ \___|_| |_| |_|\__,_|_| |_|\___|\__\___(_)___\___/|_| |_| |_| ▌ + ▐▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▌ From c6010ba5ebfe1bc38aba0644a223c5ddb9038185 Mon Sep 17 00:00:00 2001 From: Mario Mariete <11509521+melkati@users.noreply.github.com> Date: Sun, 2 Jun 2024 11:56:59 +0200 Subject: [PATCH 07/15] Add text header with copyright information --- CO2_Gadget.ino | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/CO2_Gadget.ino b/CO2_Gadget.ino index 7616b5bc..0407890b 100644 --- a/CO2_Gadget.ino +++ b/CO2_Gadget.ino @@ -40,6 +40,8 @@ /*****************************************************************************************************/ +#define SUPPORT_CAPTIVE_PORTAL // Please, don't disable this. + // Functions and enum definitions void reverseButtons(bool reversed); // Defined in CO2_Gadget_Buttons.h void outputsLoop(); // Defined in CO2_Gadget_Main.h @@ -89,11 +91,11 @@ uint16_t WiFiConnectionRetries = 0; uint16_t maxWiFiConnectionRetries = 10; bool wifiChanged = false; bool useStaticIP = false; // Set to true if you want to use a static IP -IPAddress staticIP(192, 168, 1, 199); // Change this to the desired IP -IPAddress gateway(192, 168, 1, 1); // Change this to your network's gateway -IPAddress subnet(255, 255, 255, 0); // Change this to your network's subnet mask -IPAddress dns1(8, 8, 8, 8); // Change this to your preferred DNS server -IPAddress dns2(8, 8, 4, 4); // Change this to your secondary DNS server +IPAddress staticIP(192, 168, 1, 199); // Static IP address +IPAddress gateway(192, 168, 1, 1); // Network gateway +IPAddress subnet(255, 255, 255, 0); // Subnet mask +IPAddress dns1(8, 8, 8, 8); // DNS server +IPAddress dns2(8, 8, 4, 4); // DNS server // MQTT options bool activeMQTT = true; From 9bbd0021048a9b31e4ecdd91fb1d59a34aa338ab Mon Sep 17 00:00:00 2001 From: Mariete Date: Sun, 2 Jun 2024 12:15:44 +0200 Subject: [PATCH 08/15] Update README.md --- README.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 006bc501..4c19eb1c 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,9 @@ This repository is mainly addressed at developers. If you are an end user willin If you don't have a CO2 Monitor you will also find some complete tutorials to build your own. -![CO2_Gadget_DIY_CO2_Monitor](https://github.com/melkati/CO2-Gadget/assets/11509521/58e1f306-af46-416f-a399-5900965e8c10) +

+ +

# Features @@ -66,7 +68,9 @@ This project support a large selection of ESP32 boards, displays and sensors. As an example you can find a very detailed tutorial with step-by-step video on how to build a very compact CO2 Gadget with a TTGO T-Display board and a high quality Sensirion SCD30 dual channel NDIR CO2 sensor (and battery support) [here](https://emariete.com/en/meter-co2-display-tft-color-ttgo-t-display-sensirion-scd30-2/). -![image](https://user-images.githubusercontent.com/11509521/146636210-ee11a49a-5ebc-4e3c-a11e-91e2d8676410.png) +

+ +

For latest information on other hardware use (boards, sensors, displays, etc), please check options and GPIO to use at [my blog CO2 Gadget firmware page](https://emariete.com/en/co2-meter-gadget/) @@ -74,10 +78,18 @@ For latest information on other hardware use (boards, sensors, displays, etc), p CO2 Gadget right now has support for a lot of different TFT displays. There are precompiled versions for TFT 240x135 and 320x170 pixels displays (included in ready made all in one boards as TTGO T-Display, T-Display S3 and others). +

+ +

+ ## E-INK Displays You can use many different E-INK displays. There are precompiled versions for most popular E-INK displays and all in one boards with E-INK displays as TTGO T5. Check support for your e-ink display at [CO2 Gadget installation page](https://emariete.com/en/co2-meter-gadget/#Instalacion_de_CO2_Gadget_Advanced) +

+ +

+ ## OLED Displays CO2 Gadget right now has support for many different OLED displays. There are precompiled versions for OLED I2C 1.3" 128x64 pixels display. From 99ef89c311c99bff094240df9703b84bb842464d Mon Sep 17 00:00:00 2001 From: Mariete Date: Sun, 2 Jun 2024 12:31:07 +0200 Subject: [PATCH 09/15] Update README.md --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4c19eb1c..fa4bf57e 100644 --- a/README.md +++ b/README.md @@ -4,15 +4,15 @@ [![GitHub stars](https://img.shields.io/github/stars/melkati/CO2-Gadget.svg?style=social&label=Star)](https://github.com/melkati/CO2-Gadget/stargazers) [![GitHub forks](https://img.shields.io/github/forks/melkati/CO2-Gadget.svg?style=social&label=Fork)](https://github.com/melkati/CO2-Gadget/network) -
+
- Don't forget to star ⭐ this repository + Don't forget to star ⭐ this repository NOW!
- +
🌟 Calling All Tech Enthusiasts! Join the CO2 Gadget Testing Adventure 🌟 @@ -170,7 +170,6 @@ NOTE: DHT22 is supported but is not recommended Full details on CanAirIO sensorlib [here](https://github.com/kike-canaries/canairio_sensorlib/) - # Building from Repository ## Using PlatformIO (Recommended) From 6996ba92067b2dc0d2e7d3ed50f66e9db79e0da3 Mon Sep 17 00:00:00 2001 From: Mario Mariete <11509521+melkati@users.noreply.github.com> Date: Sun, 2 Jun 2024 12:58:32 +0200 Subject: [PATCH 10/15] Update README.md --- README.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fa4bf57e..30a5f7dc 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,6 @@ These are the GPIOs used by each predefined board: | TDISPLAY_S3 | TFT 320x170 | 18/17 | 43/44 | 14/0 | 03 | 01 | 04 | 16 | 2 | esp32dev_OLED SSH1106 | SSH1106 128×64 | 17/16 | 21/22 | 15/0 | 32 | 33 | 34 | 26 | 2 | esp32dev | No display | 17/16 | 21/22 | 15/0 | 32 | 33 | 34 | 26 | 2 -| esp32dev-sandwich | No display | 17/16 | 22/21 | 15/0 | 32 | 33 | 34 | 26 | 2 | esp32dev-ST7789_240x320 | ST7789_240x320 | 17/16 | 21/22 | 19/0 | 32 | 33 | 34 | 26 | 2 - Flavour: Name of the firmware variant. @@ -170,6 +169,15 @@ NOTE: DHT22 is supported but is not recommended Full details on CanAirIO sensorlib [here](https://github.com/kike-canaries/canairio_sensorlib/) +# Installing + +Installing the CO2 Gadget advanced firmware on the board with the ESP32is super easy. You don't have to download or install anything on your PC. + +Just click the button corresponding to the version you want to install, select the port where your board is connected and click "Connect". It's as simple as that, the uploading into the ESP32 will be done in a few seconds without any further complication with only a few clicks... + +Visit the [CO2 Gadget page](https://emariete.com/en/meter-co2-gadget/) to install from the comfort of your internet browser. + + # Building from Repository ## Using PlatformIO (Recommended) @@ -198,10 +206,10 @@ Save the changes made to the `platformio.ini` file. For ease of use, PlatformIO is recommended over the Arduino IDE. After installing PlatformIO and its command line tools on your system (compatible with Windows, MacOS, and Linux), connect your compatible board via USB and execute the following command: ```bash -pio run -e TTGO_TDISPLAY_SANDWICH --target upload +pio run -e TTGO_TDISPLAY --target upload ``` -Ensure to replace "TTGO_TDISPLAY_SANDWICH" with the desired flavor of CO2 Gadget to be compiled and uploaded. You can find these flavors defined in the `platformio.ini` file or define your own. +Ensure to replace "TTGO_TDISPLAY" with the desired flavor of CO2 Gadget to be compiled and uploaded. You can find these flavors defined in the `platformio.ini` file or define your own. If using the PlatformIO GUI, follow these steps to compile and upload CO2-Gadget onto your board: press the "Alien head" icon, navigate to Project tasks, select your desired flavor, and choose Upload and Monitor. From 487b0d5cf1a5df8eb433e97b77a3c5512115b039 Mon Sep 17 00:00:00 2001 From: Mario Mariete <11509521+melkati@users.noreply.github.com> Date: Sun, 2 Jun 2024 13:13:23 +0200 Subject: [PATCH 11/15] chore: Update CO2_GADGET_REV to "084-development" in platformio.ini --- platformio.ini | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/platformio.ini b/platformio.ini index de31705f..bf0af6dc 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,7 +10,7 @@ extra_configs = platformio_extra_configs.ini [version] build_flags = -D CO2_GADGET_VERSION="\"0.12."\" - -D CO2_GADGET_REV="\"083-development"\" + -D CO2_GADGET_REV="\"084-development"\" ;**************************************************************************************** ;*** You can disable features by commenting the line with a semicolon at the beginning @@ -21,11 +21,10 @@ build_flags = -DSUPPORT_BLE -DSUPPORT_BUZZER ; -DSUPPORT_ESPNOW - ; -DSUPPORT_MDNS + -DSUPPORT_MDNS -DSUPPORT_MQTT -DSUPPORT_MQTT_DISCOVERY -DSUPPORT_OTA - -DSUPPORT_CAPTIVE_PORTAL ;**************************************************************************************** ;*** This will be deprecated. It's here only for backward compatibility. @@ -161,7 +160,6 @@ build_flags = ;**************************************************************************************** ;*** Environment specific data. You can override the common data for each environment ;**************************************************************************************** - [env:esp32dev] platform = https://github.com/platformio/platform-espressif32.git board = esp32dev From 65d30f859a628fc6c54ee7777fc5e9e2052dc701 Mon Sep 17 00:00:00 2001 From: Mario Mariete <11509521+melkati@users.noreply.github.com> Date: Sun, 2 Jun 2024 13:21:34 +0200 Subject: [PATCH 12/15] Prepare for release --- platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index bf0af6dc..e5499523 100644 --- a/platformio.ini +++ b/platformio.ini @@ -9,8 +9,8 @@ extra_configs = platformio_extra_configs.ini [version] build_flags = - -D CO2_GADGET_VERSION="\"0.12."\" - -D CO2_GADGET_REV="\"084-development"\" + -D CO2_GADGET_VERSION="\"0.14."\" + -D CO2_GADGET_REV="\"0"\" ;**************************************************************************************** ;*** You can disable features by commenting the line with a semicolon at the beginning From 636615933ec64ec1f40b2533617c51b65c561ba8 Mon Sep 17 00:00:00 2001 From: Mario Mariete <11509521+melkati@users.noreply.github.com> Date: Sun, 2 Jun 2024 13:30:41 +0200 Subject: [PATCH 13/15] Update GitHub Action for release --- .github/workflows/release3.yml | 374 ++++++++++++++++----------------- 1 file changed, 179 insertions(+), 195 deletions(-) diff --git a/.github/workflows/release3.yml b/.github/workflows/release3.yml index 3ae761df..e480d978 100644 --- a/.github/workflows/release3.yml +++ b/.github/workflows/release3.yml @@ -1,11 +1,11 @@ -name: Release V2.5 +name: Release V2.93 # # # # # # # # # # # # To create new release, just push a new tag with the format v* (v1.0, v2.0, v2.1, v2.2, etc) -# +# # In a terminal in platformIO do, for example: -# git tag -a v0.10.005 -m "For general release v0.10.005" -# git push origin v0.10.005 +# git tag -a v1.0.1 -m "Release v1.0.1" +# git push origin v1.0.1 # # # # # # # # # # # on: @@ -15,196 +15,180 @@ on: workflow_dispatch: jobs: + build_release: + name: Create Release + + runs-on: ubuntu-latest + + strategy: + matrix: + environment: + - esp32dev + - esp32dev_OLED + - TTGO_TDISPLAY + - TTGO_TDISPLAY_SANDWICH + - TDISPLAY_S3 + - esp32dev_ST7789_240x320 + - ttgo-t5-EINKBOARDGDEM0213B74 + - ttgo-t5-EINKBOARDDEPG0213BN + - ttgo-t5-EINKBOARDGDEW0213M21 + - ttgo-t7-EINKBOARDGDEM029T94 + - ttgo-t7-WEACT_GDEH0154D67 + - ttgo-t7-WEACT_DEPG0213BN + - ttgo-t7-WEACT_GxEPD2_290_BS + + env: + CHIP_FAMILY: ${{ matrix.environment == 'TDISPLAY_S3' && 'ESP32-S3' || 'ESP32' }} + + timeout-minutes: 30 + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Show environments + run: | + echo + + - name: Determine chipFamily + id: determine_chip_family + run: | + case "${{ matrix.environment }}" in + "esp32dev") + CHIP_FAMILY="ESP32";; + "TDISPLAY_S3") + CHIP_FAMILY="ESP32-S3";; + # Add more cases for other environments as needed + *) + CHIP_FAMILY="UNKNOWN";; + esac + echo "::set-output name=chipFamily::${CHIP_FAMILY}" + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio update + + - name: Get the version + id: get_version + run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/} + + - name: Show version + run: echo ${{ steps.get_version.outputs.VERSION }} + + - name: Get current date + id: date + run: | + echo "::set-output name=date::$(date +'%d-%m-%Y')" + echo "::set-output name=time::$(date +'%H:%M:%S')" + + - name: Create manifest file + id: createmanifest + run: | + + # Define offset values based on env.CHIP_FAMILY + if [[ "${{ env.CHIP_FAMILY }}" == "ESP32-S3" ]]; then + bootloader_offset=0 + partitions_offset=32768 + firmware_offset=65536 + spiffs_offset=3604480 + else + # Default values for ESP32 or other environments + bootloader_offset=4096 + partitions_offset=32768 + firmware_offset=65536 + spiffs_offset=3604480 + fi + + # Create manifest + echo "{" > ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + echo " \"name\": \"${{ github.event.repository.name }}-${{ matrix.environment }}\"," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + echo " \"flavour\": \"${{ matrix.environment }}\"," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + echo " \"version\": \"${{ steps.get_version.outputs.VERSION }}\"," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + echo " \"compilation_date\": \"${{ steps.date.outputs.date }}\"," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + echo " \"compilation_time\": \"${{ steps.date.outputs.time }}\"," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + echo " \"new_install_prompt_erase\": true," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + echo " \"new_install_improv_wait_time\": 20," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + echo " \"builds\": [" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + echo " {" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + echo " \"chipFamily\": \"${{ env.CHIP_FAMILY }}\"," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + echo " \"improv\": true," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + echo " \"parts\": [" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + echo " { \"path\": \"${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.get_version.outputs.VERSION }}-bootloader.bin\", \"offset\": $bootloader_offset }," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + echo " { \"path\": \"${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.get_version.outputs.VERSION }}-partitions.bin\", \"offset\": $partitions_offset }," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + echo " { \"path\": \"${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.get_version.outputs.VERSION }}-firmware.bin\", \"offset\": $firmware_offset }," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + echo " { \"path\": \"${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.get_version.outputs.VERSION }}-spiffs.bin\", \"offset\": $spiffs_offset }" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + echo " ]" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + echo " }" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + echo " ]" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + echo "}" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + echo "::set-output name=manifest::$(cat ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json)" + + - name: Read manifest files + run: | + echo "Manifest:" + echo ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + echo "Manifest file contents readed with cat:" + cat ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + + - name: Copy manifest files + if: startsWith(github.ref, 'refs/tags/') + run: | + mkdir ./firmware + cp ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json ./firmware/${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json + + - name: Build firmware file + run: | + pio run -e ${{ matrix.environment }} + + - name: Copy firmware files + if: startsWith(github.ref, 'refs/tags/') + run: | + cp .pio/build/${{ matrix.environment }}/bootloader.bin ./firmware/${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.get_version.outputs.VERSION }}-bootloader.bin + cp .pio/build/${{ matrix.environment }}/partitions.bin ./firmware/${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.get_version.outputs.VERSION }}-partitions.bin + cp .pio/build/${{ matrix.environment }}/firmware.bin ./firmware/${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.get_version.outputs.VERSION }}-firmware.bin + + - name: Build spiffs file + run: | + pio run -e ${{ matrix.environment }} -t buildfs + + - name: Copy spiffs files + if: startsWith(github.ref, 'refs/tags/') + run: | + ls -la ./firmware + cp .pio/build/${{ matrix.environment }}/spiffs.bin ./firmware/${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.get_version.outputs.VERSION }}-spiffs.bin + + - name: Create Release + id: create_release + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') + with: + name: ${{ github.event.repository.name }}-${{ steps.get_version.outputs.VERSION }} + files: | + ./firmware/*.bin + ./firmware/*.json + draft: false + generate_release_notes: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # get_default_envs: - # name: Gather Environments - # runs-on: ubuntu-latest - # steps: - # - uses: actions/checkout@v2 - # - uses: actions/setup-python@v2 - # - name: Install PlatformIO - # run: | - # pip install -U platformio - # platformio update - # - name: Dump github context - # run: echo "$GITHUB_CONTEXT" - # shell: bash - # env: - # GITHUB_CONTEXT: ${{ toJson(github) }} - # - name: Get default environments - # id: envs - # run: | - # echo "::set-output name=environments::$(pio project config --json-output | jq -cr '.[0][1][0][1]')" - # echo ${{ steps.envs.outputs.environments }} - # outputs: - # environments: ${{ steps.envs.outputs.environments }} - - build: - name: Create Release - - runs-on: ubuntu-latest - # needs: get_default_envs - - strategy: - matrix: - # environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }} - environment: [esp32dev, esp32dev_OLED, TTGO_TDISPLAY, TTGO_TDISPLAY_SANDWICH, TDISPLAY_S3, esp32dev_ST7789_240x320] - + - name: 📂 Sync files - FTP-Deploy-Action + uses: SamKirkland/FTP-Deploy-Action@2.0.0 env: - CHIP_FAMILY: ${{ matrix.environment == 'TDISPLAY_S3' && 'ESP32-S3' || 'ESP32' }} - - timeout-minutes: 30 # time out after 30 minutes (default is 360 minutes) - - steps: - - - name: Checkout code - uses: actions/checkout@v2 - - - name: Show enviroments - run: | - echo - - - name: Determine chipFamily - id: determine_chip_family - run: | - case "${{ matrix.environment }}" in - "esp32dev") - CHIP_FAMILY="ESP32";; - "TDISPLAY_S3") - CHIP_FAMILY="ESP32-S3";; - # Add more cases for other environments as needed - *) - CHIP_FAMILY="UNKNOWN";; - esac - echo "::set-output name=chipFamily::${CHIP_FAMILY}" - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 - with: - python-version: ${{ matrix.python-version }} - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -U platformio - platformio update - - - name: Get the version - id: get_version - run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/} - - - name: Show version - run: echo ${{ steps.get_version.outputs.VERSION }} - - - name: Get current date - id: date - run: | - echo "::set-output name=date::$(date +'%d-%m-%Y')" - echo "::set-output name=time::$(date +'%H:%M:%S')" - - - name: Create manifest file - id: createmanifest - run: | - - # Define offset values based on env.CHIP_FAMILY - if [[ "${{ env.CHIP_FAMILY }}" == "ESP32-S3" ]]; then - bootloader_offset=0 - partitions_offset=32768 - app0_offset=57344 - firmware_offset=65536 - spiffs_offset=13172736 - else - # Default values for ESP32 or other environments - bootloader_offset=4096 - partitions_offset=32768 - app0_offset=57344 - firmware_offset=65536 - spiffs_offset=3997696 - fi - - # Create manifest - echo "{" > ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - echo " \"name\": \"${{ github.event.repository.name }}-${{ matrix.environment }}\"," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - echo " \"flavour\": \"${{ matrix.environment }}\"," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - echo " \"version\": \"${{ steps.get_version.outputs.VERSION }}\"," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - echo " \"compilation_date\": \"${{ steps.date.outputs.date }}\"," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - echo " \"compilation_time\": \"${{ steps.date.outputs.time }}\"," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - echo " \"new_install_prompt_erase\": true," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - echo " \"new_install_improv_wait_time\": 20," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - echo " \"builds\": [" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - echo " {" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - echo " \"chipFamily\": \"${{ env.CHIP_FAMILY }}\"," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - echo " \"improv\": true," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - echo " \"parts\": [" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - echo " { \"path\": \"${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.get_version.outputs.VERSION }}-bootloader.bin\", \"offset\": $bootloader_offset }," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - echo " { \"path\": \"${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.get_version.outputs.VERSION }}-partitions.bin\", \"offset\": $partitions_offset }," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - echo " { \"path\": \"${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.get_version.outputs.VERSION }}-firmware.bin\", \"offset\": $firmware_offset }," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - echo " { \"path\": \"${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.get_version.outputs.VERSION }}-spiffs.bin\", \"offset\": $spiffs_offset }" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - echo " ]" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - echo " }" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - echo " ]" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - echo "}" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - echo "::set-output name=manifest::$(cat ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json)" - - - name: Read manifest files - run: | - echo "Manifest:" - echo ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - echo "Manifest file contents readed with cat:" - cat ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - - - name: Copy manifest files - if: startsWith(github.ref, 'refs/tags/') - run: | - mkdir ./firmware - cp ${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json ./firmware/${{ github.event.repository.name }}-${{ matrix.environment }}.manifest.json - - - name: Build firmware file - run: | - pio run -e ${{ matrix.environment }} - - - name: Copy firmware files - if: startsWith(github.ref, 'refs/tags/') - run: | - cp .pio/build/${{ matrix.environment }}/bootloader.bin ./firmware/${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.get_version.outputs.VERSION }}-bootloader.bin - cp .pio/build/${{ matrix.environment }}/partitions.bin ./firmware/${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.get_version.outputs.VERSION }}-partitions.bin - cp .pio/build/${{ matrix.environment }}/firmware.bin ./firmware/${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.get_version.outputs.VERSION }}-firmware.bin - - - name: Build spiffs file - run: | - pio run -e ${{ matrix.environment }} -t buildfs - - - name: Copy spiffs files - if: startsWith(github.ref, 'refs/tags/') - run: | - ls -la ./firmware - cp .pio/build/${{ matrix.environment }}/spiffs.bin ./firmware/${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.get_version.outputs.VERSION }}-spiffs.bin - - - name: Create Release - id: create_release - uses: softprops/action-gh-release@v1 - if: startsWith(github.ref, 'refs/tags/') - with: - name: ${{ github.event.repository.name }}-${{ steps.get_version.outputs.VERSION }} - files: | - ./firmware/*.bin - ./firmware/*.json - draft: false - # prerelease: true - generate_release_notes: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_ACTION_TOKEN }} - - name: 📂 Sync files - FTP-Deploy-Action - uses: SamKirkland/FTP-Deploy-Action@2.0.0 - env: - FTP_SERVER: ${{ secrets.FTP_SERVER }} - FTP_USERNAME: ${{ secrets.FTP_USER }} - FTP_PASSWORD: ${{ secrets.FTP_PASSWORD }} - LOCAL_DIR: ./firmware/ - REMOTE_DIR: /${{ github.event.repository.name }}/ - METHOD: ftp - PORT: 21 - ARGS: --verbose - - name: Clean eMariete.com Cache - # run: curl https://emariete.com/clean_cache_wp_rocket.php - run: curl https://emariete.com/clean_cache_litespeed.php + FTP_SERVER: ${{ secrets.FTP_SERVER }} + FTP_USERNAME: ${{ secrets.FTP_USER }} + FTP_PASSWORD: ${{ secrets.FTP_PASSWORD }} + LOCAL_DIR: ./firmware/ + REMOTE_DIR: /${{ github.event.repository.name }}/ + METHOD: ftp + PORT: 21 + ARGS: --verbose + + - name: Clean eMariete.com Cache + run: curl https://emariete.com/clean_cache_litespeed.php From be6a94039088588c310bfd9aaf567eb68e864e6d Mon Sep 17 00:00:00 2001 From: Mario Mariete <11509521+melkati@users.noreply.github.com> Date: Sun, 2 Jun 2024 15:19:11 +0200 Subject: [PATCH 14/15] Improve preferences.h logging --- CO2_Gadget_Preferences.h | 11 +++++++++++ platformio.ini | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CO2_Gadget_Preferences.h b/CO2_Gadget_Preferences.h index 250da990..ab11d269 100644 --- a/CO2_Gadget_Preferences.h +++ b/CO2_Gadget_Preferences.h @@ -709,6 +709,7 @@ bool handleSavePreferencesFromJSON(String jsonPreferences) { preferences.begin("CO2-Gadget", false); customCalibrationValue = JsonDocument["customCalValue"]; if (tempOffset != float(JsonDocument["tempOffset"])) { + Serial.println("-->[PREF] Temp Offset changed from " + String(tempOffset) + " to " + String(JsonDocument["tempOffset"].as())); tempOffset = float(JsonDocument["tempOffset"]); sensors.setTempOffset(tempOffset); } @@ -733,6 +734,7 @@ bool handleSavePreferencesFromJSON(String jsonPreferences) { batteryDischargedMillivolts = JsonDocument["batDischgd"]; batteryFullyChargedMillivolts = JsonDocument["batChargd"]; if (vRef != JsonDocument["vRef"]) { // If battery reference changed, apply it + Serial.println("-->[PREF] vRef changed from " + String(vRef) + " to " + String(JsonDocument["vRef"].as())); vRef = JsonDocument["vRef"]; battery.begin(vRef, voltageDividerRatio, &asigmoidal); readBatteryVoltage(); @@ -763,6 +765,7 @@ bool handleSavePreferencesFromJSON(String jsonPreferences) { debugSensors = JsonDocument["debugSensors"]; #if defined(SUPPORT_TFT) || defined(SUPPORT_OLED) || defined(SUPPORT_EINK) if (displayReverse != JsonDocument["displayReverse"]) { + Serial.println("-->[PREF] Display Reverse changed from " + String(displayReverse) + " to " + String(JsonDocument["displayReverse"].as())); displayReverse = JsonDocument["displayReverse"]; setDisplayReverse(displayReverse); reverseButtons(displayReverse); @@ -790,31 +793,37 @@ bool handleSavePreferencesFromJSON(String jsonPreferences) { } if (displayShowTemperature != JsonDocument["showTemp"]) { + Serial.println("-->[PREF] Display Temperature changed from " + String(displayShowTemperature) + " to " + String(JsonDocument["showTemp"].as())); displayShowTemperature = JsonDocument["showTemp"]; shouldRedrawDisplay = true; } if (displayShowHumidity != JsonDocument["showHumidity"]) { + Serial.println("-->[PREF] Display Humidity changed from " + String(displayShowHumidity) + " to " + String(JsonDocument["showHumidity"].as())); displayShowHumidity = JsonDocument["showHumidity"]; shouldRedrawDisplay = true; } if (displayShowBattery != JsonDocument["showBattery"]) { + Serial.println("-->[PREF] Display Battery changed from " + String(displayShowBattery) + " to " + String(JsonDocument["showBattery"].as())); displayShowBattery = JsonDocument["showBattery"]; shouldRedrawDisplay = true; } if (displayShowBatteryVoltage != JsonDocument["showBattVolt"]) { + Serial.println("-->[PREF] Display Battery Voltage changed from " + String(displayShowBatteryVoltage) + " to " + String(JsonDocument["showBattVolt"].as())); displayShowBatteryVoltage = JsonDocument["showBattVolt"]; shouldRedrawDisplay = true; } if (displayShowCO2 != JsonDocument["showCO2"]) { + Serial.println("-->[PREF] Display CO2 changed from " + String(displayShowCO2) + " to " + String(JsonDocument["showCO2"].as())); displayShowCO2 = JsonDocument["showCO2"]; shouldRedrawDisplay = true; } if (displayShowPM25 != JsonDocument["showPM25"]) { + Serial.println("-->[PREF] Display PM2.5 changed from " + String(displayShowPM25) + " to " + String(JsonDocument["showPM25"].as())); displayShowPM25 = JsonDocument["showPM25"]; shouldRedrawDisplay = true; } @@ -854,11 +863,13 @@ bool handleSavePreferencesFromJSON(String jsonPreferences) { // If JsonDocument["wifiPass"] is present and different from the current one, update the wifiPass variable if (JsonDocument.containsKey("wifiPass") && wifiPass != JsonDocument["wifiPass"].as().c_str()) { + Serial.println("-->[PREF] WiFi Password changed. Updating WiFi Password in preferences"); wifiPass = JsonDocument["wifiPass"].as().c_str(); wifiChanged = true; } // If JsonDocument["mqttPass"] is present and different from the current one, update the mqttPass variable if (JsonDocument.containsKey("mqttPass") && mqttPass != JsonDocument["mqttPass"].as().c_str()) { + Serial.println("-->[PREF] MQTT Password changed. Updating MQTT Password in preferences"); mqttPass = JsonDocument["mqttPass"].as().c_str(); wifiChanged = true; } diff --git a/platformio.ini b/platformio.ini index e5499523..352cd1c3 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,7 +10,7 @@ extra_configs = platformio_extra_configs.ini [version] build_flags = -D CO2_GADGET_VERSION="\"0.14."\" - -D CO2_GADGET_REV="\"0"\" + -D CO2_GADGET_REV="\"000"\" ;**************************************************************************************** ;*** You can disable features by commenting the line with a semicolon at the beginning From e8de5dea9de74aacc0785a22a2dd32e86903629a Mon Sep 17 00:00:00 2001 From: Mario Mariete <11509521+melkati@users.noreply.github.com> Date: Mon, 3 Jun 2024 07:15:49 +0200 Subject: [PATCH 15/15] chore: Update font color in style.css to increase contrast for value and units in dark mode --- data/style.css | 4 ++-- data/style.css.gz | Bin 2412 -> 2413 bytes 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/style.css b/data/style.css index 639e1b50..bf88c2c1 100644 --- a/data/style.css +++ b/data/style.css @@ -360,14 +360,14 @@ button:hover { } .value { - fill: #666; + fill: var(--font-color); font-family: sans-serif; font-size: 0.7em; text-anchor: middle; } .unit { - fill: #666; + fill: var(--font-color); font-family: sans-serif; font-size: 0.3em; text-anchor: middle; diff --git a/data/style.css.gz b/data/style.css.gz index c8b3db1f035eaba64a211a0750031fa13da0db55..7686d9fdc08584f9783d64e286e878439da57497 100644 GIT binary patch delta 2407 zcmV-t37Gcm673QPABzYGmrz}i2O)pFV=?h98WR(ZCVnw~C{VU41JZ(5CH!|!TcFUw zoPIMm_l~uP({rAl_Z!lps2bZ&i@dVqA}eTkZ@1eWY&$yk-_taO?-i*s+%N5S7-Iyh zBrfvdJEOI@Y%QVeR0T9wYS7S&N^Iy8_A+M!-Pgpib{M^aGM4 z;mYS_U1>XU91lB6*(sD!T~$Rsv%QT`8aM-mRC$grK+bm0F9jX#1krD!+wx)`3m#=r z)+OIi>h!A$WOcnokyT_&9|V8Fus-)jf}{GV=mn&%AFMrRxAp=K?Bi8W)`6sMj|&)Q z1;d@HJrkU08wL4=@?dR%*&-lp$&g#Jz&GK=aI;jf` zbU1bUcTp7Yoqayj)2rEd@nPfl12hS)$*@HDh zIkOq2Bt00H@Ey^I%5{HMhw&=s)vuosJ(4_h+LOCJdFzwEK5ZLCtgO&{$8*4J9m|D} zLkC+_00!sKgAG<>B(WwuBvH?$L`g#OWB*Q2{s0-Io{ap$p@+}y$r~-Wo5ulP^CxR! zX**f+_}WG~T9Xh~X6R>_X86Z}|FIK7aa@ri4?&XpoF8sUQk{Q7*KzJI;Aln*O^MLa z5O605%*KQjyz--u17oUgZFTqszM9}4Tr%fS7K{j?Db7$uzGI;wu*xf(S5q~=)~q7w z&wevxC5pj7adnGven1&H<~FJDnT0WIV>&`$(8z@g5qH}oX`x0KXM*E`A|Xm%|BDvE#dP7y(1ZG2{7#IQOh6fQI|LNng36#WLp9FL|ihxU5_i*UVc{2laT zT+R-zjO5>#o#C`vkG@lKJZTV`Ahr4n0EqMi!7p?vw>qAKUP8!w%PFS&Tz$D9-j{D2 z;+irLn@g;2!rKDv33Dp(tnuv-5?H1MJ%=nt8Gh(O^`w7OcG#Gt0gys`o;nW5GaEkU z{i4gmJ&oXWD}|1VEI9}>5I}Yi_L0LCkz+6oVQGPiA`~U0W+J|fe1uyN5N`Ec>8%d^ z&5<0g2Mj$c;%{S18%aMH-FkS^$`DJyVrY382{YBL+IuC%DW*7&F?$8jH-8M$Q~ht| z)OQ`TV(x!|6o@TF`ZgsvOBk*ic}nPB6QrjS1%m|-aSoOyA*0Rk5$B0+R;F=W&M|F< zBAhjI7pqASTu_Nq$T_Hr>ZjeRLZM?Guf^)IG`qnbxItcsJu2aK(#u$WQ3tcq9zqPoP&?AzbOpuhe)WTt_9Oa^r>7i& zgQ$Ns?nUB7sn7Zzl?*!d#>rEVYx*1MwtafW2|*T%DM)LfJh2|0(GOdi`OX27RWqym zk&@)V|JYCgz)FQ}SuC>9O{oRHLS_!IEM_!qH~0h=E+ZVmJXh9q_(?-z?z|S2QPc{P|3y z&JgWR=(&v3ql85ml>+0&xi7x;m1Q8VzMGIlPz#bQlSMtNXz>k;AT%AJth%j^qrZQ- z=^3cErzGCyreEMmJO;Pvkju$g_7Np5j$lm8a%E_=i@LI)TD?UyVeQjz_-9Jd88fRV zY@GW|$1_0;K%k&LZ$+I}M+a=evpNyGclgk3p8~B;%6;9S-Ny0by-9nj&%~hh<2}Yf zyZMf?x&y>)xyP-%YT-xby?Kn!UGaaM$0rEmXObitbTD;Js(jK`KZq22M#@qtST|JfLDTZh7)V=+_w;) zlpJm;srabjTqI!Am{tUuAe&|qj)D?6Tv}pXitN(1_rw^g7gw0I^52yFfW&`V6=fKJ zNAYwc9#oSho_N-HXv4;u|5>bl#6hDbz}TB@r)T=5K<^ivnQkG_#tO;_Q>ek;P{qqOOo&pi#W?j z$p{l6N~No-lK7mA;{@i75{7?-1+Ew(!)ufrarFsP1`tdG>6NAQpGR*-qWb4P&jj5B zx!rW5P%#c~rX{P|_>~bi${or_aHa%vg>+4HQDsW>JO8+D+ROI_hWZ)o-@qT z zT`kf1nQC;+!d%_i`vVh005Iuq6+{3 delta 2406 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 Y$v3-ZS1ZYy)s(mX1~~_rT3jLk0O1y@