diff --git a/data/src/css/style.scss b/data/src/css/style.scss
index 6c9ed74..194a86e 100644
--- a/data/src/css/style.scss
+++ b/data/src/css/style.scss
@@ -126,4 +126,11 @@ nav {
 
 #toggleTimerArea {
     cursor: pointer;
+}
+
+#system_info {
+    padding: 0;
+    li {
+        list-style: none;
+    }
 }
\ No newline at end of file
diff --git a/data/src/index.html b/data/src/index.html
index 2865177..ec16f39 100644
--- a/data/src/index.html
+++ b/data/src/index.html
@@ -107,7 +107,6 @@
             </div>
             <footer>
               <button type="submit" class="btn btn-primary">Show Text</button>
-              <button type="button" class="btn btn-secondary" id="restartBtn">Restart</button>
             </footer>
           </form>
           <hr>
@@ -122,7 +121,19 @@
             <button type="button" class="btn btn-secondary" id="turnOffLedsBtn">Turn off</button>
             <button type="submit" class="btn btn-primary">Set color</button>
           </form>
+          <hr>
+          <h2>System info</h2>
+          <ul class="small" id="system_info">
+            <li>Version: <span id="gitRev"></span></li>
+            <li>Build time: <span id="lastBuildTime"></span></li>
+            <li>IP: <span id="ipAddress"></span></li>
+            <li>Hostname: <span id="hostname"></span></li>
+          </ul>
+          <button type="button" class="btn btn-danger" id="restartBtn">Restart</button>
+          <button type="button" class="btn btn-warning" id="forceFullRefresh">Force full refresh</button>
+
         </div>
+        
       </div>
       <div class="col-sm-5">
         <div id="output" class="p-3 border bg-light">Loading, please wait...</div>
@@ -191,6 +202,14 @@
                 <div class="form-text">Short amounts might shorten lifespan.</div>
               </div>
             </div>
+            <div class="row">
+              <div class="col-sm-12">
+                <div class="form-check form-switch">
+                  <input class="form-check-input" type="checkbox" id="ledTestOnPower" name="ledTestOnPower" value="1">
+                  <label class="form-check-label" for="ledTestOnPower">LED power-on test</label>
+                </div>
+              </div>
+            </div>
             <div class="row">
               <div class="col-sm-12">
                 <div class="form-check form-switch">
@@ -215,6 +234,21 @@
                 </div>
               </div>
             </div>
+            <div class="row">
+              <div class="col-sm-12">
+                <div class="form-check form-switch">
+                  <input class="form-check-input" type="checkbox" id="otaEnabled" name="otaEnabled" value="1">
+                  <label class="form-check-label" for="otaEnabled">OTA updates (restart required)</label>
+                </div>
+              </div>
+            </div>
+            <div class="row">
+              <div class="col-sm-12">
+                <div class="form-check form-switch">
+                  <input class="form-check-input" type="checkbox" id="mdnsEnabled" name="mdnsEnabled" value="1">
+                  <label class="form-check-label" for="mdnsEnabled">mDNS (restart required)</label>
+                </div>
+              </div>
             <div class="row">
               <label class="col-sm-6 col-form-label" for="ledBrightness">LED brightness</label>
               <div class="col-sm-6">
@@ -232,9 +266,10 @@
               {{#each screens }}
               <div class="row">
                 <div class="col-sm-12">
-                <div class="form-check form-switch">
-                  <input class="form-check-input" type="checkbox" id="screen{{id}}" name="screen[{{id}}]" value="1" {{#if enabled}}checked{{/if}}>
-                  <label class="form-check-label" for="screen{{id}}">{{name}}</label>
+                  <div class="form-check form-switch">
+                    <input class="form-check-input" type="checkbox" id="screen{{id}}" name="screen[{{id}}]" value="1" {{#if enabled}}checked{{/if}}>
+                    <label class="form-check-label" for="screen{{id}}">{{name}}</label>
+                  </div>
                 </div>
               </div>
               {{/each}}
@@ -250,10 +285,7 @@
     </div>
   </div>
   <footer>
-    <small>
-      <span id="gitRev"></span>
-      <span id="lastBuildTime"></span>
-    </small>
+   
   </footer>
   <script src="/js/script.js"></script>
 </body>
diff --git a/data/src/js/script.ts b/data/src/js/script.ts
index 9523f42..ad48c44 100644
--- a/data/src/js/script.ts
+++ b/data/src/js/script.ts
@@ -43,25 +43,38 @@ let processStatusData = (jsonData) => {
 
 
 if (!!window.EventSource) {
-    var source = new EventSource('/events');
+    const connectEventSource = () => {
+        let source = new EventSource('/events');
 
-    source.addEventListener('open', function (e) {
-        console.log("Status EventSource Connected");
-        if (e.data) {
+        source.addEventListener('open', (e)  => {
+            console.log("Status EventSource Connected");
+            if (e.data) {
+                processStatusData(JSON.parse(e.data));
+            }
+        }, false);
+
+        source.addEventListener('error', (e) => {
+            if (e.target.readyState != EventSource.OPEN) {
+                console.log("Status EventSource Disconnected");
+                setTimeout(connectEventSource, 10000);
+            }
+            source.close();
+        }, false);
+
+        source.addEventListener('status', (e) => {
             processStatusData(JSON.parse(e.data));
-        }
-    }, false);
+        }, false);
 
-    source.addEventListener('error', function (e) {
-        if (e.target.readyState != EventSource.OPEN) {
-            console.log("Status EventSource Disconnected");
-        }
-        source.close();
-    }, false);
+        source.addEventListener('message', (e) => {
+            if (e.data == "closing") {
+                console.log("Closing EventSource, trying to reconnect in 10 seconds")
+                source.close();
+                setTimeout(connectEventSource, 10000);
+            }
+        }, false);
+    };
 
-    source.addEventListener('status', function (e) {
-        processStatusData(JSON.parse(e.data));
-    }, false);
+    connectEventSource();
 }
 
 
@@ -106,8 +119,14 @@ fetch('/api/settings', {
         if (jsonData.mcapBigChar)
             document.getElementById('mcapBigChar').checked = true;
 
-        if (jsonData.useBitcoinNode)
-            document.getElementById('useBitcoinNode').checked = true;
+        if (jsonData.mdnsEnabled)
+            document.getElementById('mdnsEnabled').checked = true;
+
+        if (jsonData.otaEnabled)
+            document.getElementById('otaEnabled').checked = true;
+
+        if (jsonData.ledTestOnPower)
+            document.getElementById('ledTestOnPower').checked = true;
 
         // let nodeFields = ["rpcHost", "rpcPort", "rpcUser", "tzOffset"];
 
@@ -123,10 +142,16 @@ fetch('/api/settings', {
         document.getElementById('minSecPriceUpd').value = jsonData.minSecPriceUpd;
 
         if (jsonData.gitRev)
-            document.getElementById('gitRev').innerHTML = "Version: " + jsonData.gitRev;
+            document.getElementById('gitRev').innerHTML = jsonData.gitRev;
+
+        if (jsonData.hostname)
+            document.getElementById('hostname').innerHTML = jsonData.hostname;
+
+        if (jsonData.ip)
+            document.getElementById('ipAddress').innerHTML = jsonData.ip;
 
         if (jsonData.lastBuildTime)
-            document.getElementById('lastBuildTime').innerHTML = " / " + new Date((jsonData.lastBuildTime * 1000)).toLocaleString();
+            document.getElementById('lastBuildTime').innerHTML = new Date((jsonData.lastBuildTime * 1000)).toLocaleString();
 
         var source = document.getElementById("screens-template").innerHTML;
         var template = Handlebars.compile(source);
@@ -160,6 +185,11 @@ document.getElementById('restartBtn').onclick = (event) => {
     return false;
 }
 
+document.getElementById('forceFullRefresh').onclick = (event) => {
+    fetch('/api/full_refresh');
+    return false;
+}
+
 
 var ledsForm = document.querySelector('#ledsForm');
 ledsForm.onsubmit = (event) => {
diff --git a/src/lib/config.cpp b/src/lib/config.cpp
index 21f808d..68e3c75 100644
--- a/src/lib/config.cpp
+++ b/src/lib/config.cpp
@@ -42,6 +42,8 @@ void setup()
 
 void tryImprovSetup()
 {
+    WiFi.onEvent(WiFiEvent);
+
     if (!preferences.getBool("wifiConfigured", false))
     {
         setFgColor(GxEPD_BLACK);
@@ -434,4 +436,66 @@ void improv_set_error(improv::Error error)
     data[10] = checksum;
 
     Serial.write(data.data(), data.size());
-}
\ No newline at end of file
+}
+
+void WiFiEvent(WiFiEvent_t event)
+{
+    Serial.printf("[WiFi-event] event: %d\n", event);
+
+    switch (event) {
+        case ARDUINO_EVENT_WIFI_READY: 
+            Serial.println("WiFi interface ready");
+            break;
+        case ARDUINO_EVENT_WIFI_SCAN_DONE:
+            Serial.println("Completed scan for access points");
+            break;
+        case ARDUINO_EVENT_WIFI_STA_START:
+            Serial.println("WiFi client started");
+            break;
+        case ARDUINO_EVENT_WIFI_STA_STOP:
+            Serial.println("WiFi clients stopped");
+            break;
+        case ARDUINO_EVENT_WIFI_STA_CONNECTED:
+            Serial.println("Connected to access point");
+            break;
+        case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
+            Serial.println("Disconnected from WiFi access point");
+            queueLedEffect(LED_EFFECT_WIFI_CONNECT_ERROR);
+            break;
+        case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE:
+            Serial.println("Authentication mode of access point has changed");
+            break;
+        case ARDUINO_EVENT_WIFI_STA_GOT_IP:
+            Serial.print("Obtained IP address: ");
+            Serial.println(WiFi.localIP());
+            break;
+        case ARDUINO_EVENT_WIFI_STA_LOST_IP:
+            Serial.println("Lost IP address and IP address is reset to 0");
+            queueLedEffect(LED_EFFECT_WIFI_CONNECT_ERROR);
+            break;
+        case ARDUINO_EVENT_WIFI_AP_START:
+            Serial.println("WiFi access point started");
+            break;
+        case ARDUINO_EVENT_WIFI_AP_STOP:
+            Serial.println("WiFi access point  stopped");
+            break;
+        case ARDUINO_EVENT_WIFI_AP_STACONNECTED:
+            Serial.println("Client connected");
+            break;
+        case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED:
+            Serial.println("Client disconnected");
+            break;
+        case ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED:
+            Serial.println("Assigned IP address to client");
+            break;
+        case ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED:
+            Serial.println("Received probe request");
+            break;
+        case ARDUINO_EVENT_WIFI_AP_GOT_IP6:
+            Serial.println("AP IPv6 is preferred");
+            break;
+        case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
+            Serial.println("STA IPv6 is preferred");
+            break;
+        default: break;
+    }}
\ No newline at end of file
diff --git a/src/lib/config.hpp b/src/lib/config.hpp
index 6537bfc..7f6d5cb 100644
--- a/src/lib/config.hpp
+++ b/src/lib/config.hpp
@@ -56,3 +56,5 @@ void onImprovErrorCallback(improv::Error err);
 void improv_set_state(improv::State state);
 void improv_send_response(std::vector<uint8_t> &response);
 void improv_set_error(improv::Error error);
+
+void WiFiEvent(WiFiEvent_t event);
\ No newline at end of file
diff --git a/src/lib/epd.cpp b/src/lib/epd.cpp
index 248a035..560bfef 100644
--- a/src/lib/epd.cpp
+++ b/src/lib/epd.cpp
@@ -68,6 +68,14 @@ int bgColor = GxEPD_BLACK;
 
 uint8_t qrcode[800];
 
+void forceFullRefresh()
+{
+    for (uint i = 0; i < NUM_SCREENS; i++)
+    {
+        lastFullRefresh[i] = NULL;
+    }
+}
+
 void setupDisplays()
 {
     for (uint i = 0; i < NUM_SCREENS; i++)
@@ -77,7 +85,7 @@ void setupDisplays()
 
     updateQueue = xQueueCreate(UPDATE_QUEUE_SIZE, sizeof(UpdateDisplayTaskItem));
 
-    xTaskCreate(prepareDisplayUpdateTask, "PrepareUpd", 4096, NULL, tskIDLE_PRIORITY, NULL);
+    xTaskCreate(prepareDisplayUpdateTask, "PrepareUpd", 4096, NULL, 11, NULL);
 
     for (uint i = 0; i < NUM_SCREENS; i++)
     {
@@ -86,8 +94,8 @@ void setupDisplays()
 
         int *taskParam = new int;
         *taskParam = i;
-
-        xTaskCreate(updateDisplay, ("EpdUpd" + String(i)).c_str(), 2048, taskParam, tskIDLE_PRIORITY, &tasks[i]); // create task
+        
+        xTaskCreate(updateDisplay, ("EpdUpd" + String(i)).c_str(), 2048, taskParam, 11, &tasks[i]); // create task
     }
 
     epdContent = {"B",
@@ -103,12 +111,16 @@ void setupDisplays()
 
 void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent)
 {
-    epdContent = newEpdContent;
+    setEpdContent(newEpdContent, false);
+}
 
+void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent, bool forceUpdate)
+{
     for (uint i = 0; i < NUM_SCREENS; i++)
     {
-        if (epdContent[i].compareTo(currentEpdContent[i]) != 0)
+        if (newEpdContent[i].compareTo(currentEpdContent[i]) != 0 || forceUpdate)
         {
+            epdContent[i] = newEpdContent[i];
             UpdateDisplayTaskItem dispUpdate = {i};
             xQueueSend(updateQueue, &dispUpdate, portMAX_DELAY);
             // if (xSemaphoreTake(epdUpdateSemaphore[i], pdMS_TO_TICKS(5000)) == pdTRUE)
@@ -132,21 +144,11 @@ void prepareDisplayUpdateTask(void *pvParameters)
         if (xQueueReceive(updateQueue, &receivedItem, portMAX_DELAY))
         {
             uint epdIndex = receivedItem.dispNum;
-            if (epdContent[epdIndex].compareTo(currentEpdContent[epdIndex]) != 0)
-            {
-
-                displays[epdIndex].init(0, false); // Little longer reset duration because of MCP
-            }
+            
+            displays[epdIndex].init(0, false); // Little longer reset duration because of MCP
 
             bool updatePartial = true;
 
-            // // Full Refresh every half hour
-            // if (!lastFullRefresh[epdIndex] || (millis() - lastFullRefresh[epdIndex]) > (preferences.getUInt("fullRefreshMin", 30) * 60 * 1000))
-            // {
-            //     updatePartial = false;
-            //     lastFullRefresh[epdIndex] = millis();
-            // }
-
             if (strstr(epdContent[epdIndex].c_str(), "/") != NULL)
             {
                 String top = epdContent[epdIndex].substring(0, epdContent[epdIndex].indexOf("/"));
@@ -174,10 +176,7 @@ void prepareDisplayUpdateTask(void *pvParameters)
                 }
             }
 
-            if (xSemaphoreTake(epdUpdateSemaphore[epdIndex], pdMS_TO_TICKS(5000)) == pdTRUE)
-            {
-                xTaskNotifyGive(tasks[epdIndex]);
-            }
+            xTaskNotifyGive(tasks[epdIndex]);
         }
     }
 }
@@ -192,10 +191,8 @@ extern "C" void updateDisplay(void *pvParameters) noexcept
         // Wait for the task notification
         ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
 
-        // if (epdContent[epdIndex].compareTo(currentEpdContent[epdIndex]) != 0)
+        // if (xSemaphoreTake(epdUpdateSemaphore[epdIndex], pdMS_TO_TICKS(5000)) == pdTRUE)
         // {
-
-        //     displays[epdIndex].init(0, false); // Little longer reset duration because of MCP
             uint count = 0;
             while (EPD_BUSY[epdIndex].digitalRead() == HIGH || count < 10)
             {
@@ -213,75 +210,29 @@ extern "C" void updateDisplay(void *pvParameters) noexcept
             if (!lastFullRefresh[epdIndex] || (millis() - lastFullRefresh[epdIndex]) > (preferences.getUInt("fullRefreshMin", 30) * 60 * 1000))
             {
                 updatePartial = false;
-                lastFullRefresh[epdIndex] = millis();
             }
 
-//             // if (updatePartial)
-//             // {
-//             //     displays[epdIndex].setPartialWindow(0, 0, displays[i].width(), display[i].height());
-//             // }
-
-//             if (strstr(epdContent[epdIndex].c_str(), "/") != NULL)
-//             {
-//                 String top = epdContent[epdIndex].substring(0, epdContent[epdIndex].indexOf("/"));
-//                 String bottom = epdContent[epdIndex].substring(epdContent[epdIndex].indexOf("/") + 1);
-// #ifdef PAGED_WRITE
-//                 splitTextPaged(epdIndex, top, bottom, updatePartial);
-// #else
-//                 splitText(epdIndex, top, bottom, updatePartial);
-// #endif
-//             }
-//             else if (epdContent[epdIndex].startsWith(F("qr")))
-//             {
-//                 renderQr(epdIndex, epdContent[epdIndex], updatePartial);
-//             }
-//             else if (epdContent[epdIndex].length() > 5)
-//             {
-//                 renderText(epdIndex, epdContent[epdIndex], updatePartial);
-//             }
-//             else
-//             {
-
-// #ifdef PAGED_WRITE
-//                 showDigitPaged(epdIndex, epdContent[epdIndex].c_str()[0], updatePartial, &FONT_BIG);
-// #else
-//                 if (epdContent[epdIndex].length() > 1)
-//                 {
-//                     showChars(epdIndex, epdContent[epdIndex], updatePartial, &Antonio_SemiBold30pt7b);
-//                 }
-//                 else
-//                 {
-//                     showDigit(epdIndex, epdContent[epdIndex].c_str()[0], updatePartial, &FONT_BIG);
-//                 }
-// #endif
-//             }
-
-// #ifdef PAGED_WRITE
-//             currentEpdContent[epdIndex] = epdContent[epdIndex];
-// #else
             char tries = 0;
             while (tries < 3)
             {
                 if (displays[epdIndex].displayWithReturn(updatePartial))
                 {
                     displays[epdIndex].hibernate();
+                    currentEpdContent[epdIndex] = epdContent[epdIndex];
+                    if (!updatePartial) 
+                        lastFullRefresh[epdIndex] = millis();
                     break;
                 }
 
                 vTaskDelay(pdMS_TO_TICKS(100));
                 tries++;
             }
-            currentEpdContent[epdIndex] = epdContent[epdIndex];
 
-// #endif
+        //     xSemaphoreGive(epdUpdateSemaphore[epdIndex]);
         // }
-        xSemaphoreGive(epdUpdateSemaphore[epdIndex]);
     }
 }
 
-void updateDisplayAlt(int epdIndex)
-{
-}
 
 void splitText(const uint dispNum, const String &top, const String &bottom, bool partial)
 {
@@ -319,55 +270,6 @@ void splitText(const uint dispNum, const String &top, const String &bottom, bool
     displays[dispNum].print(bottom);
 }
 
-// void splitTextPaged(const uint dispNum, String top, String bottom, bool partial)
-// {
-//     displays[dispNum].setRotation(2);
-//     displays[dispNum].setFont(&FONT_SMALL);
-//     displays[dispNum].setTextColor(getFgColor());
-
-//     // Top text
-//     int16_t ttbx, ttby;
-//     uint16_t ttbw, ttbh;
-//     displays[dispNum].getTextBounds(top, 0, 0, &ttbx, &ttby, &ttbw, &ttbh);
-//     uint16_t tx = ((displays[dispNum].width() - ttbw) / 2) - ttbx;
-//     uint16_t ty = ((displays[dispNum].height() - ttbh) / 2) - ttby - ttbh / 2 - 12;
-
-//     // Bottom text
-//     int16_t tbbx, tbby;
-//     uint16_t tbbw, tbbh;
-//     displays[dispNum].getTextBounds(bottom, 0, 0, &tbbx, &tbby, &tbbw, &tbbh);
-//     uint16_t bx = ((displays[dispNum].width() - tbbw) / 2) - tbbx;
-//     uint16_t by = ((displays[dispNum].height() - tbbh) / 2) - tbby + tbbh / 2 + 12;
-
-//     // Make separator as wide as the shortest text.
-//     uint16_t lineWidth, lineX;
-//     if (tbbw < ttbh)
-//         lineWidth = tbbw;
-//     else
-//         lineWidth = ttbw;
-//     lineX = round((displays[dispNum].width() - lineWidth) / 2);
-
-//     if (partial)
-//     {
-//         displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(), displays[dispNum].height());
-//     }
-//     else
-//     {
-//         displays[dispNum].setFullWindow();
-//     }
-//     displays[dispNum].firstPage();
-
-//     do
-//     {
-//         displays[dispNum].fillScreen(getBgColor());
-//         displays[dispNum].setCursor(tx, ty);
-//         displays[dispNum].print(top);
-//         displays[dispNum].fillRoundRect(lineX, displays[dispNum].height() / 2 - 3, lineWidth, 6, 3, getFgColor());
-//         displays[dispNum].setCursor(bx, by);
-//         displays[dispNum].print(bottom);
-//     } while (displays[dispNum].nextPage());
-// }
-
 void showDigit(const uint dispNum, char chr, bool partial, const GFXfont *font)
 {
     String str(chr);
@@ -401,36 +303,6 @@ void showChars(const uint dispNum, const String &chars, bool partial, const GFXf
     displays[dispNum].print(chars);
 }
 
-// void showDigitPaged(const uint dispNum, char chr, bool partial, const GFXfont *font)
-// {
-//     String str(chr);
-//     displays[dispNum].setRotation(2);
-//     displays[dispNum].setFont(font);
-//     displays[dispNum].setTextColor(getFgColor());
-//     int16_t tbx, tby;
-//     uint16_t tbw, tbh;
-//     displays[dispNum].getTextBounds(str, 0, 0, &tbx, &tby, &tbw, &tbh);
-//     // center the bounding box by transposition of the origin:
-//     uint16_t x = ((displays[dispNum].width() - tbw) / 2) - tbx;
-//     uint16_t y = ((displays[dispNum].height() - tbh) / 2) - tby;
-//     if (partial)
-//     {
-//         displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(), displays[dispNum].height());
-//     }
-//     else
-//     {
-//         displays[dispNum].setFullWindow();
-//     }
-//     displays[dispNum].firstPage();
-
-//     do
-//     {
-//         displays[dispNum].fillScreen(getBgColor());
-//         displays[dispNum].setCursor(x, y);
-//         displays[dispNum].print(str);
-//     } while (displays[dispNum].nextPage());
-// }
-
 int getBgColor()
 {
     return bgColor;
@@ -526,9 +398,14 @@ void waitUntilNoneBusy()
 {
     for (int i = 0; i < NUM_SCREENS; i++)
     {
+        uint count = 0;
         while (EPD_BUSY[i].digitalRead())
         {
+            count++;
             vTaskDelay(10);
+            if (count > 200) {
+                displays[i].init(0, false);
+            }
         }
     }
 }
\ No newline at end of file
diff --git a/src/lib/epd.hpp b/src/lib/epd.hpp
index b4601fc..5f34f06 100644
--- a/src/lib/epd.hpp
+++ b/src/lib/epd.hpp
@@ -20,17 +20,14 @@ typedef struct
     char dispNum;
 } UpdateDisplayTaskItem;
 
+void forceFullRefresh();
 void setupDisplays();
-// void taskEpd(void *pvParameters);
 
 void splitText(const uint dispNum, const String&  top, const String&  bottom, bool partial);
-//void splitTextPaged(const uint dispNum, String top, String bottom, bool partial);
 
 void showDigit(const uint dispNum, char chr, bool partial, const GFXfont *font);
 void showChars(const uint dispNum, const String& chars, bool partial, const GFXfont *font);
 
-//void showDigitPaged(const uint dispNum, char chr, bool partial, const GFXfont *font);
-
 extern "C" void updateDisplay(void *pvParameters) noexcept;
 void updateDisplayAlt(int epdIndex);
 void prepareDisplayUpdateTask(void *pvParameters);
@@ -43,6 +40,7 @@ void setFgColor(int color);
 void renderText(const uint dispNum, const String& text, bool partial);
 void renderQr(const uint dispNum, const String& text, bool partial);
 
+void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent, bool forceUpdate);
 void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent);
 std::array<String, NUM_SCREENS> getCurrentEpdContent();
 void waitUntilNoneBusy();
\ No newline at end of file
diff --git a/src/lib/led_handler.cpp b/src/lib/led_handler.cpp
index eaa3ae8..fa49108 100644
--- a/src/lib/led_handler.cpp
+++ b/src/lib/led_handler.cpp
@@ -24,14 +24,12 @@ void ledTask(void *parameter)
                 switch (ledTaskParams)
                 {
                 case LED_POWER_TEST:
-                    pixels.setPixelColor(0, pixels.Color(255, 0, 0));
-                    pixels.setPixelColor(1, pixels.Color(0, 255, 0));
-                    pixels.setPixelColor(2, pixels.Color(0, 0, 255));
-                    pixels.setPixelColor(3, pixels.Color(255, 255, 255));
-                    pixels.show();
-                    vTaskDelay(pdMS_TO_TICKS(1000));
+                    ledRainbow(20);
+                    pixels.clear();
                     break;
                 case LED_EFFECT_WIFI_CONNECT_ERROR:
+                    blinkDelayTwoColor(100, 1, pixels.Color(8, 161, 236), pixels.Color(255, 0, 0));
+                    break;
                 case LED_FLASH_ERROR:
                     blinkDelayColor(250, 3, 255, 0, 0);
                     break;
@@ -39,6 +37,16 @@ void ledTask(void *parameter)
                 case LED_FLASH_SUCCESS:
                     blinkDelayColor(150, 3, 0, 255, 0);
                     break;
+                case LED_PROGRESS_100:
+                    pixels.setPixelColor(0, pixels.Color(0, 255, 0));
+                case LED_PROGRESS_75:
+                    pixels.setPixelColor(1, pixels.Color(0, 255, 0));
+                case LED_PROGRESS_50:
+                    pixels.setPixelColor(2, pixels.Color(0, 255, 0));
+                case LED_PROGRESS_25:
+                    pixels.setPixelColor(3, pixels.Color(0, 255, 0));
+                    pixels.show();
+                    break;
                 case LED_FLASH_UPDATE:
                     break;
                 case LED_FLASH_BLOCK_NOTIFY:
@@ -122,14 +130,12 @@ void ledTask(void *parameter)
 
                 // revert to previous state unless power test
 
-                if (ledTaskParams != LED_POWER_TEST) {
-                    for (int i = 0; i < NEOPIXEL_COUNT; i++)
-                    {
-                        pixels.setPixelColor(i, oldLights[i]);
-                    }
-
-                    pixels.show();
+                for (int i = 0; i < NEOPIXEL_COUNT; i++)
+                {
+                    pixels.setPixelColor(i, oldLights[i]);
                 }
+
+                pixels.show();
             }
         }
     }
@@ -240,7 +246,13 @@ void setLights(int r, int g, int b)
 void setLights(uint32_t color)
 {
     preferences.putUInt("ledColor", color);
-    preferences.putBool("ledStatus", true);
+
+    bool ledStatus = true;
+    if (color == pixels.Color(0, 0, 0))
+    {
+        ledStatus = false;
+    }
+    preferences.putBool("ledStatus", false);
 
     for (int i = 0; i < NEOPIXEL_COUNT; i++)
     {
@@ -264,4 +276,74 @@ bool queueLedEffect(uint effect)
 
     uint flashType = effect;
     xQueueSend(ledTaskQueue, &flashType, portMAX_DELAY);
+}
+
+void ledRainbow(int wait)
+{
+    // Hue of first pixel runs 5 complete loops through the color wheel.
+    // Color wheel has a range of 65536 but it's OK if we roll over, so
+    // just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
+    // means we'll make 5*65536/256 = 1280 passes through this loop:
+    for (long firstPixelHue = 0; firstPixelHue < 5 * 65536; firstPixelHue += 256)
+    {
+        // strip.rainbow() can take a single argument (first pixel hue) or
+        // optionally a few extras: number of rainbow repetitions (default 1),
+        // saturation and value (brightness) (both 0-255, similar to the
+        // ColorHSV() function, default 255), and a true/false flag for whether
+        // to apply gamma correction to provide 'truer' colors (default true).
+        pixels.rainbow(firstPixelHue);
+        // Above line is equivalent to:
+        // strip.rainbow(firstPixelHue, 1, 255, 255, true);
+        pixels.show(); // Update strip with new contents
+        delayMicroseconds(wait);
+        //        vTaskDelay(pdMS_TO_TICKS(wait)); // Pause for a moment
+    }
+}
+
+void ledTheaterChase(uint32_t color, int wait)
+{
+    for (int a = 0; a < 10; a++)
+    { // Repeat 10 times...
+        for (int b = 0; b < 3; b++)
+        {                   //  'b' counts from 0 to 2...
+            pixels.clear(); //   Set all pixels in RAM to 0 (off)
+            // 'c' counts up from 'b' to end of strip in steps of 3...
+            for (int c = b; c < pixels.numPixels(); c += 3)
+            {
+                pixels.setPixelColor(c, color); // Set pixel 'c' to value 'color'
+            }
+            pixels.show();                   // Update strip with new contents
+            vTaskDelay(pdMS_TO_TICKS(wait)); // Pause for a moment
+        }
+    }
+}
+
+void ledTheaterChaseRainbow(int wait)
+{
+    int firstPixelHue = 0; // First pixel starts at red (hue 0)
+    for (int a = 0; a < 30; a++)
+    { // Repeat 30 times...
+        for (int b = 0; b < 3; b++)
+        {                   //  'b' counts from 0 to 2...
+            pixels.clear(); //   Set all pixels in RAM to 0 (off)
+            // 'c' counts up from 'b' to end of strip in increments of 3...
+            for (int c = b; c < pixels.numPixels(); c += 3)
+            {
+                // hue of pixel 'c' is offset by an amount to make one full
+                // revolution of the color wheel (range 65536) along the length
+                // of the strip (strip.numPixels() steps):
+                int hue = firstPixelHue + c * 65536L / pixels.numPixels();
+                uint32_t color = pixels.gamma32(pixels.ColorHSV(hue)); // hue -> RGB
+                pixels.setPixelColor(c, color);                        // Set pixel 'c' to value 'color'
+            }
+            pixels.show();                   // Update strip with new contents
+            vTaskDelay(pdMS_TO_TICKS(wait)); // Pause for a moment
+            firstPixelHue += 65536 / 90;     // One cycle of color wheel over 90 frames
+        }
+    }
+}
+
+Adafruit_NeoPixel getPixels()
+{
+    return pixels;
 }
\ No newline at end of file
diff --git a/src/lib/led_handler.hpp b/src/lib/led_handler.hpp
index 987b688..acca0bf 100644
--- a/src/lib/led_handler.hpp
+++ b/src/lib/led_handler.hpp
@@ -22,8 +22,13 @@ const int LED_EFFECT_WIFI_CONNECTING = 101;
 const int LED_EFFECT_WIFI_CONNECT_ERROR = 102;
 const int LED_EFFECT_WIFI_CONNECT_SUCCESS = 103;
 const int LED_EFFECT_WIFI_ERASE_SETTINGS = 104;
+const int LED_PROGRESS_25 = 200;
+const int LED_PROGRESS_50 = 201;
+const int LED_PROGRESS_75 = 202;
+const int LED_PROGRESS_100 = 203;
 const int LED_POWER_TEST = 999;
 extern TaskHandle_t ledTaskHandle;
+extern Adafruit_NeoPixel pixels;
 
 void ledTask(void *pvParameters);
 void setupLeds();
@@ -35,4 +40,8 @@ void clearLeds();
 QueueHandle_t getLedTaskQueue();
 bool queueLedEffect(uint effect);
 void setLights(int r, int g, int b);
-void setLights(uint32_t color);
\ No newline at end of file
+void setLights(uint32_t color);
+void ledRainbow(int wait);
+void ledTheaterChaseRainbow(int wait);
+void ledTheaterChase(uint32_t color, int wait);
+Adafruit_NeoPixel getPixels();
\ No newline at end of file
diff --git a/src/lib/ota.cpp b/src/lib/ota.cpp
index ad5800d..7740027 100644
--- a/src/lib/ota.cpp
+++ b/src/lib/ota.cpp
@@ -2,53 +2,89 @@
 
 TaskHandle_t taskOtaHandle = NULL;
 
-
 void setupOTA()
 {
+  if (preferences.getBool("otaEnabled", true))
+  {
     ArduinoOTA.onStart(onOTAStart);
 
-    ArduinoOTA.onProgress([](unsigned int progress, unsigned int total)
-                          { Serial.printf("OTA Progress: %u%%\r", (progress / (total / 100))); });
-
-    ArduinoOTA.onEnd([]()
-                     { Serial.println("\nOTA update finished"); });
+    ArduinoOTA.onProgress(onOTAProgress);
 
+    ArduinoOTA.onEnd(onOTAComplete);
 
     ArduinoOTA.setHostname(getMyHostname().c_str());
     ArduinoOTA.setMdnsEnabled(false);
+    ArduinoOTA.setRebootOnSuccess(false);
     ArduinoOTA.begin();
 
     xTaskCreate(handleOTATask, "handleOTA", 4096, NULL, tskIDLE_PRIORITY, &taskOtaHandle);
+  }
+}
 
+void onOTAProgress(unsigned int progress, unsigned int total)
+{
+  uint percentage = progress / (total / 100);
+  pixels.fill(pixels.Color(0, 255, 0));
+  if (percentage < 100)
+  {
+    pixels.setPixelColor(0, pixels.Color(0, 0, 0));
+  }
+  if (percentage < 75)
+  {
+    pixels.setPixelColor(1, pixels.Color(0, 0, 0));
+  }
+  if (percentage < 50)
+  {
+    pixels.setPixelColor(2, pixels.Color(0, 0, 0));
+  }
+  if (percentage < 25)
+  {
+    pixels.setPixelColor(3, pixels.Color(0, 0, 0));
+  }
+  pixels.show();
 }
 
 void onOTAStart()
 {
-    // Stop all timers
-    esp_timer_stop(screenRotateTimer);
-    esp_timer_stop(minuteTimer);
+  forceFullRefresh();
+  std::array<String, NUM_SCREENS> epdContent = {"U", "P", "D", "A", "T", "E", "!"};
+  setEpdContent(epdContent);
+  // Stop all timers
+  esp_timer_stop(screenRotateTimer);
+  esp_timer_stop(minuteTimer);
 
-    // Stop or suspend all tasks
+  // Stop or suspend all tasks
   //  vTaskSuspend(priceUpdateTaskHandle);
-//    vTaskSuspend(blockUpdateTaskHandle);
-    vTaskSuspend(workerTaskHandle);
-    vTaskSuspend(taskScreenRotateTaskHandle);
+  //    vTaskSuspend(blockUpdateTaskHandle);
+  vTaskSuspend(workerTaskHandle);
+  vTaskSuspend(taskScreenRotateTaskHandle);
 
-    vTaskSuspend(ledTaskHandle);
-    vTaskSuspend(buttonTaskHandle);
+  vTaskSuspend(ledTaskHandle);
+  vTaskSuspend(buttonTaskHandle);
 
-    stopWebServer();
-    stopBlockNotify();
-    stopPriceNotify();
+  stopWebServer();
+  stopBlockNotify();
+  stopPriceNotify();
 }
 
-void handleOTATask(void *parameter) {
-  for (;;) {
-    // Task 1 code
-    ArduinoOTA.handle();  // Allow OTA updates to occur
+void handleOTATask(void *parameter)
+{
+  for (;;)
+  {
+    ArduinoOTA.handle(); // Allow OTA updates to occur
     vTaskDelay(pdMS_TO_TICKS(2500));
   }
 }
 
-void downloadUpdate() {
-}
\ No newline at end of file
+void downloadUpdate()
+{
+}
+
+void onOTAComplete()
+{
+  Serial.println("\nOTA update finished");
+  Wire.end();
+  SPI.end();
+  delay(1000);
+  ESP.restart();
+}
diff --git a/src/lib/ota.hpp b/src/lib/ota.hpp
index 7a6ec81..69b5613 100644
--- a/src/lib/ota.hpp
+++ b/src/lib/ota.hpp
@@ -6,4 +6,6 @@
 void setupOTA();
 void onOTAStart();
 void handleOTATask(void *parameter);
-void downloadUpdate();
\ No newline at end of file
+void onOTAProgress(unsigned int progress, unsigned int total);
+void downloadUpdate();
+void onOTAComplete();
\ No newline at end of file
diff --git a/src/lib/screen_handler.cpp b/src/lib/screen_handler.cpp
index bef59cf..e92040b 100644
--- a/src/lib/screen_handler.cpp
+++ b/src/lib/screen_handler.cpp
@@ -27,7 +27,7 @@ const int usPerMinute = 60 * usPerSecond;
 // } WorkItem;
 
 #define WORK_QUEUE_SIZE 10
-QueueHandle_t workQueue;
+QueueHandle_t workQueue = NULL;
 
 uint currentScreen;
 
@@ -257,6 +257,7 @@ void setupTasks()
 
     xTaskCreate(taskScreenRotate, "rotateScreen", 2048, NULL, tskIDLE_PRIORITY, &taskScreenRotateTaskHandle);
 
+    waitUntilNoneBusy();
     setCurrentScreen(preferences.getUInt("currentScreen", 0));
 }
 
diff --git a/src/lib/utils.cpp b/src/lib/utils.cpp
index 5eaf958..abf5d51 100644
--- a/src/lib/utils.cpp
+++ b/src/lib/utils.cpp
@@ -6,9 +6,13 @@ int modulo(int x, int N)
 }
 
 String getMyHostname() {
-    byte mac[6];
-    WiFi.macAddress(mac);
-    return "btclock" + String(mac[4], 16) = String(mac[5], 16);
+    uint8_t mac[6];
+    //WiFi.macAddress(mac);
+    esp_efuse_mac_get_default(mac);
+    char hostname[15]; 
+    snprintf(hostname, sizeof(hostname), "btclock-%02x%02x%02x",
+             mac[3], mac[4], mac[5]);
+    return hostname;
 }
 
 double getSupplyAtBlock(uint blockNr) {
diff --git a/src/lib/webserver.cpp b/src/lib/webserver.cpp
index d94c129..2c4c958 100644
--- a/src/lib/webserver.cpp
+++ b/src/lib/webserver.cpp
@@ -24,6 +24,7 @@ void setupWebserver()
 
     server.on("/api/status", HTTP_GET, onApiStatus);
     server.on("/api/system_status", HTTP_GET, onApiSystemStatus);
+    server.on("/api/full_refresh", HTTP_GET, onApiFullRefresh);
 
     server.on("/api/action/pause", HTTP_GET, onApiActionPause);
     server.on("/api/action/timer_restart", HTTP_GET, onApiActionTimerRestart);
@@ -50,15 +51,22 @@ void setupWebserver()
     DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
 
     server.begin();
-    // if (!MDNS.begin(getMyHostname()))
-    // {
-    //     Serial.println(F("Error setting up MDNS responder!"));
-    //     while (1)
-    //     {
-    //         delay(1000);
-    //     }
-    // }
-    // MDNS.addService("http", "tcp", 80);
+
+    if (preferences.getBool("mdnsEnabled", true))
+    {
+        if (!MDNS.begin(getMyHostname()))
+        {
+            Serial.println(F("Error setting up MDNS responder!"));
+            while (1)
+            {
+                delay(1000);
+            }
+        }
+        MDNS.addService("http", "tcp", 80);
+        MDNS.addServiceTxt("http", "tcp", "model", "BTClock");
+        MDNS.addServiceTxt("http", "tcp", "version", "3.0");
+        MDNS.addServiceTxt("http", "tcp", "rev", GIT_REV);
+    }
 
     xTaskCreate(eventSourceTask, "eventSourceTask", 4096, NULL, tskIDLE_PRIORITY, &eventSourceTaskHandle);
 }
@@ -152,6 +160,20 @@ void onApiActionTimerRestart(AsyncWebServerRequest *request)
     request->send(200);
 }
 
+/**
+ * @Api
+ * @Path("/api/full_refresh")
+ */
+void onApiFullRefresh(AsyncWebServerRequest *request)
+{
+    forceFullRefresh();
+    std::array<String, NUM_SCREENS> newEpdContent = getCurrentEpdContent();
+
+    setEpdContent(newEpdContent, true);
+
+    request->send(200);
+}
+
 void onApiShowScreen(AsyncWebServerRequest *request)
 {
     if (request->hasParam("s"))
@@ -186,6 +208,12 @@ void onApiShowText(AsyncWebServerRequest *request)
 void onApiRestart(AsyncWebServerRequest *request)
 {
     request->send(200);
+
+    if (events.count())
+        events.send("closing");
+
+    delay(500);
+
     esp_restart();
 }
 
@@ -207,15 +235,17 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
     root["wpTimeout"] = preferences.getUInt("wpTimeout", 600);
     root["tzOffset"] = preferences.getInt("gmtOffset", TIME_OFFSET_SECONDS) / 60;
     root["useBitcoinNode"] = preferences.getBool("useNode", false);
-    // root["rpcPort"] = preferences.getUInt("rpcPort", BITCOIND_PORT);
-    // root["rpcUser"] = preferences.getString("rpcUser", BITCOIND_RPC_USER);
-    // root["rpcHost"] = preferences.getString("rpcHost", BITCOIND_HOST);
     root["mempoolInstance"] = preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
     root["ledTestOnPower"] = preferences.getBool("ledTestOnPower", true);
     root["ledFlashOnUpdate"] = preferences.getBool("ledFlashOnUpd", false);
     root["ledBrightness"] = preferences.getUInt("ledBrightness", 128);
     root["stealFocusOnBlock"] = preferences.getBool("stealFocus", true);
     root["mcapBigChar"] = preferences.getBool("mcapBigChar", true);
+    root["mdnsEnabled"] = preferences.getBool("mdnsEnabled", true);
+    root["otaEnabled"] = preferences.getBool("otaEnabled", true);
+
+    root["hostname"] = getMyHostname();
+    root["ip"] = WiFi.localIP();
 
 #ifdef GIT_REV
     root["gitRev"] = String(GIT_REV);
@@ -274,18 +304,55 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
 
     settingsChanged = processEpdColorSettings(request);
 
+    if (request->hasParam("ledTestOnPower", true))
+    {
+        AsyncWebParameter *ledTestOnPower = request->getParam("ledTestOnPower", true);
+
+        preferences.putBool("ledTestOnPower", ledTestOnPower->value().toInt());
+        settingsChanged = true;
+    }
+    else
+    {
+        preferences.putBool("ledTestOnPower", 0);
+        settingsChanged = true;
+    }
+
     if (request->hasParam("ledFlashOnUpd", true))
     {
         AsyncWebParameter *ledFlashOnUpdate = request->getParam("ledFlashOnUpd", true);
 
         preferences.putBool("ledFlashOnUpd", ledFlashOnUpdate->value().toInt());
-//        Serial.printf("Setting led flash on update to %d\r\n", ledFlashOnUpdate->value().toInt());
         settingsChanged = true;
     }
     else
     {
         preferences.putBool("ledFlashOnUpd", 0);
-//        Serial.print("Setting led flash on update to false");
+        settingsChanged = true;
+    }
+
+    if (request->hasParam("mdnsEnabled", true))
+    {
+        AsyncWebParameter *mdnsEnabled = request->getParam("mdnsEnabled", true);
+
+        preferences.putBool("mdnsEnabled", mdnsEnabled->value().toInt());
+        settingsChanged = true;
+    }
+    else
+    {
+        preferences.putBool("mdnsEnabled", 0);
+        settingsChanged = true;
+    }
+
+    if (request->hasParam("otaEnabled", true))
+    {
+        AsyncWebParameter *otaEnabled = request->getParam("otaEnabled", true);
+
+        preferences.putBool("otaEnabled", otaEnabled->value().toInt());
+        settingsChanged = true;
+    }
+    else
+    {
+        preferences.putBool("otaEnabled", 0);
         settingsChanged = true;
     }
 
@@ -294,13 +361,11 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
         AsyncWebParameter *stealFocusOnBlock = request->getParam("stealFocusOnBlock", true);
 
         preferences.putBool("stealFocus", stealFocusOnBlock->value().toInt());
-//        Serial.printf("Setting steal focus on new block to %d\r\n", stealFocusOnBlock->value().toInt());
         settingsChanged = true;
     }
     else
     {
         preferences.putBool("stealFocus", 0);
-//        Serial.print("Setting steal focus on new block to false");
         settingsChanged = true;
     }
 
@@ -309,13 +374,11 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
         AsyncWebParameter *mcapBigChar = request->getParam("mcapBigChar", true);
 
         preferences.putBool("mcapBigChar", mcapBigChar->value().toInt());
-        Serial.printf("Setting big characters for market cap to %d\r\n", mcapBigChar->value().toInt());
         settingsChanged = true;
     }
     else
     {
         preferences.putBool("mcapBigChar", 0);
-//        Serial.print("Setting big characters for market cap to false");
         settingsChanged = true;
     }
 
@@ -324,7 +387,6 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
         AsyncWebParameter *mempoolInstance = request->getParam("mempoolInstance", true);
 
         preferences.putString("mempoolInstance", mempoolInstance->value().c_str());
-//        Serial.printf("Setting mempool instance to %s\r\n", mempoolInstance->value().c_str());
         settingsChanged = true;
     }
 
@@ -333,7 +395,6 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
         AsyncWebParameter *ledBrightness = request->getParam("ledBrightness", true);
 
         preferences.putUInt("ledBrightness", ledBrightness->value().toInt());
-//        Serial.printf("Setting brightness to %d\r\n", ledBrightness->value().toInt());
         settingsChanged = true;
     }
 
@@ -342,7 +403,6 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
         AsyncWebParameter *fullRefreshMin = request->getParam("fullRefreshMin", true);
 
         preferences.putUInt("fullRefreshMin", fullRefreshMin->value().toInt());
-//        Serial.printf("Set full refresh minutes to %d\r\n", fullRefreshMin->value().toInt());
         settingsChanged = true;
     }
 
@@ -351,7 +411,6 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
         AsyncWebParameter *wpTimeout = request->getParam("wpTimeout", true);
 
         preferences.putUInt("wpTimeout", wpTimeout->value().toInt());
-//        Serial.printf("Set WiFi portal timeout seconds to %ld\r\n", wpTimeout->value().toInt());
         settingsChanged = true;
     }
 
@@ -367,7 +426,6 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
             AsyncWebParameter *screenParam = request->getParam(key, true);
             visible = screenParam->value().toInt();
         }
-//        Serial.printf("Setting screen %d to %d\r\n", i, visible);
 
         preferences.putBool(prefKey.c_str(), visible);
     }
@@ -377,7 +435,6 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
         AsyncWebParameter *p = request->getParam("tzOffset", true);
         int tzOffsetSeconds = p->value().toInt() * 60;
         preferences.putInt("gmtOffset", tzOffsetSeconds);
-//        Serial.printf("Setting tz offset to %d\r\n", tzOffsetSeconds);
         settingsChanged = true;
     }
 
@@ -386,7 +443,6 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
         AsyncWebParameter *p = request->getParam("minSecPriceUpd", true);
         int minSecPriceUpd = p->value().toInt();
         preferences.putUInt("minSecPriceUpd", minSecPriceUpd);
-//        Serial.printf("Setting minSecPriceUpd to %d\r\n", minSecPriceUpd);
         settingsChanged = true;
     }
 
@@ -398,34 +454,6 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
         settingsChanged = true;
     }
 
-    // if (request->hasParam("useBitcoinNode", true))
-    // {
-    //     AsyncWebParameter *p = request->getParam("useBitcoinNode", true);
-    //     bool useBitcoinNode = p->value().toInt();
-    //     preferences.putBool("useNode", useBitcoinNode);
-    //     settingsChanged = true;
-
-    //     String rpcVars[] = {"rpcHost", "rpcPort", "rpcUser", "rpcPass"};
-
-    //     for (String v : rpcVars)
-    //     {
-    //         if (request->hasParam(v, true))
-    //         {
-    //             AsyncWebParameter *pv = request->getParam(v, true);
-    //             // Don't store an empty password, probably new settings save
-    //             if (!(v.equals("rpcPass") && pv->value().length() == 0))
-    //             {
-    //                 preferences.putString(v.c_str(), pv->value().c_str());
-    //             }
-    //         }
-    //     }
-    // }
-    // else
-    // {
-    //     preferences.putBool("useNode", false);
-    //     settingsChanged = true;
-    // }
-
     request->send(200);
     if (settingsChanged)
     {
@@ -460,9 +488,14 @@ void onApiLightsSetColor(AsyncWebServerRequest *request)
     if (request->hasParam("c"))
     {
         String rgbColor = request->getParam("c")->value();
-        uint r, g, b;
-        sscanf(rgbColor.c_str(), "%02x%02x%02x", &r, &g, &b);
-        setLights(r, g, b);
+
+        if (rgbColor.compareTo("off") == 0) {
+            setLights(0, 0, 0);
+        } else {
+            uint r, g, b;
+            sscanf(rgbColor.c_str(), "%02x%02x%02x", &r, &g, &b);
+            setLights(r, g, b);
+        }
         request->send(200, "text/plain", rgbColor);
     }
 }
diff --git a/src/lib/webserver.hpp b/src/lib/webserver.hpp
index 7af562e..70613dc 100644
--- a/src/lib/webserver.hpp
+++ b/src/lib/webserver.hpp
@@ -3,7 +3,7 @@
 #include "ESPAsyncWebServer.h"
 #include <ArduinoJson.h>
 #include <LittleFS.h>
-// #include <ESPmDNS.h>
+#include <ESPmDNS.h>
 
 #include "lib/block_notify.hpp"
 #include "lib/price_notify.hpp"
@@ -28,6 +28,7 @@ void onApiActionPause(AsyncWebServerRequest *request);
 void onApiActionTimerRestart(AsyncWebServerRequest *request);
 void onApiSettingsGet(AsyncWebServerRequest *request);
 void onApiSettingsPost(AsyncWebServerRequest *request);
+void onApiFullRefresh(AsyncWebServerRequest *request);
 
 void onApiLightsOff(AsyncWebServerRequest *request);
 void onApiLightsSetColor(AsyncWebServerRequest *request);