mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-06-25 11:26:30 +00:00
Ingress panel support (#1047)
* Ingress Panel support * Fix lists * Allow to set the value * fix panels * Update ha realtime * Fix url * Fix update
This commit is contained in:
parent
e10fe16f21
commit
decf254e5f
23
API.md
23
API.md
@ -516,7 +516,8 @@ Get all available addons.
|
|||||||
"ingress": "bool",
|
"ingress": "bool",
|
||||||
"ingress_entry": "null|/api/hassio_ingress/slug",
|
"ingress_entry": "null|/api/hassio_ingress/slug",
|
||||||
"ingress_url": "null|/api/hassio_ingress/slug/entry.html",
|
"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": {},
|
"options": {},
|
||||||
"audio_output": "null|0,0",
|
"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}`
|
- VIEW `/ingress/{token}`
|
||||||
|
|
||||||
Ingress WebUI for this Add-on. The addon need support HASS Auth!
|
Ingress WebUI for this Add-on. The addon need support HASS Auth!
|
||||||
|
@ -47,6 +47,10 @@ from ..const import (
|
|||||||
ATTR_INGRESS_ENTRY,
|
ATTR_INGRESS_ENTRY,
|
||||||
ATTR_INGRESS_PORT,
|
ATTR_INGRESS_PORT,
|
||||||
ATTR_INGRESS_TOKEN,
|
ATTR_INGRESS_TOKEN,
|
||||||
|
ATTR_INGRESS_PANEL,
|
||||||
|
ATTR_INGRESS_PANEL_ADMIN,
|
||||||
|
ATTR_INGRESS_PANEL_ICON,
|
||||||
|
ATTR_INGRESS_PANEL_TITLE,
|
||||||
ATTR_KERNEL_MODULES,
|
ATTR_KERNEL_MODULES,
|
||||||
ATTR_LEGACY,
|
ATTR_LEGACY,
|
||||||
ATTR_LOCATON,
|
ATTR_LOCATON,
|
||||||
@ -449,6 +453,21 @@ class Addon(CoreSysAttributes):
|
|||||||
return self.sys_ingress.get_dynamic_port(self.slug)
|
return self.sys_ingress.get_dynamic_port(self.slug)
|
||||||
return port
|
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
|
@property
|
||||||
def host_network(self):
|
def host_network(self):
|
||||||
"""Return True if add-on run on host network."""
|
"""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 True if the add-on access support ingress."""
|
||||||
return self._mesh[ATTR_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
|
@property
|
||||||
def with_gpio(self):
|
def with_gpio(self):
|
||||||
"""Return True if the add-on access to GPIO interface."""
|
"""Return True if the add-on access to GPIO interface."""
|
||||||
|
@ -41,6 +41,10 @@ from ..const import (
|
|||||||
ATTR_INGRESS_ENTRY,
|
ATTR_INGRESS_ENTRY,
|
||||||
ATTR_INGRESS_PORT,
|
ATTR_INGRESS_PORT,
|
||||||
ATTR_INGRESS_TOKEN,
|
ATTR_INGRESS_TOKEN,
|
||||||
|
ATTR_INGRESS_PANEL,
|
||||||
|
ATTR_INGRESS_PANEL_ADMIN,
|
||||||
|
ATTR_INGRESS_PANEL_ICON,
|
||||||
|
ATTR_INGRESS_PANEL_TITLE,
|
||||||
ATTR_KERNEL_MODULES,
|
ATTR_KERNEL_MODULES,
|
||||||
ATTR_LEGACY,
|
ATTR_LEGACY,
|
||||||
ATTR_LOCATON,
|
ATTR_LOCATON,
|
||||||
@ -159,6 +163,9 @@ SCHEMA_ADDON_CONFIG = vol.Schema({
|
|||||||
vol.Optional(ATTR_INGRESS, default=False): vol.Boolean(),
|
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_PORT, default=8099): vol.Any(NETWORK_PORT, vol.Equal(0)),
|
||||||
vol.Optional(ATTR_INGRESS_ENTRY): vol.Coerce(str),
|
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_HOMEASSISTANT): vol.Maybe(vol.Coerce(str)),
|
||||||
vol.Optional(ATTR_HOST_NETWORK, default=False): vol.Boolean(),
|
vol.Optional(ATTR_HOST_NETWORK, default=False): vol.Boolean(),
|
||||||
vol.Optional(ATTR_HOST_PID, 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_OUTPUT): ALSA_DEVICE,
|
||||||
vol.Optional(ATTR_AUDIO_INPUT): ALSA_DEVICE,
|
vol.Optional(ATTR_AUDIO_INPUT): ALSA_DEVICE,
|
||||||
vol.Optional(ATTR_PROTECTED, default=True): vol.Boolean(),
|
vol.Optional(ATTR_PROTECTED, default=True): vol.Boolean(),
|
||||||
|
vol.Optional(ATTR_INGRESS_PANEL, default=False): vol.Boolean(),
|
||||||
}, extra=vol.REMOVE_EXTRA)
|
}, extra=vol.REMOVE_EXTRA)
|
||||||
|
|
||||||
|
|
||||||
|
@ -195,6 +195,7 @@ class RestAPI(CoreSysAttributes):
|
|||||||
|
|
||||||
self.webapp.add_routes([
|
self.webapp.add_routes([
|
||||||
web.post('/ingress/session', api_ingress.create_session),
|
web.post('/ingress/session', api_ingress.create_session),
|
||||||
|
web.get('/ingress/panels', api_ingress.panels),
|
||||||
web.view('/ingress/{token}/{path:.*}', api_ingress.handler),
|
web.view('/ingress/{token}/{path:.*}', api_ingress.handler),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@ from ..const import (
|
|||||||
ATTR_INGRESS_ENTRY,
|
ATTR_INGRESS_ENTRY,
|
||||||
ATTR_INGRESS_PORT,
|
ATTR_INGRESS_PORT,
|
||||||
ATTR_INGRESS_URL,
|
ATTR_INGRESS_URL,
|
||||||
|
ATTR_INGRESS_PANEL,
|
||||||
ATTR_INSTALLED,
|
ATTR_INSTALLED,
|
||||||
ATTR_IP_ADDRESS,
|
ATTR_IP_ADDRESS,
|
||||||
ATTR_KERNEL_MODULES,
|
ATTR_KERNEL_MODULES,
|
||||||
@ -100,6 +101,7 @@ SCHEMA_OPTIONS = vol.Schema({
|
|||||||
vol.Optional(ATTR_AUTO_UPDATE): vol.Boolean(),
|
vol.Optional(ATTR_AUTO_UPDATE): vol.Boolean(),
|
||||||
vol.Optional(ATTR_AUDIO_OUTPUT): ALSA_DEVICE,
|
vol.Optional(ATTR_AUDIO_OUTPUT): ALSA_DEVICE,
|
||||||
vol.Optional(ATTR_AUDIO_INPUT): ALSA_DEVICE,
|
vol.Optional(ATTR_AUDIO_INPUT): ALSA_DEVICE,
|
||||||
|
vol.Optional(ATTR_INGRESS_PANEL): vol.Boolean(),
|
||||||
})
|
})
|
||||||
|
|
||||||
# pylint: disable=no-value-for-parameter
|
# pylint: disable=no-value-for-parameter
|
||||||
@ -227,6 +229,7 @@ class APIAddons(CoreSysAttributes):
|
|||||||
ATTR_INGRESS_ENTRY: addon.ingress_entry,
|
ATTR_INGRESS_ENTRY: addon.ingress_entry,
|
||||||
ATTR_INGRESS_URL: addon.ingress_url,
|
ATTR_INGRESS_URL: addon.ingress_url,
|
||||||
ATTR_INGRESS_PORT: addon.ingress_port,
|
ATTR_INGRESS_PORT: addon.ingress_port,
|
||||||
|
ATTR_INGRESS_PANEL: addon.ingress_panel,
|
||||||
}
|
}
|
||||||
|
|
||||||
@api_process
|
@api_process
|
||||||
@ -251,6 +254,9 @@ class APIAddons(CoreSysAttributes):
|
|||||||
addon.audio_input = body[ATTR_AUDIO_INPUT]
|
addon.audio_input = body[ATTR_AUDIO_INPUT]
|
||||||
if ATTR_AUDIO_OUTPUT in body:
|
if ATTR_AUDIO_OUTPUT in body:
|
||||||
addon.audio_output = body[ATTR_AUDIO_OUTPUT]
|
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()
|
addon.save_data()
|
||||||
|
|
||||||
|
@ -14,7 +14,17 @@ from aiohttp.web_exceptions import (
|
|||||||
from multidict import CIMultiDict, istr
|
from multidict import CIMultiDict, istr
|
||||||
|
|
||||||
from ..addons.addon import Addon
|
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 ..coresys import CoreSysAttributes
|
||||||
from .utils import api_process
|
from .utils import api_process
|
||||||
|
|
||||||
@ -45,6 +55,20 @@ class APIIngress(CoreSysAttributes):
|
|||||||
"""Create URL to container."""
|
"""Create URL to container."""
|
||||||
return f"http://{addon.ip_address}:{addon.ingress_port}/{path}"
|
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
|
@api_process
|
||||||
async def create_session(self, request: web.Request) -> Dict[str, Any]:
|
async def create_session(self, request: web.Request) -> Dict[str, Any]:
|
||||||
"""Create a new session."""
|
"""Create a new session."""
|
||||||
|
@ -198,8 +198,16 @@ ATTR_INGRESS_PORT = "ingress_port"
|
|||||||
ATTR_INGRESS_ENTRY = "ingress_entry"
|
ATTR_INGRESS_ENTRY = "ingress_entry"
|
||||||
ATTR_INGRESS_TOKEN = "ingress_token"
|
ATTR_INGRESS_TOKEN = "ingress_token"
|
||||||
ATTR_INGRESS_URL = "ingress_url"
|
ATTR_INGRESS_URL = "ingress_url"
|
||||||
|
ATTR_INGRESS_PANEL = "ingress_panel"
|
||||||
|
ATTR_INGRESS_PANEL_ICON = "ingress_panel_icon"
|
||||||
|
ATTR_INGRESS_PANEL_TITLE = "ingress_panel_title"
|
||||||
|
ATTR_INGRESS_PANEL_ADMIN = "ingress_panel_admin"
|
||||||
|
ATTR_TITLE = "title"
|
||||||
|
ATTR_ENABLE = "enable"
|
||||||
ATTR_IP_ADDRESS = "ip_address"
|
ATTR_IP_ADDRESS = "ip_address"
|
||||||
ATTR_SESSION = "session"
|
ATTR_SESSION = "session"
|
||||||
|
ATTR_ADMIN = "admin"
|
||||||
|
ATTR_PANELS = "panels"
|
||||||
|
|
||||||
PROVIDE_SERVICE = "provide"
|
PROVIDE_SERVICE = "provide"
|
||||||
NEED_SERVICE = "need"
|
NEED_SERVICE = "need"
|
||||||
|
@ -3,7 +3,7 @@ from datetime import timedelta
|
|||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
import secrets
|
import secrets
|
||||||
from typing import Dict, Optional
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
from .addons.addon import Addon
|
from .addons.addon import Addon
|
||||||
from .const import ATTR_PORTS, ATTR_SESSION, FILE_HASSIO_INGRESS
|
from .const import ATTR_PORTS, ATTR_SESSION, FILE_HASSIO_INGRESS
|
||||||
@ -40,6 +40,16 @@ class Ingress(JsonConfig, CoreSysAttributes):
|
|||||||
"""Return list of dynamic ports."""
|
"""Return list of dynamic ports."""
|
||||||
return self._data[ATTR_PORTS]
|
return self._data[ATTR_PORTS]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def addons(self) -> 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:
|
async def load(self) -> None:
|
||||||
"""Update internal data."""
|
"""Update internal data."""
|
||||||
self._update_token_list()
|
self._update_token_list()
|
||||||
@ -77,9 +87,7 @@ class Ingress(JsonConfig, CoreSysAttributes):
|
|||||||
self.tokens.clear()
|
self.tokens.clear()
|
||||||
|
|
||||||
# Read all ingress token and build a map
|
# Read all ingress token and build a map
|
||||||
for addon in self.sys_addons.list_installed:
|
for addon in self.addons:
|
||||||
if not addon.with_ingress:
|
|
||||||
continue
|
|
||||||
self.tokens[addon.ingress_token] = addon.slug
|
self.tokens[addon.ingress_token] = addon.slug
|
||||||
|
|
||||||
def create_session(self) -> str:
|
def create_session(self) -> str:
|
||||||
@ -118,3 +126,12 @@ class Ingress(JsonConfig, CoreSysAttributes):
|
|||||||
self.ports[addon_slug] = port
|
self.ports[addon_slug] = port
|
||||||
self.save_data()
|
self.save_data()
|
||||||
return port
|
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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user