From bd6b9ff1da814ebbe15ba0ea07fa695c0142c6e1 Mon Sep 17 00:00:00 2001
From: gitolicious <mrjchn@gmail.com>
Date: Tue, 28 May 2019 10:19:17 +0200
Subject: [PATCH] added link from dashboard to web server, if configured (#556)

* added link from dashboard to web server, if configured

* linter fixes

* simplified integration lookup

* included loaded_integration in storage json

* included loaded_integration in storage json

* fixed loaded_integrations

plus linter changes

* fixed comment: List

Co-Authored-By: Otto Winter <otto@otto-winter.com>

* return empty list

Co-Authored-By: Otto Winter <otto@otto-winter.com>

* convert to list

Co-Authored-By: Otto Winter <otto@otto-winter.com>

* default to empty list on missing loaded_integrations

Co-Authored-By: Otto Winter <otto@otto-winter.com>

* None check no longer needed

Co-Authored-By: Otto Winter <otto@otto-winter.com>

* None check no longer needed

Co-Authored-By: Otto Winter <otto@otto-winter.com>

* removed newline
---
 esphome/const.py                       |  1 +
 esphome/dashboard/dashboard.py         |  6 ++++++
 esphome/dashboard/templates/index.html |  3 +++
 esphome/storage_json.py                | 11 +++++++++--
 4 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/esphome/const.py b/esphome/const.py
index 45f72a19fd..50f20ed432 100644
--- a/esphome/const.py
+++ b/esphome/const.py
@@ -201,6 +201,7 @@ CONF_LEVEL = 'level'
 CONF_LG = 'lg'
 CONF_LIBRARIES = 'libraries'
 CONF_LIGHT = 'light'
+CONF_LOADED_INTEGRATIONS = 'loaded_integrations'
 CONF_LOCAL = 'local'
 CONF_LOGGER = 'logger'
 CONF_LOGS = 'logs'
diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py
index 0d6a26c6ee..14b06a3c82 100644
--- a/esphome/dashboard/dashboard.py
+++ b/esphome/dashboard/dashboard.py
@@ -430,6 +430,12 @@ class DashboardEntry(object):
     def update_new(self):
         return const.__version__
 
+    @property
+    def loaded_integrations(self):
+        if self.storage is None:
+            return []
+        return self.storage.loaded_integrations
+
 
 class MainRequestHandler(BaseHandler):
     @authenticated
diff --git a/esphome/dashboard/templates/index.html b/esphome/dashboard/templates/index.html
index b7df4509cc..b912bf8467 100644
--- a/esphome/dashboard/templates/index.html
+++ b/esphome/dashboard/templates/index.html
@@ -67,6 +67,9 @@
           <div class="card-content">
             <span class="card-title">
               {{ escape(entry.name) }}
+              {% if 'web_server' in entry.loaded_integrations %}
+                <a href="http://{{ escape(entry.address) }}" target="_blank"><i class="material-icons icon-grey">launch</i></a>
+              {% end %}
               <i class="material-icons right dropdown-trigger" data-target="dropdown-{{ i }}">more_vert</i>
             </span>
             <p>
diff --git a/esphome/storage_json.py b/esphome/storage_json.py
index 4d789b969c..b04f056f11 100644
--- a/esphome/storage_json.py
+++ b/esphome/storage_json.py
@@ -37,7 +37,7 @@ def trash_storage_path(base_path):  # type: (str) -> str
 class StorageJSON(object):
     def __init__(self, storage_version, name, esphome_version,
                  src_version, arduino_version, address, esp_platform, board, build_path,
-                 firmware_bin_path):
+                 firmware_bin_path, loaded_integrations):
         # Version of the storage JSON schema
         assert storage_version is None or isinstance(storage_version, int)
         self.storage_version = storage_version  # type: int
@@ -61,6 +61,9 @@ class StorageJSON(object):
         self.build_path = build_path  # type: str
         # The absolute path to the firmware binary
         self.firmware_bin_path = firmware_bin_path  # type: str
+        # A list of strings of names of loaded integrations
+        self.loaded_integrations = loaded_integrations   # type: List[str]
+        self.loaded_integrations.sort()
 
     def as_dict(self):
         return {
@@ -74,6 +77,7 @@ class StorageJSON(object):
             'board': self.board,
             'build_path': self.build_path,
             'firmware_bin_path': self.firmware_bin_path,
+            'loaded_integrations': self.loaded_integrations,
         }
 
     def to_json(self):
@@ -97,6 +101,7 @@ class StorageJSON(object):
             board=esph.board,
             build_path=esph.build_path,
             firmware_bin_path=esph.firmware_bin,
+            loaded_integrations=list(esph.loaded_integrations),
         )
 
     @staticmethod
@@ -113,6 +118,7 @@ class StorageJSON(object):
             board=board,
             build_path=None,
             firmware_bin_path=None,
+            loaded_integrations=[],
         )
 
     @staticmethod
@@ -130,9 +136,10 @@ class StorageJSON(object):
         board = storage.get('board')
         build_path = storage.get('build_path')
         firmware_bin_path = storage.get('firmware_bin_path')
+        loaded_integrations = storage.get('loaded_integrations', [])
         return StorageJSON(storage_version, name, esphome_version,
                            src_version, arduino_version, address, esp_platform, board, build_path,
-                           firmware_bin_path)
+                           firmware_bin_path, loaded_integrations)
 
     @staticmethod
     def load(path):  # type: (str) -> Optional[StorageJSON]