diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 000000000..dc90e5a1c --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,17 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 60 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - security +# Label to use when marking an issue as stale +staleLabel: wontfix +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false diff --git a/API.md b/API.md index 0d4679e42..030599729 100644 --- a/API.md +++ b/API.md @@ -516,7 +516,8 @@ Get all available addons. "ingress": "bool", "ingress_entry": "null|/api/hassio_ingress/slug", "ingress_url": "null|/api/hassio_ingress/slug/entry.html", - "ingress_port": "null|int" + "ingress_port": "null|int", + "ingress_panel": "null|bool" } ``` @@ -537,7 +538,8 @@ Get all available addons. }, "options": {}, "audio_output": "null|0,0", - "audio_input": "null|0,0" + "audio_input": "null|0,0", + "ingress_panel": "bool" } ``` @@ -602,6 +604,23 @@ Create a new Session for access to ingress service. } ``` +- GET `/ingress/panels` + +Return a list of enabled panels. + +```json +{ + "panels": { + "addon_slug": { + "enable": "boolean", + "icon": "mdi:...", + "title": "title", + "admin": "boolean" + } + } +} +``` + - VIEW `/ingress/{token}` Ingress WebUI for this Add-on. The addon need support HASS Auth! diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4f89bd810..450fd8450 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -14,8 +14,8 @@ pr: - dev variables: - versionHadolint: v1.16.3 - versionBuilder: 1.1 + versionHadolint: 'v1.16.3' + versionBuilder: '1.1' jobs: diff --git a/hassio/addons/addon.py b/hassio/addons/addon.py index c6980a24e..659de8fc6 100644 --- a/hassio/addons/addon.py +++ b/hassio/addons/addon.py @@ -47,6 +47,10 @@ from ..const import ( ATTR_INGRESS_ENTRY, ATTR_INGRESS_PORT, ATTR_INGRESS_TOKEN, + ATTR_INGRESS_PANEL, + ATTR_INGRESS_PANEL_ADMIN, + ATTR_INGRESS_PANEL_ICON, + ATTR_INGRESS_PANEL_TITLE, ATTR_KERNEL_MODULES, ATTR_LEGACY, ATTR_LOCATON, @@ -449,6 +453,21 @@ class Addon(CoreSysAttributes): return self.sys_ingress.get_dynamic_port(self.slug) return port + @property + def ingress_icon(self) -> str: + """Return panel icon for Ingress frame.""" + return self._mesh[ATTR_INGRESS_PANEL_ICON] + + @property + def ingress_title(self) -> str: + """Return panel icon for Ingress frame.""" + return self._mesh.get(ATTR_INGRESS_PANEL_TITLE, self.name) + + @property + def ingress_admin(self) -> str: + """Return panel icon for Ingress frame.""" + return self._mesh[ATTR_INGRESS_PANEL_ADMIN] + @property def host_network(self): """Return True if add-on run on host network.""" @@ -538,6 +557,18 @@ class Addon(CoreSysAttributes): """Return True if the add-on access support ingress.""" return self._mesh[ATTR_INGRESS] + @property + def ingress_panel(self) -> Optional[bool]: + """Return True if the add-on access support ingress.""" + if self.is_installed: + return self._data.user[self._id][ATTR_INGRESS_PANEL] + return None + + @ingress_panel.setter + def ingress_panel(self, value: bool): + """Return True if the add-on access support ingress.""" + self._data.user[self._id][ATTR_INGRESS_PANEL] = value + @property def with_gpio(self): """Return True if the add-on access to GPIO interface.""" diff --git a/hassio/addons/validate.py b/hassio/addons/validate.py index 1bc5ca216..a618961a8 100644 --- a/hassio/addons/validate.py +++ b/hassio/addons/validate.py @@ -41,6 +41,10 @@ from ..const import ( ATTR_INGRESS_ENTRY, ATTR_INGRESS_PORT, ATTR_INGRESS_TOKEN, + ATTR_INGRESS_PANEL, + ATTR_INGRESS_PANEL_ADMIN, + ATTR_INGRESS_PANEL_ICON, + ATTR_INGRESS_PANEL_TITLE, ATTR_KERNEL_MODULES, ATTR_LEGACY, ATTR_LOCATON, @@ -159,6 +163,9 @@ SCHEMA_ADDON_CONFIG = vol.Schema({ vol.Optional(ATTR_INGRESS, default=False): vol.Boolean(), vol.Optional(ATTR_INGRESS_PORT, default=8099): vol.Any(NETWORK_PORT, vol.Equal(0)), vol.Optional(ATTR_INGRESS_ENTRY): vol.Coerce(str), + vol.Optional(ATTR_INGRESS_PANEL_ICON, default="mdi:puzzle"): vol.Coerce(str), + vol.Optional(ATTR_INGRESS_PANEL_TITLE): vol.Coerce(str), + vol.Optional(ATTR_INGRESS_PANEL_ADMIN, default=True): vol.Boolean(), vol.Optional(ATTR_HOMEASSISTANT): vol.Maybe(vol.Coerce(str)), vol.Optional(ATTR_HOST_NETWORK, default=False): vol.Boolean(), vol.Optional(ATTR_HOST_PID, default=False): vol.Boolean(), @@ -239,6 +246,7 @@ SCHEMA_ADDON_USER = vol.Schema({ vol.Optional(ATTR_AUDIO_OUTPUT): ALSA_DEVICE, vol.Optional(ATTR_AUDIO_INPUT): ALSA_DEVICE, vol.Optional(ATTR_PROTECTED, default=True): vol.Boolean(), + vol.Optional(ATTR_INGRESS_PANEL, default=False): vol.Boolean(), }, extra=vol.REMOVE_EXTRA) diff --git a/hassio/api/__init__.py b/hassio/api/__init__.py index 865a3cb54..32f453bec 100644 --- a/hassio/api/__init__.py +++ b/hassio/api/__init__.py @@ -195,6 +195,7 @@ class RestAPI(CoreSysAttributes): self.webapp.add_routes([ web.post('/ingress/session', api_ingress.create_session), + web.get('/ingress/panels', api_ingress.panels), web.view('/ingress/{token}/{path:.*}', api_ingress.handler), ]) diff --git a/hassio/api/addons.py b/hassio/api/addons.py index 455195f08..13475539f 100644 --- a/hassio/api/addons.py +++ b/hassio/api/addons.py @@ -46,6 +46,7 @@ from ..const import ( ATTR_INGRESS_ENTRY, ATTR_INGRESS_PORT, ATTR_INGRESS_URL, + ATTR_INGRESS_PANEL, ATTR_INSTALLED, ATTR_IP_ADDRESS, ATTR_KERNEL_MODULES, @@ -100,6 +101,7 @@ SCHEMA_OPTIONS = vol.Schema({ vol.Optional(ATTR_AUTO_UPDATE): vol.Boolean(), vol.Optional(ATTR_AUDIO_OUTPUT): ALSA_DEVICE, vol.Optional(ATTR_AUDIO_INPUT): ALSA_DEVICE, + vol.Optional(ATTR_INGRESS_PANEL): vol.Boolean(), }) # pylint: disable=no-value-for-parameter @@ -227,6 +229,7 @@ class APIAddons(CoreSysAttributes): ATTR_INGRESS_ENTRY: addon.ingress_entry, ATTR_INGRESS_URL: addon.ingress_url, ATTR_INGRESS_PORT: addon.ingress_port, + ATTR_INGRESS_PANEL: addon.ingress_panel, } @api_process @@ -251,6 +254,9 @@ class APIAddons(CoreSysAttributes): addon.audio_input = body[ATTR_AUDIO_INPUT] if ATTR_AUDIO_OUTPUT in body: addon.audio_output = body[ATTR_AUDIO_OUTPUT] + if ATTR_INGRESS_PANEL in body: + addon.ingress_panel = body[ATTR_INGRESS_PANEL] + await self.sys_ingress.update_hass_panel(addon) addon.save_data() diff --git a/hassio/api/ingress.py b/hassio/api/ingress.py index 438fa81d9..ac9ae289a 100644 --- a/hassio/api/ingress.py +++ b/hassio/api/ingress.py @@ -14,7 +14,17 @@ from aiohttp.web_exceptions import ( from multidict import CIMultiDict, istr from ..addons.addon import Addon -from ..const import ATTR_SESSION, HEADER_TOKEN, REQUEST_FROM, COOKIE_INGRESS +from ..const import ( + ATTR_ADMIN, + ATTR_ICON, + ATTR_SESSION, + ATTR_TITLE, + ATTR_PANELS, + ATTR_ENABLE, + COOKIE_INGRESS, + HEADER_TOKEN, + REQUEST_FROM, +) from ..coresys import CoreSysAttributes from .utils import api_process @@ -45,6 +55,20 @@ class APIIngress(CoreSysAttributes): """Create URL to container.""" return f"http://{addon.ip_address}:{addon.ingress_port}/{path}" + @api_process + async def panels(self, request: web.Request) -> Dict[str, Any]: + """Create a list of panel data.""" + addons = {} + for addon in self.sys_ingress.addons: + addons[addon.slug] = { + ATTR_TITLE: addon.ingress_title, + ATTR_ICON: addon.ingress_icon, + ATTR_ADMIN: addon.ingress_admin, + ATTR_ENABLE: addon.ingress_panel, + } + + return {ATTR_PANELS: addons} + @api_process async def create_session(self, request: web.Request) -> Dict[str, Any]: """Create a new session.""" diff --git a/hassio/api/panel/chunk.083a9da4ddb0a000aec4.js b/hassio/api/panel/chunk.083a9da4ddb0a000aec4.js deleted file mode 100644 index f21bfdd1e..000000000 --- a/hassio/api/panel/chunk.083a9da4ddb0a000aec4.js +++ /dev/null @@ -1 +0,0 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[12],{131:function(e,n,t){"use strict";t.r(n);t(94),t(108),t(41),t(25);var o=t(4),a=t(9),i=(t(116),t(15),t(14),t(127),t(128),t(117),t(26),t(30));function r(e){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function s(){var e=function(e,n){n||(n=e.slice(0));return Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(n)}}))}(['\n \n \n
\n \n\n \n \n \n \n \n \n \n \n \n \n
\n
\n Save\n
\n
\n '],['\n \n \n
\n \n\n \n \n \n \n \n \n \n \n \n \n
\n
\n Save\n
\n
\n ']);return s=function(){return e},e}function c(e,n){for(var t=0;t\n :host {\n display: block;\n }\n paper-card {\n display: block;\n }\n .card-actions {\n @apply --layout;\n @apply --layout-justified;\n }\n .errors {\n color: var(--google-red-500);\n margin-bottom: 16px;\n }\n iron-autogrow-textarea {\n width: 100%;\n font-family: monospace;\n }\n .syntaxerror {\n color: var(--google-red-500);\n }\n \n \n
\n \n \n
\n
\n Reset to defaults\n Save\n
\n
\n ']);return f=function(){return e},e}function b(e,n){for(var t=0;t bit more top margin */\n font-weight: 500;\n overflow: hidden;\n text-transform: uppercase;\n text-overflow: ellipsis;\n transition: background-color 0.3s ease-in-out;\n text-transform: var(--ha-label-badge-label-text-transform, uppercase);\n }\n .label-badge .label.big span {\n font-size: 90%;\n padding: 10% 12% 7% 12%; /* push smaller text a bit down to center vertically */\n }\n .badge-container .title {\n margin-top: 1em;\n font-size: var(--ha-label-badge-title-font-size, 0.9em);\n width: var(--ha-label-badge-title-width, 5em);\n font-weight: var(--ha-label-badge-title-font-weight, 400);\n overflow: hidden;\n text-overflow: ellipsis;\n line-height: normal;\n }\n "]);return E=function(){return e},e}function R(){var e=M(['\n
',"
\n "]);return R=function(){return e},e}function z(){var e=M(['\n \n ',"\n \n "]);return z=function(){return e},e}function D(){var e=M(["\n ","\n "]);return D=function(){return e},e}function H(){var e=M(['\n \n ']);return H=function(){return e},e}function N(){var e=M(['\n
\n
\n \n ',"\n ","\n
\n ","\n
\n ","\n \n "]);return N=function(){return e},e}function M(e,n){return n||(n=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(n)}}))}function U(e,n){for(var t=0;t4)}),!this.icon||this.value||this.image?"":Object(w.e)(H(),this.icon),this.value&&!this.image?Object(w.e)(D(),this.value):"",this.label?Object(w.e)(z(),Object(O.a)({label:!0,big:this.label.length>5}),this.label):"",this.description?Object(w.e)(R(),this.description):"")}},{key:"updated",value:function(e){$(q(n.prototype),"updated",this).call(this,e),e.has("image")&&(this.shadowRoot.getElementById("badge").style.backgroundImage=this.image?"url(".concat(this.image,")"):"")}}])&&U(t.prototype,o),a&&U(t,a),n}();customElements.define("ha-label-badge",W);t(70);var J=t(42),X=t(100);t(35);function F(e){return(F="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function G(){var e=function(e,n){n||(n=e.slice(0));return Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(n)}}))}(['\n \n\n \n\n \n
\n
\n [[addon.name]]\n
\n \n \n
\n
\n
\n [[addon.description]].
\n Visit\n [[addon.name]] page for\n details.\n
\n \n \n
\n

Addon Security Rating

\n
\n Hass.io provides a security rating to each of the add-ons, which indicates the risks involved when using this add-on. The more access an addon requires on your system, the lower the score, thus raising the possible security risks.\n
\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n \n \n Open web UI\n \n \n \n
\n
\n \n ']);return G=function(){return e},e}function V(e,n){for(var t=0;t4?"green":e>2?"yellow":"red"}},{key:"startOnBootToggled",value:function(){var e={boot:"auto"===this.addon.boot?"manual":"auto"};this.hass.callApi("POST","hassio/addons/".concat(this.addonSlug,"/options"),e)}},{key:"autoUpdateToggled",value:function(){var e={auto_update:!this.addon.auto_update};this.hass.callApi("POST","hassio/addons/".concat(this.addonSlug,"/options"),e)}},{key:"protectionToggled",value:function(){var e={protected:!this.addon.protected};this.hass.callApi("POST","hassio/addons/".concat(this.addonSlug,"/security"),e),this.set("addon.protected",!this.addon.protected)}},{key:"showMoreInfo",value:function(e){var n=e.target.getAttribute("id");Object(X.a)(this,{title:Z[n].title,content:Z[n].description})}},{key:"openChangelog",value:function(){var e=this;this.hass.callApi("get","hassio/addons/".concat(this.addonSlug,"/changelog")).then(function(e){return e},function(){return"Error getting changelog"}).then(function(n){Object(X.a)(e,{title:"Changelog",content:n})})}},{key:"_unistallClicked",value:function(){var e=this;if(confirm("Are you sure you want to uninstall this add-on?")){var n="hassio/addons/".concat(this.addonSlug,"/uninstall"),t={path:n};this.hass.callApi("post",n).then(function(e){t.success=!0,t.response=e},function(e){t.success=!1,t.response=e}).then(function(){e.fire("hass-api-called",t)})}}}])&&V(t.prototype,r),s&&V(t,s),n}();customElements.define("hassio-addon-info",ee);var ne=t(101);function te(e){return(te="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function oe(){var e=function(e,n){n||(n=e.slice(0));return Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(n)}}))}(['\n \n ','\n \n
\n
\n Refresh\n
\n
\n ']);return oe=function(){return e},e}function ae(e,n){for(var t=0;t\n :host {\n display: block;\n }\n paper-card {\n display: block;\n }\n .errors {\n color: var(--google-red-500);\n margin-bottom: 16px;\n }\n .card-actions {\n @apply --layout;\n @apply --layout-justified;\n }\n \n \n
\n \n\n \n \n \n \n \n \n \n \n \n
ContainerHostDescription
\n
\n
\n Reset to defaults\n Save\n
\n
\n ']);return de=function(){return e},e}function ue(e,n){for(var t=0;t\n :host {\n color: var(--primary-text-color);\n --paper-card-header-color: var(--primary-text-color);\n }\n .content {\n padding: 24px 0 32px;\n display: flex;\n flex-direction: column;\n align-items: center;\n }\n hassio-addon-info,\n hassio-addon-network,\n hassio-addon-audio,\n hassio-addon-config {\n margin-bottom: 24px;\n width: 600px;\n }\n hassio-addon-logs {\n max-width: calc(100% - 8px);\n min-width: 600px;\n }\n @media only screen and (max-width: 600px) {\n hassio-addon-info,\n hassio-addon-network,\n hassio-addon-audio,\n hassio-addon-config,\n hassio-addon-logs {\n max-width: 100%;\n min-width: 100%;\n }\n }\n \n \n
\n \n\n \n
\n
\n ']);return ge=function(){return e},e}function ye(e,n){for(var t=0;t=0?n:null}:null}),e._resize();for(var n=document.createTreeWalker(e,1,null,!1);n.nextNode();){var t=n.currentNode;"A"===t.tagName&&t.host!==document.location.host?t.target="_blank":"IMG"===t.tagName&&t.addEventListener("load",e._resize)}}else 2===e._scriptLoaded&&(e.innerText=e.content)}))}}])&&r(i.prototype,h),f&&r(i,f),n}();customElements.define("ha-markdown",h)}}]); \ No newline at end of file diff --git a/hassio/api/panel/chunk.083a9da4ddb0a000aec4.js.gz b/hassio/api/panel/chunk.083a9da4ddb0a000aec4.js.gz deleted file mode 100644 index a088ef2b6..000000000 Binary files a/hassio/api/panel/chunk.083a9da4ddb0a000aec4.js.gz and /dev/null differ diff --git a/hassio/api/panel/chunk.61f4e5888ff9846fe4b8.js b/hassio/api/panel/chunk.61f4e5888ff9846fe4b8.js new file mode 100644 index 000000000..0f4c58c44 --- /dev/null +++ b/hassio/api/panel/chunk.61f4e5888ff9846fe4b8.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[12],{131:function(e,n,t){"use strict";t.r(n);t(94),t(108),t(41),t(25);var o=t(4),a=t(9),i=(t(116),t(15),t(14),t(127),t(128),t(117),t(26),t(30));function r(e){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function s(){var e=function(e,n){n||(n=e.slice(0));return Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(n)}}))}(['\n \n \n
\n \n\n \n \n \n \n \n \n \n \n \n \n
\n
\n Save\n
\n
\n '],['\n \n \n
\n \n\n \n \n \n \n \n \n \n \n \n \n
\n
\n Save\n
\n
\n ']);return s=function(){return e},e}function c(e,n){for(var t=0;t\n :host {\n display: block;\n }\n paper-card {\n display: block;\n }\n .card-actions {\n @apply --layout;\n @apply --layout-justified;\n }\n .errors {\n color: var(--google-red-500);\n margin-bottom: 16px;\n }\n iron-autogrow-textarea {\n width: 100%;\n font-family: monospace;\n }\n .syntaxerror {\n color: var(--google-red-500);\n }\n \n \n
\n \n \n
\n
\n Reset to defaults\n Save\n
\n
\n ']);return f=function(){return e},e}function b(e,n){for(var t=0;t bit more top margin */\n font-weight: 500;\n overflow: hidden;\n text-transform: uppercase;\n text-overflow: ellipsis;\n transition: background-color 0.3s ease-in-out;\n text-transform: var(--ha-label-badge-label-text-transform, uppercase);\n }\n .label-badge .label.big span {\n font-size: 90%;\n padding: 10% 12% 7% 12%; /* push smaller text a bit down to center vertically */\n }\n .badge-container .title {\n margin-top: 1em;\n font-size: var(--ha-label-badge-title-font-size, 0.9em);\n width: var(--ha-label-badge-title-width, 5em);\n font-weight: var(--ha-label-badge-title-font-weight, 400);\n overflow: hidden;\n text-overflow: ellipsis;\n line-height: normal;\n }\n "]);return E=function(){return e},e}function R(){var e=M(['\n
',"
\n "]);return R=function(){return e},e}function z(){var e=M(['\n \n ',"\n \n "]);return z=function(){return e},e}function D(){var e=M(["\n ","\n "]);return D=function(){return e},e}function H(){var e=M(['\n \n ']);return H=function(){return e},e}function N(){var e=M(['\n
\n
\n \n ',"\n ","\n
\n ","\n
\n ","\n \n "]);return N=function(){return e},e}function M(e,n){return n||(n=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(n)}}))}function U(e,n){for(var t=0;t4)}),!this.icon||this.value||this.image?"":Object(w.e)(H(),this.icon),this.value&&!this.image?Object(w.e)(D(),this.value):"",this.label?Object(w.e)(z(),Object(O.a)({label:!0,big:this.label.length>5}),this.label):"",this.description?Object(w.e)(R(),this.description):"")}},{key:"updated",value:function(e){$(q(n.prototype),"updated",this).call(this,e),e.has("image")&&(this.shadowRoot.getElementById("badge").style.backgroundImage=this.image?"url(".concat(this.image,")"):"")}}])&&U(t.prototype,o),a&&U(t,a),n}();customElements.define("ha-label-badge",W);t(70);var J=t(42),X=t(100);t(35);function F(e){return(F="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function G(){var e=function(e,n){n||(n=e.slice(0));return Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(n)}}))}(['\n \n\n \n\n \n
\n
\n [[addon.name]]\n
\n \n \n
\n
\n
\n [[addon.description]].
\n Visit\n [[addon.name]] page for\n details.\n
\n \n \n
\n

Add-on Security Rating

\n
\n Hass.io provides a security rating to each of the add-ons, which indicates the risks involved when using this add-on. The more access an add-on requires on your system, the lower the score, thus raising the possible security risks.\n
\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n \n \n Open web UI\n \n \n \n
\n
\n \n ']);return G=function(){return e},e}function V(e,n){for(var t=0;t4?"green":e>2?"yellow":"red"}},{key:"startOnBootToggled",value:function(){var e={boot:"auto"===this.addon.boot?"manual":"auto"};this.hass.callApi("POST","hassio/addons/".concat(this.addonSlug,"/options"),e)}},{key:"autoUpdateToggled",value:function(){var e={auto_update:!this.addon.auto_update};this.hass.callApi("POST","hassio/addons/".concat(this.addonSlug,"/options"),e)}},{key:"protectionToggled",value:function(){var e={protected:!this.addon.protected};this.hass.callApi("POST","hassio/addons/".concat(this.addonSlug,"/security"),e),this.set("addon.protected",!this.addon.protected)}},{key:"panelToggled",value:function(){var e={ingress_panel:!this.addon.ingress_panel};this.hass.callApi("POST","hassio/addons/".concat(this.addonSlug,"/options"),e)}},{key:"showMoreInfo",value:function(e){var n=e.target.getAttribute("id");Object(X.a)(this,{title:Z[n].title,content:Z[n].description})}},{key:"openChangelog",value:function(){var e=this;this.hass.callApi("get","hassio/addons/".concat(this.addonSlug,"/changelog")).then(function(e){return e},function(){return"Error getting changelog"}).then(function(n){Object(X.a)(e,{title:"Changelog",content:n})})}},{key:"_unistallClicked",value:function(){var e=this;if(confirm("Are you sure you want to uninstall this add-on?")){var n="hassio/addons/".concat(this.addonSlug,"/uninstall"),t={path:n};this.hass.callApi("post",n).then(function(e){t.success=!0,t.response=e},function(e){t.success=!1,t.response=e}).then(function(){e.fire("hass-api-called",t)})}}}])&&V(t.prototype,r),s&&V(t,s),n}();customElements.define("hassio-addon-info",ee);var ne=t(101);function te(e){return(te="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function oe(){var e=function(e,n){n||(n=e.slice(0));return Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(n)}}))}(['\n \n ','\n \n
\n
\n Refresh\n
\n
\n ']);return oe=function(){return e},e}function ae(e,n){for(var t=0;t\n :host {\n display: block;\n }\n paper-card {\n display: block;\n }\n .errors {\n color: var(--google-red-500);\n margin-bottom: 16px;\n }\n .card-actions {\n @apply --layout;\n @apply --layout-justified;\n }\n \n \n
\n \n\n \n \n \n \n \n \n \n \n \n
ContainerHostDescription
\n
\n
\n Reset to defaults\n Save\n
\n
\n ']);return de=function(){return e},e}function ue(e,n){for(var t=0;t\n :host {\n color: var(--primary-text-color);\n --paper-card-header-color: var(--primary-text-color);\n }\n .content {\n padding: 24px 0 32px;\n display: flex;\n flex-direction: column;\n align-items: center;\n }\n hassio-addon-info,\n hassio-addon-network,\n hassio-addon-audio,\n hassio-addon-config {\n margin-bottom: 24px;\n width: 600px;\n }\n hassio-addon-logs {\n max-width: calc(100% - 8px);\n min-width: 600px;\n }\n @media only screen and (max-width: 600px) {\n hassio-addon-info,\n hassio-addon-network,\n hassio-addon-audio,\n hassio-addon-config,\n hassio-addon-logs {\n max-width: 100%;\n min-width: 100%;\n }\n }\n \n \n
\n \n\n \n
\n
\n ']);return ge=function(){return e},e}function ye(e,n){for(var t=0;t=0?n:null}:null}),e._resize();for(var n=document.createTreeWalker(e,1,null,!1);n.nextNode();){var t=n.currentNode;"A"===t.tagName&&t.host!==document.location.host?t.target="_blank":"IMG"===t.tagName&&t.addEventListener("load",e._resize)}}else 2===e._scriptLoaded&&(e.innerText=e.content)}))}}])&&r(i.prototype,h),f&&r(i,f),n}();customElements.define("ha-markdown",h)}}]); \ No newline at end of file diff --git a/hassio/api/panel/chunk.61f4e5888ff9846fe4b8.js.gz b/hassio/api/panel/chunk.61f4e5888ff9846fe4b8.js.gz new file mode 100644 index 000000000..d4513f4d3 Binary files /dev/null and b/hassio/api/panel/chunk.61f4e5888ff9846fe4b8.js.gz differ diff --git a/hassio/api/panel/entrypoint.js b/hassio/api/panel/entrypoint.js index 3bf29ca34..f656416b2 100644 --- a/hassio/api/panel/entrypoint.js +++ b/hassio/api/panel/entrypoint.js @@ -1 +1 @@ -!function(e){function n(n){for(var t,o,a=n[0],i=n[1],c=0,u=[];c List[Addon]: + """Return list of ingress Add-ons.""" + addons = [] + for addon in self.sys_addons.list_installed: + if not addon.with_ingress: + continue + addons.append(addon) + return addons + async def load(self) -> None: """Update internal data.""" self._update_token_list() @@ -77,9 +87,7 @@ class Ingress(JsonConfig, CoreSysAttributes): self.tokens.clear() # Read all ingress token and build a map - for addon in self.sys_addons.list_installed: - if not addon.with_ingress: - continue + for addon in self.addons: self.tokens[addon.ingress_token] = addon.slug def create_session(self) -> str: @@ -118,3 +126,12 @@ class Ingress(JsonConfig, CoreSysAttributes): self.ports[addon_slug] = port self.save_data() return port + + async def update_hass_panel(self, addon: Addon): + """Return True if Home Assistant up and running.""" + method = "post" if addon.ingress_panel else "delete" + async with self.sys_homeassistant.make_request(method, f"api/hassio_push/panel/{addon.slug}") as resp: + if resp.status in (200, 201): + _LOGGER.info("Update Ingress as panel for %s", addon.slug) + else: + _LOGGER.warning("Fails Ingress panel for %s with %i", addon.slug, resp.status) diff --git a/hassio/misc/hardware.py b/hassio/misc/hardware.py index 0183fb0fe..034a8d6e2 100644 --- a/hassio/misc/hardware.py +++ b/hassio/misc/hardware.py @@ -70,7 +70,7 @@ class Hardware: def audio_devices(self): """Return all available audio interfaces.""" if not ASOUND_CARDS.exists(): - _LOGGER.info("No audio devices found") + _LOGGER.debug("No audio devices found") return {} try: diff --git a/home-assistant-polymer b/home-assistant-polymer index bbae3291e..ad40d9927 160000 --- a/home-assistant-polymer +++ b/home-assistant-polymer @@ -1 +1 @@ -Subproject commit bbae3291e1187e764088bdbbc28e95d3e20382d1 +Subproject commit ad40d9927ba40927302b1256ad0cf2f3f3858903