From 97194ee23ad0753374a140a362ab056b9e20822e Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Thu, 3 Oct 2024 15:46:12 +0200 Subject: [PATCH 1/9] Check host network connectivity Check host network connectivity and offer DNS alternatives in case host internet is not available. This allows users with broken DNS setup to configure the host network with a working DNS. Requires https://github.com/home-assistant/supervisor/pull/5321. --- rootfs/usr/share/www/index.html | 28 ++++++- rootfs/usr/share/www/static/scripts.js | 100 ++++++++++++++++++++++++- rootfs/usr/share/www/static/styles.css | 34 ++++++++- 3 files changed, 157 insertions(+), 5 deletions(-) diff --git a/rootfs/usr/share/www/index.html b/rootfs/usr/share/www/index.html index 9aca701..16d8253 100644 --- a/rootfs/usr/share/www/index.html +++ b/rootfs/usr/share/www/index.html @@ -26,7 +26,7 @@

This may take 20 minutes or more

-
+

Error installing Home Assistant

+
+

Networking issue detected

+ +
+ + +
+

     
   
diff --git a/rootfs/usr/share/www/static/scripts.js b/rootfs/usr/share/www/static/scripts.js
index dc08a37..2d88318 100644
--- a/rootfs/usr/share/www/static/scripts.js
+++ b/rootfs/usr/share/www/static/scripts.js
@@ -81,7 +81,7 @@ function fetchLogs() {
       res.text().then(function (text) {
         var logElement = document.getElementById("log");
         if (errorCheck.test(text)) {
-          document.body.classList.add("error");
+          document.body.classList.add("supervisor-error");
           document.getElementById("show_logs").innerText = "Download raw logs";
           logElement.showFull = true;
         }
@@ -112,8 +112,40 @@ function scheduleFetchLogs() {
   scheduleTimeout = setTimeout(fetchLogs, 5000);
 }
 
+function fetchNetworkInfo() {
+  fetch("/supervisor/network/info").then(function (res) {
+    if (!res.ok)
+      return;
+
+    res.json().then(function (data) {
+      if (!data.data.host_internet) {
+        document.body.classList.add("network-issue");
+      }
+
+      if (document.body.classList.contains("network-issue")) {
+        const primaryInterface = data.data.interfaces.find(intf => intf.primary);
+        var dnsElement = document.getElementById("current_dns");
+        if (!primaryInterface) {
+          dnsElement.innerText = "(no primary interface)";
+        } else {
+          dnsElement.innerText = [...(primaryInterface.ipv4?.nameservers || []), ...(primaryInterface.ipv6?.nameservers || [])].join(', ');
+        }
+      }
+
+    });
+  }, scheduleFetchNetworkInfo());
+}
+
+var scheduleNetworkTimeout;
+
+function scheduleFetchNetworkInfo() {
+  clearTimeout(scheduleNetworkTimeout);
+  scheduleNetworkTimeout = setTimeout(fetchNetworkInfo, 5000);
+}
+
 scheduleTry();
 fetchLogs();
+fetchNetworkInfo();
 
 document.getElementById("show_logs").addEventListener("click", toggleLogs);
 function toggleLogs(event) {
@@ -139,6 +171,72 @@ function toggleLogs(event) {
   }
 }
 
+document.getElementById("try_cloudflare_dns").addEventListener("click", function() {
+  setDns(["1.1.1.1", "1.0.0.1"], ["2606:4700:4700::1111", "2606:4700:4700::1001"]);
+});
+
+document.getElementById("try_google_dns").addEventListener("click", function() {
+  setDns(["8.8.8.8", "8.8.4.4"], ["2001:4860:4860::8888", "2001:4860:4860::8844"]);
+});
+
+function setDns(ipv4nameservers, ipv6nameservers) {
+  // Step 1: Fetch the primary network interface from the /network/info endpoint
+  fetch("/supervisor/network/info", {
+      method: 'GET',
+      headers: {
+          'Content-Type': 'application/json',
+      }
+  })
+  .then(response => {
+      if (!response.ok) {
+          throw new Error('Failed to fetch network info');
+      }
+      return response.json();
+  })
+  .then(data => {
+      // Step 2: Find the primary interface
+      const primaryInterface = data.data.interfaces.find(intf => intf.primary && intf.enabled);
+      if (!primaryInterface) {
+          throw new Error('No primary interface found');
+      }
+
+      // Step 3: Update the DNS settings for the primary interface
+      const payload = {
+          ipv4: {
+              method: "auto",
+              nameservers: ipv4nameservers
+          },
+          ipv6: {
+              method: "auto",
+              nameservers: ipv6nameservers
+          }
+      };
+
+      return fetch(`/supervisor/network/interface/${primaryInterface.interface}/update`, {
+          method: 'POST',
+          headers: {
+              'Content-Type': 'application/json',
+          },
+          body: JSON.stringify(payload)
+      });
+  })
+  .then(response => {
+      if (!response.ok) {
+          throw new Error('Failed to update the interface');
+      }
+      fetchNetworkInfo();
+      return response.json();
+  })
+  .then(data => {
+      console.log('Success:', data);
+      // Optionally handle the success case, e.g., updating the UI or showing a message
+  })
+  .catch((error) => {
+      console.error('Error:', error);
+      // Optionally handle the error case, e.g., showing an error message
+  });
+}
+
 var dialogs = document.querySelectorAll('dialog');
 dialogs.forEach(dialog => {
   dialogPolyfill.registerDialog(dialog);
diff --git a/rootfs/usr/share/www/static/styles.css b/rootfs/usr/share/www/static/styles.css
index 6e99914..e1306c0 100644
--- a/rootfs/usr/share/www/static/styles.css
+++ b/rootfs/usr/share/www/static/styles.css
@@ -30,15 +30,15 @@ body {
   white-space: nowrap;
 }
 
-.error .state-normal {
+.supervisor-error .state-normal {
   display: none;
 }
 
-.state-error {
+#state-error {
   display: none;
 }
 
-.error .state-error {
+.supervisor-error #state-error {
   display: block;
 }
 
@@ -60,6 +60,9 @@ body {
   pointer-events: none;
   content: "";
   border-radius: 4px;
+}
+
+.error .alert::after {
   background-color: #db4437;
 }
 
@@ -76,6 +79,31 @@ body {
   margin-right: 0;
 }
 
+#state-network-issue {
+  display: none;
+}
+
+.network-issue #state-network-issue {
+  display: block;
+}
+
+.network-issue .state-normal {
+  display: none;
+}
+
+.warning .alert-icon {
+  fill: #ffa600;
+}
+
+.warning .alert::after {
+  background-color: #ffa600;
+}
+
+.warning .actions {
+  display: flex;
+  margin-bottom: 16px;
+}
+
 .header {
   text-align: center;
   margin-top: 32px;

From 94b7d29b3975346fdf51acf279efcc87185ba16c Mon Sep 17 00:00:00 2001
From: Stefan Agner 
Date: Mon, 7 Oct 2024 11:20:46 +0200
Subject: [PATCH 2/9] Apply suggestions from code review

Co-authored-by: Bram Kragten 
---
 rootfs/usr/share/www/static/scripts.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/rootfs/usr/share/www/static/scripts.js b/rootfs/usr/share/www/static/scripts.js
index 2d88318..22c63b9 100644
--- a/rootfs/usr/share/www/static/scripts.js
+++ b/rootfs/usr/share/www/static/scripts.js
@@ -124,7 +124,7 @@ function fetchNetworkInfo() {
 
       if (document.body.classList.contains("network-issue")) {
         const primaryInterface = data.data.interfaces.find(intf => intf.primary);
-        var dnsElement = document.getElementById("current_dns");
+        const dnsElement = document.getElementById("current_dns");
         if (!primaryInterface) {
           dnsElement.innerText = "(no primary interface)";
         } else {
@@ -136,7 +136,7 @@ function fetchNetworkInfo() {
   }, scheduleFetchNetworkInfo());
 }
 
-var scheduleNetworkTimeout;
+const scheduleNetworkTimeout;
 
 function scheduleFetchNetworkInfo() {
   clearTimeout(scheduleNetworkTimeout);

From a9f065cdda40bb0d0902d77e6b821379c0e840df Mon Sep 17 00:00:00 2001
From: Stefan Agner 
Date: Mon, 7 Oct 2024 11:47:30 +0200
Subject: [PATCH 3/9] Remove network issue warning once resolved

---
 rootfs/usr/share/www/static/scripts.js | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/rootfs/usr/share/www/static/scripts.js b/rootfs/usr/share/www/static/scripts.js
index 22c63b9..2ab9163 100644
--- a/rootfs/usr/share/www/static/scripts.js
+++ b/rootfs/usr/share/www/static/scripts.js
@@ -121,6 +121,10 @@ function fetchNetworkInfo() {
       if (!data.data.host_internet) {
         document.body.classList.add("network-issue");
       }
+      else
+      {
+        document.body.classList.remove("network-issue");
+      }
 
       if (document.body.classList.contains("network-issue")) {
         const primaryInterface = data.data.interfaces.find(intf => intf.primary);

From 199346e6fc54939b38c3b649a175c0fe674677d8 Mon Sep 17 00:00:00 2001
From: Stefan Agner 
Date: Mon, 7 Oct 2024 13:16:45 +0200
Subject: [PATCH 4/9] Make const var again

All consts need to be assigned on creation. This is not possible in this
case.
---
 rootfs/usr/share/www/static/scripts.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/rootfs/usr/share/www/static/scripts.js b/rootfs/usr/share/www/static/scripts.js
index 2ab9163..b60f487 100644
--- a/rootfs/usr/share/www/static/scripts.js
+++ b/rootfs/usr/share/www/static/scripts.js
@@ -140,7 +140,7 @@ function fetchNetworkInfo() {
   }, scheduleFetchNetworkInfo());
 }
 
-const scheduleNetworkTimeout;
+var scheduleNetworkTimeout;
 
 function scheduleFetchNetworkInfo() {
   clearTimeout(scheduleNetworkTimeout);

From d549059deafc7742a642a05f49f8b1e44b3801d8 Mon Sep 17 00:00:00 2001
From: Stefan Agner 
Date: Mon, 7 Oct 2024 13:18:42 +0200
Subject: [PATCH 5/9] Improve wording

---
 rootfs/usr/share/www/index.html | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/rootfs/usr/share/www/index.html b/rootfs/usr/share/www/index.html
index 16d8253..c0fc91a 100644
--- a/rootfs/usr/share/www/index.html
+++ b/rootfs/usr/share/www/index.html
@@ -64,8 +64,8 @@ 

Networking issue detected

- - + +


From 4e526098a240ffa72566389f50a05961b61ebf01 Mon Sep 17 00:00:00 2001
From: Stefan Agner 
Date: Mon, 7 Oct 2024 13:32:25 +0200
Subject: [PATCH 6/9] Replace try with use

---
 rootfs/usr/share/www/index.html        | 4 ++--
 rootfs/usr/share/www/static/scripts.js | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/rootfs/usr/share/www/index.html b/rootfs/usr/share/www/index.html
index c0fc91a..6be74a2 100644
--- a/rootfs/usr/share/www/index.html
+++ b/rootfs/usr/share/www/index.html
@@ -64,8 +64,8 @@ 

Networking issue detected

- - + +

diff --git a/rootfs/usr/share/www/static/scripts.js b/rootfs/usr/share/www/static/scripts.js
index b60f487..2e4ae9f 100644
--- a/rootfs/usr/share/www/static/scripts.js
+++ b/rootfs/usr/share/www/static/scripts.js
@@ -175,11 +175,11 @@ function toggleLogs(event) {
   }
 }
 
-document.getElementById("try_cloudflare_dns").addEventListener("click", function() {
+document.getElementById("use_cloudflare_dns").addEventListener("click", function() {
   setDns(["1.1.1.1", "1.0.0.1"], ["2606:4700:4700::1111", "2606:4700:4700::1001"]);
 });
 
-document.getElementById("try_google_dns").addEventListener("click", function() {
+document.getElementById("use_google_dns").addEventListener("click", function() {
   setDns(["8.8.8.8", "8.8.4.4"], ["2001:4860:4860::8888", "2001:4860:4860::8844"]);
 });
 

From dde1126e00b40f13840795c391511996a6b6de8f Mon Sep 17 00:00:00 2001
From: Stefan Agner 
Date: Mon, 7 Oct 2024 13:47:13 +0200
Subject: [PATCH 7/9] Improve network issue wording

---
 rootfs/usr/share/www/index.html | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/rootfs/usr/share/www/index.html b/rootfs/usr/share/www/index.html
index 6be74a2..85fc2a8 100644
--- a/rootfs/usr/share/www/index.html
+++ b/rootfs/usr/share/www/index.html
@@ -55,11 +55,14 @@ 

Networking issue detected

- There is a network issue on the host operating system, which prevents Home Assistant from installing completely. - This might be DNS related, currently using as DNS. + Home Assistant OS detected a networking issue in your setup. As part of the initial + setup, Home Assistant OS downloads the latest version of Home Assistant Core. This + networking issue prevents this download. The network issue might be DNS related. + The currently used DNS service is: .

- Update your routers configuration if you want to use a custom DNS. Alternatively, you can try a public DNS. + To resolve this, you can try a different DNS server. Select one of the options below. + Alternatively, change your router configuration to use your own custom DNS server.

From 02f0f5aa28e8bce997efc899335cf06e0552fecd Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Mon, 7 Oct 2024 17:31:37 +0200 Subject: [PATCH 8/9] Rename function, call schedule outside of then() The idea is to call schedule() always, no matter if there is an error or not. So call it outside of the then() block. --- rootfs/usr/share/www/static/scripts.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/rootfs/usr/share/www/static/scripts.js b/rootfs/usr/share/www/static/scripts.js index 2e4ae9f..4d719ea 100644 --- a/rootfs/usr/share/www/static/scripts.js +++ b/rootfs/usr/share/www/static/scripts.js @@ -98,7 +98,8 @@ function fetchLogs() { } }); } - }, scheduleFetchLogs()); + }); + scheduleFetchLogs(); } function scheduleTry() { @@ -112,7 +113,7 @@ function scheduleFetchLogs() { scheduleTimeout = setTimeout(fetchLogs, 5000); } -function fetchNetworkInfo() { +function fetchSupervisorInfo() { fetch("/supervisor/network/info").then(function (res) { if (!res.ok) return; @@ -137,19 +138,20 @@ function fetchNetworkInfo() { } }); - }, scheduleFetchNetworkInfo()); + }); + scheduleFetchSupervisorInfo(); } -var scheduleNetworkTimeout; +var scheduleSupervisorTimeout; -function scheduleFetchNetworkInfo() { - clearTimeout(scheduleNetworkTimeout); - scheduleNetworkTimeout = setTimeout(fetchNetworkInfo, 5000); +function scheduleFetchSupervisorInfo() { + clearTimeout(scheduleSupervisorTimeout); + scheduleSupervisorTimeout = setTimeout(fetchSupervisorInfo, 5000); } scheduleTry(); fetchLogs(); -fetchNetworkInfo(); +fetchSupervisorInfo(); document.getElementById("show_logs").addEventListener("click", toggleLogs); function toggleLogs(event) { @@ -228,7 +230,7 @@ function setDns(ipv4nameservers, ipv6nameservers) { if (!response.ok) { throw new Error('Failed to update the interface'); } - fetchNetworkInfo(); + fetchSupervisorInfo(); return response.json(); }) .then(data => { From 86c5c5a097c67acc90e58e3f3f59a2c02a5b9780 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Mon, 7 Oct 2024 17:34:14 +0200 Subject: [PATCH 9/9] Use let --- rootfs/usr/share/www/static/scripts.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rootfs/usr/share/www/static/scripts.js b/rootfs/usr/share/www/static/scripts.js index 4d719ea..74c7145 100644 --- a/rootfs/usr/share/www/static/scripts.js +++ b/rootfs/usr/share/www/static/scripts.js @@ -106,7 +106,7 @@ function scheduleTry() { setTimeout(testAvailable, 5000); } -var scheduleTimeout; +let scheduleTimeout; function scheduleFetchLogs() { clearTimeout(scheduleTimeout); @@ -142,7 +142,7 @@ function fetchSupervisorInfo() { scheduleFetchSupervisorInfo(); } -var scheduleSupervisorTimeout; +let scheduleSupervisorTimeout; function scheduleFetchSupervisorInfo() { clearTimeout(scheduleSupervisorTimeout);