Przeglądaj źródła

Software updater functionality and Updated Changelog

Thokoop 1 rok temu
rodzic
commit
ffec0f7937
5 zmienionych plików z 216 dodań i 24 usunięć
  1. 9 2
      CHANGELOG.md
  2. 106 1
      app.py
  3. 27 6
      static/css/style.css
  4. 56 10
      static/js/main.js
  5. 18 5
      templates/index.html

+ 9 - 2
CHANGELOG.md

@@ -2,9 +2,16 @@
 
 All notable changes to this project will be documented in this file.
 
-## [1.4.0] Firmware updater
+## [1.4.0] Soft- and Firmware updater
 
-- Added Firmware updater to make it possible to flash the Arduino Firmware from the web app.
+### Added
+
+- Added Software Updater to make it easier to update the app and get the latest firmware updates for the Arduino
+- Added Firmware updater to make it possible to flash the Firmware from the web app.
+- Included Icons in UI
+
+### Changed
+- Moved Roboto font files locally
 
 ## [1.3.0] Revamped UI
 

+ 106 - 1
app.py

@@ -102,6 +102,59 @@ def get_ino_firmware_details(ino_file_path):
         print(f"Error reading .ino file: {str(e)}")
         return None
 
+def check_git_updates():
+    try:
+        # Fetch the latest updates from the remote repository
+        subprocess.run(["git", "fetch", "--tags", "--force"], check=True)
+
+        # Get the latest tag from the remote
+        latest_remote_tag = subprocess.check_output(
+            ["git", "describe", "--tags", "--abbrev=0", "origin/main"]
+        ).strip().decode()
+
+        # Get the latest tag from the local branch
+        latest_local_tag = subprocess.check_output(
+            ["git", "describe", "--tags", "--abbrev=0"]
+        ).strip().decode()
+
+        # Check if there are new commits
+        local_hash = subprocess.check_output(["git", "rev-parse", "HEAD"]).strip()
+        remote_hash = subprocess.check_output(["git", "rev-parse", "origin/main"]).strip()
+        updates_available = local_hash != remote_hash
+
+        # Count how many tags the local branch is behind
+        tag_behind_count = 0
+        if latest_local_tag != latest_remote_tag:
+            tags = subprocess.check_output(
+                ["git", "tag", "--merged", "origin/main"], text=True
+            ).splitlines()
+
+            found_local = False
+            for tag in tags:
+                if tag == latest_local_tag:
+                    found_local = True
+                elif found_local:
+                    tag_behind_count += 1
+                    if tag == latest_remote_tag:
+                        break
+
+        return {
+            "updates_available": updates_available,
+            "tag_behind_count": tag_behind_count,  # Tags behind
+            "latest_remote_tag": latest_remote_tag,
+            "latest_local_tag": latest_local_tag,
+        }
+    except subprocess.CalledProcessError as e:
+        print(f"Error checking Git updates: {e}")
+        return {
+            "updates_available": False,
+            "tag_behind_count": 0,
+            "latest_remote_tag": None,
+            "latest_local_tag": None,
+        }
+
+
+
 def list_serial_ports():
     """Return a list of available serial ports."""
     ports = serial.tools.list_ports.comports()
@@ -1140,7 +1193,59 @@ def flash_firmware():
                 os.remove(os.path.join(build_dir, file))
             os.rmdir(build_dir)
 
+@app.route('/check_software_update', methods=['GET'])
+def check_updates():
+    update_info = check_git_updates()
+    return jsonify(update_info)
+
+@app.route('/update_software', methods=['POST'])
+def update_software():
+    error_log = []
+
+    def run_command(command, error_message):
+        try:
+            subprocess.run(command, check=True)
+        except subprocess.CalledProcessError as e:
+            print(f"{error_message}: {e}")
+            error_log.append(error_message)
+
+    # Fetch the latest version tag from remote
+    try:
+        subprocess.run(["git", "fetch", "--tags"], check=True)
+        latest_remote_tag = subprocess.check_output(
+            ["git", "describe", "--tags", "--abbrev=0", "origin/main"]
+        ).strip().decode()
+    except subprocess.CalledProcessError as e:
+        error_log.append(f"Failed to fetch tags or get latest remote tag: {e}")
+        return jsonify({
+            "success": False,
+            "error": "Failed to fetch tags or determine the latest version.",
+            "details": error_log
+        }), 500
+
+    # Checkout the latest tag
+    run_command(["git", "checkout", latest_remote_tag, '--force'], f"Failed to checkout version {latest_remote_tag}")
+
+    # Restart Docker containers
+    run_command(["docker", "compose", "up", "-d"], "Failed to restart Docker containers")
+
+    # Check if the update was successful
+    update_status = check_git_updates()
+
+    if (
+        update_status["updates_available"] is False
+        and update_status["latest_local_tag"] == update_status["latest_remote_tag"]
+    ):
+        # Update was successful
+        return jsonify({"success": True})
+    else:
+        # Update failed; include the errors in the response
+        return jsonify({
+            "success": False,
+            "error": "Update incomplete",
+            "details": error_log
+        }), 500
 if __name__ == '__main__':
     # Auto-connect to serial
     connect_to_serial()
-    app.run(debug=True, host='0.0.0.0', port=8080)
+    app.run(debug=False, host='0.0.0.0', port=8080)

+ 27 - 6
static/css/style.css

@@ -195,7 +195,7 @@ input, select {
     padding: 5px;
     width: 100%;
     cursor: pointer;
-    font-size: 1.2rem;
+    font-size: 1rem;
     transition: background-color 0.3s;
 }
 
@@ -212,6 +212,7 @@ input, select {
     background: var(--theme-primary);
     color: var(--text-secondary);
     border-radius: 5px;
+    font-weight: bold;
 }
 
 .nav-items {
@@ -268,7 +269,7 @@ button {
     border-radius: 5px;
     cursor: pointer;
     font-size: 1rem;
-    transition: background 0.3s ease,color 0.3s ease;
+    transition: var(--transition-medium) all;
     display: flex;
     justify-content: center;
     align-items: center;
@@ -372,7 +373,7 @@ section.sticky {
     border-top: 1px solid var(--border-primary);
     box-shadow: var(--shadow-primary);
     transform: translateY(0);
-    transition: 250ms transform, 250ms height;
+    transition: var(--transition-medium) transform, var(--transition-medium) height;
     visibility: visible;
     max-height: 70vh;
     width: 100%;
@@ -530,7 +531,7 @@ section .header .add-button {
     padding: 10px;
     border-bottom: 1px solid var(--border-primary);
     cursor: pointer;
-    transition: background-color 0.3s ease;
+    transition: background-color var(--transition-medium);
 }
 
 .file-list li:hover {
@@ -628,6 +629,7 @@ section .header .add-button {
     gap: 10px;
     flex-wrap: wrap;
     width: 100%;
+    justify-content: space-between;
 }
 
 .action-buttons .scrollable-selection {
@@ -693,7 +695,7 @@ button#debug_button {
     font-size: 1.5rem;
     margin-left: 40px;
     flex: 0 0 auto;
-    transition: 250ms all;
+    transition: var(--transition-medium) all;
 }
 
 button#debug_button:hover,
@@ -723,6 +725,15 @@ button#debug_button.active {
     display: flex;
 }
 
+#open-settings-button {
+    aspect-ratio: auto;
+}
+
+#open-settings-button span {
+    order: -1;
+    margin-right: 5px;
+
+}
 
 /* Preview Canvas */
 #patternPreviewCanvas {
@@ -1076,7 +1087,7 @@ input[type="number"]:focus {
     align-items: center;
     backdrop-filter: blur(2px);
     opacity: 0;
-    transition: opacity 250ms ease-in-out;
+    transition: opacity var(--transition-medium);
 }
 .notification.show {
     opacity: 1; /* Fully visible */
@@ -1215,4 +1226,14 @@ input[type="number"]:focus {
     body.playing #currently-playing-container.open .header {
         display: none;
     }
+
+    #open-settings-button span {
+        opacity: 0;
+        transition: var(--transition-medium) opacity;
+    }
+
+    #open-settings-button:hover span {
+        opacity: 1;
+    }
+
 }

+ 56 - 10
static/js/main.js

@@ -671,13 +671,9 @@ async function fetchFirmwareInfo(motorType = null) {
             : { method: "GET" };
 
         const response = await fetch("/get_firmware_info", options);
-        if (!response.ok) {
-            throw new Error(`Server responded with status ${response.status}`);
-        }
-
         const data = await response.json();
         if (data.success) {
-            const { installedVersion, installedType, inoVersion, inoType, updateAvailable } = data;
+            const { installedVersion, installedType, inoVersion, updateAvailable } = data;
 
             // Handle unknown motor type
             if (!installedType || installedType === "Unknown") {
@@ -699,7 +695,7 @@ async function fetchFirmwareInfo(motorType = null) {
                 currentVersionElement.textContent = `Current version: ${installedVersion || "Unknown"}`;
 
                 if (updateAvailable) {
-                    newVersionElement.textContent = `New version: ${inoVersion}`;
+                    newVersionElement.textContent = `Latest version: ${inoVersion}`;
                     updateButtonElement.style.display = "block";
                     checkButton.style.display = "none";
                 } else {
@@ -785,6 +781,59 @@ async function updateFirmware() {
     }
 }
 
+async function checkForUpdates() {
+    try {
+        const response = await fetch('/check_software_update');
+        const data = await response.json();
+
+        data.updates_available = true;
+
+        // Handle updates available logic
+        if (data.updates_available) {
+            const updateButton = document.getElementById('update-software-btn');
+            updateButton.classList.remove('hidden'); // Show the button
+        }
+
+        // Update current and latest version in the UI
+        const currentVersionElem = document.getElementById('current_git_version');
+        const latestVersionElem = document.getElementById('latest_git_version');
+
+        currentVersionElem.textContent = `Current Version: ${data.latest_local_tag || 'Unknown'}`;
+        latestVersionElem.textContent = (data.latest_remote_tag !== data.latest_local_tag)
+            ? `Latest Version: ${data.latest_remote_tag}`
+            : 'You are up to date!';
+
+    } catch (error) {
+        console.error('Error checking for updates:', error);
+    }
+}
+
+async function updateSoftware() {
+    const updateButton = document.getElementById('update-software-btn');
+
+    try {
+        // Disable the button and update the text
+        updateButton.disabled = true;
+        updateButton.querySelector('span').textContent = 'Updating...';
+
+        const response = await fetch('/update_software', { method: 'POST' });
+        const data = await response.json();
+
+        if (data.success) {
+            logMessage('Software updated successfully!', LOG_TYPE.SUCCESS);
+            window.location.reload(); // Reload the page after update
+        } else {
+            logMessage('Failed to update software: ' + data.error, LOG_TYPE.ERROR);
+        }
+    } catch (error) {
+        console.error('Error updating software:', error);
+        logMessage('Failed to update software', LOG_TYPE.ERROR);
+    } finally {
+        // Re-enable the button and reset the text
+        updateButton.disabled = false;
+        updateButton.textContent = 'Update Software'; // Adjust to the original text
+    }
+}
 
 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 //  PART A: Loading / listing playlists from the server
@@ -954,10 +1003,6 @@ async function loadPlaylist(playlistName) {
         logMessage(`Loading playlist: ${playlistName}`);
         const response = await fetch(`/get_playlist?name=${encodeURIComponent(playlistName)}`);
 
-        if (!response.ok) {
-            throw new Error(`HTTP error! Status: ${response.status}`);
-        }
-
         const data = await response.json();
 
         if (!data.name) {
@@ -1698,6 +1743,7 @@ document.addEventListener('DOMContentLoaded', () => {
     attachSettingsSaveListeners(); // Attach event listeners to save changes
     attachFullScreenListeners();
     fetchFirmwareInfo();
+    checkForUpdates();
 
     // Periodically check for currently playing status
     setInterval(updateCurrentlyPlaying, 3000);

+ 18 - 5
templates/index.html

@@ -235,6 +235,7 @@
                 <h2>Device Controls</h2>
                 <button id="open-settings-button" class="no-bg" onclick="toggleSettings()">
                     <i class="fa-solid fa-gears"></i>
+                    <span class="small">Settings</span>
                 </button>
             </div>
             <div class="action-buttons square">
@@ -323,9 +324,9 @@
                 </div>
             </section>
 
-            <section class="version">
+            <section class="firmware version">
                 <div class="header">
-                    <h2>Firmware Update</h2>
+                    <h2>Firmware Version</h2>
                 </div>
                 <div id="firmware_info">
                     <div id="motor_type"></div>
@@ -349,6 +350,21 @@
                     </button>
                 </div>
             </section>
+            <section class="software version">
+                <div class="header">
+                    <h2>Software Version</h2>
+                </div>
+                <div id="git_version_info">
+                    <div id="current_git_version">Current Version: Unknown</div>
+                    <div id="latest_git_version">Latest Version: Unknown</div>
+                </div>
+                <div class="button-group">
+                    <button id="update-software-btn" class="hidden" onclick="updateSoftware()">
+                        <i class="fa-solid fa-wrench"></i>
+                        <span>Update Software</span>
+                    </button>
+                </div>
+            </section>
             <section class="debug main">
                 <div id="github">
                 <span>Help us improve! <a href="https://github.com/tuanchris/dune-weaver/pulls" target="_blank">Submit a Pull Request</a> or <a
@@ -359,9 +375,6 @@
                              alt="GitHub Issues">
                     </a>
                 </div>
-                <button id="debug_button" onclick="toggleDebugLog()">
-                    <i class="fa-solid fa-bug"></i>
-                </button>
             </section>
         </section>
     </main>