mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-25 18:16:32 +00:00
commit
ff903d7b5a
3
API.md
3
API.md
@ -52,7 +52,7 @@ The addons from `addons` are only installed one.
|
|||||||
"slug": "xy",
|
"slug": "xy",
|
||||||
"description": "description",
|
"description": "description",
|
||||||
"repository": "12345678|null",
|
"repository": "12345678|null",
|
||||||
"version": "LAST_VERSION",
|
"version": "LATEST_VERSION",
|
||||||
"installed": "INSTALL_VERSION",
|
"installed": "INSTALL_VERSION",
|
||||||
"icon": "bool",
|
"icon": "bool",
|
||||||
"logo": "bool",
|
"logo": "bool",
|
||||||
@ -537,6 +537,7 @@ Get all available addons.
|
|||||||
"kernel_modules": "bool",
|
"kernel_modules": "bool",
|
||||||
"devicetree": "bool",
|
"devicetree": "bool",
|
||||||
"docker_api": "bool",
|
"docker_api": "bool",
|
||||||
|
"video": "bool",
|
||||||
"audio": "bool",
|
"audio": "bool",
|
||||||
"audio_input": "null|0,0",
|
"audio_input": "null|0,0",
|
||||||
"audio_output": "null|0,0",
|
"audio_output": "null|0,0",
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
trigger:
|
trigger:
|
||||||
batch: true
|
batch: true
|
||||||
branches:
|
branches:
|
||||||
exclude:
|
include:
|
||||||
- "*"
|
- dev
|
||||||
tags:
|
tags:
|
||||||
include:
|
include:
|
||||||
- "*"
|
- "*"
|
||||||
|
@ -57,6 +57,7 @@ from ..const import (
|
|||||||
ATTR_UDEV,
|
ATTR_UDEV,
|
||||||
ATTR_URL,
|
ATTR_URL,
|
||||||
ATTR_VERSION,
|
ATTR_VERSION,
|
||||||
|
ATTR_VIDEO,
|
||||||
ATTR_WEBUI,
|
ATTR_WEBUI,
|
||||||
SECURITY_DEFAULT,
|
SECURITY_DEFAULT,
|
||||||
SECURITY_DISABLE,
|
SECURITY_DISABLE,
|
||||||
@ -393,6 +394,11 @@ class AddonModel(CoreSysAttributes):
|
|||||||
"""Return True if the add-on access to audio."""
|
"""Return True if the add-on access to audio."""
|
||||||
return self.data[ATTR_AUDIO]
|
return self.data[ATTR_AUDIO]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def with_video(self) -> bool:
|
||||||
|
"""Return True if the add-on access to video."""
|
||||||
|
return self.data[ATTR_VIDEO]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def homeassistant_version(self) -> Optional[str]:
|
def homeassistant_version(self) -> Optional[str]:
|
||||||
"""Return min Home Assistant version they needed by Add-on."""
|
"""Return min Home Assistant version they needed by Add-on."""
|
||||||
|
@ -77,6 +77,7 @@ from ..const import (
|
|||||||
ATTR_USER,
|
ATTR_USER,
|
||||||
ATTR_UUID,
|
ATTR_UUID,
|
||||||
ATTR_VERSION,
|
ATTR_VERSION,
|
||||||
|
ATTR_VIDEO,
|
||||||
ATTR_WEBUI,
|
ATTR_WEBUI,
|
||||||
BOOT_AUTO,
|
BOOT_AUTO,
|
||||||
BOOT_MANUAL,
|
BOOT_MANUAL,
|
||||||
@ -219,6 +220,7 @@ SCHEMA_ADDON_CONFIG = vol.Schema(
|
|||||||
vol.Optional(ATTR_APPARMOR, default=True): vol.Boolean(),
|
vol.Optional(ATTR_APPARMOR, default=True): vol.Boolean(),
|
||||||
vol.Optional(ATTR_FULL_ACCESS, default=False): vol.Boolean(),
|
vol.Optional(ATTR_FULL_ACCESS, default=False): vol.Boolean(),
|
||||||
vol.Optional(ATTR_AUDIO, default=False): vol.Boolean(),
|
vol.Optional(ATTR_AUDIO, default=False): vol.Boolean(),
|
||||||
|
vol.Optional(ATTR_VIDEO, default=False): vol.Boolean(),
|
||||||
vol.Optional(ATTR_GPIO, default=False): vol.Boolean(),
|
vol.Optional(ATTR_GPIO, default=False): vol.Boolean(),
|
||||||
vol.Optional(ATTR_DEVICETREE, default=False): vol.Boolean(),
|
vol.Optional(ATTR_DEVICETREE, default=False): vol.Boolean(),
|
||||||
vol.Optional(ATTR_KERNEL_MODULES, default=False): vol.Boolean(),
|
vol.Optional(ATTR_KERNEL_MODULES, default=False): vol.Boolean(),
|
||||||
|
@ -82,6 +82,7 @@ from ..const import (
|
|||||||
ATTR_UDEV,
|
ATTR_UDEV,
|
||||||
ATTR_URL,
|
ATTR_URL,
|
||||||
ATTR_VERSION,
|
ATTR_VERSION,
|
||||||
|
ATTR_VIDEO,
|
||||||
ATTR_WEBUI,
|
ATTR_WEBUI,
|
||||||
BOOT_AUTO,
|
BOOT_AUTO,
|
||||||
BOOT_MANUAL,
|
BOOT_MANUAL,
|
||||||
@ -238,6 +239,7 @@ class APIAddons(CoreSysAttributes):
|
|||||||
ATTR_DEVICETREE: addon.with_devicetree,
|
ATTR_DEVICETREE: addon.with_devicetree,
|
||||||
ATTR_UDEV: addon.with_udev,
|
ATTR_UDEV: addon.with_udev,
|
||||||
ATTR_DOCKER_API: addon.access_docker_api,
|
ATTR_DOCKER_API: addon.access_docker_api,
|
||||||
|
ATTR_VIDEO: addon.with_video,
|
||||||
ATTR_AUDIO: addon.with_audio,
|
ATTR_AUDIO: addon.with_audio,
|
||||||
ATTR_AUDIO_INPUT: None,
|
ATTR_AUDIO_INPUT: None,
|
||||||
ATTR_AUDIO_OUTPUT: None,
|
ATTR_AUDIO_OUTPUT: None,
|
||||||
|
@ -25,18 +25,21 @@ _LOGGER: logging.Logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
def excract_supervisor_token(request: web.Request) -> Optional[str]:
|
def excract_supervisor_token(request: web.Request) -> Optional[str]:
|
||||||
"""Extract Supervisor token from request."""
|
"""Extract Supervisor token from request."""
|
||||||
|
supervisor_token = request.headers.get(HEADER_TOKEN)
|
||||||
|
if supervisor_token:
|
||||||
|
return supervisor_token
|
||||||
|
|
||||||
|
# Remove with old Hass.io fallback
|
||||||
|
supervisor_token = request.headers.get(HEADER_TOKEN_OLD)
|
||||||
|
if supervisor_token:
|
||||||
|
return supervisor_token
|
||||||
|
|
||||||
|
# API access only
|
||||||
supervisor_token = request.headers.get(AUTHORIZATION)
|
supervisor_token = request.headers.get(AUTHORIZATION)
|
||||||
if supervisor_token:
|
if supervisor_token:
|
||||||
return supervisor_token.split(" ")[-1]
|
return supervisor_token.split(" ")[-1]
|
||||||
|
|
||||||
# Header token handling
|
return None
|
||||||
supervisor_token = request.headers.get(HEADER_TOKEN)
|
|
||||||
|
|
||||||
# Remove with old Hass.io fallback
|
|
||||||
if not supervisor_token:
|
|
||||||
supervisor_token = request.headers.get(HEADER_TOKEN_OLD)
|
|
||||||
|
|
||||||
return supervisor_token
|
|
||||||
|
|
||||||
|
|
||||||
def json_loads(data: Any) -> Dict[str, Any]:
|
def json_loads(data: Any) -> Dict[str, Any]:
|
||||||
|
@ -3,7 +3,7 @@ from enum import Enum
|
|||||||
from ipaddress import ip_network
|
from ipaddress import ip_network
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
HASSIO_VERSION = "198"
|
HASSIO_VERSION = "199"
|
||||||
|
|
||||||
|
|
||||||
URL_HASSIO_ADDONS = "https://github.com/home-assistant/hassio-addons"
|
URL_HASSIO_ADDONS = "https://github.com/home-assistant/hassio-addons"
|
||||||
@ -149,6 +149,7 @@ ATTR_TYPE = "type"
|
|||||||
ATTR_TIMEOUT = "timeout"
|
ATTR_TIMEOUT = "timeout"
|
||||||
ATTR_AUTO_UPDATE = "auto_update"
|
ATTR_AUTO_UPDATE = "auto_update"
|
||||||
ATTR_CUSTOM = "custom"
|
ATTR_CUSTOM = "custom"
|
||||||
|
ATTR_VIDEO = "video"
|
||||||
ATTR_AUDIO = "audio"
|
ATTR_AUDIO = "audio"
|
||||||
ATTR_AUDIO_INPUT = "audio_input"
|
ATTR_AUDIO_INPUT = "audio_input"
|
||||||
ATTR_AUDIO_OUTPUT = "audio_output"
|
ATTR_AUDIO_OUTPUT = "audio_output"
|
||||||
|
@ -147,6 +147,11 @@ class DockerAddon(DockerInterface):
|
|||||||
for device in serial_devs:
|
for device in serial_devs:
|
||||||
devices.append(f"{device}:{device}:rwm")
|
devices.append(f"{device}:{device}:rwm")
|
||||||
|
|
||||||
|
# Use video devices
|
||||||
|
if self.addon.with_video:
|
||||||
|
for device in self.sys_hardware.video_devices:
|
||||||
|
devices.append(f"{device.path!s}:{device.path!s}:rwm")
|
||||||
|
|
||||||
# Return None if no devices is present
|
# Return None if no devices is present
|
||||||
return devices or None
|
return devices or None
|
||||||
|
|
||||||
|
@ -243,10 +243,14 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
_LOGGER.warning("Fails install landingpage, retry after 30sec")
|
_LOGGER.warning("Fails install landingpage, retry after 30sec")
|
||||||
await asyncio.sleep(30)
|
await asyncio.sleep(30)
|
||||||
else:
|
else:
|
||||||
|
self.version = self.instance.version
|
||||||
|
self.save_data()
|
||||||
break
|
break
|
||||||
|
|
||||||
self.version = self.instance.version
|
# Start landingpage
|
||||||
self.save_data()
|
_LOGGER.info("Start HomeAssistant landingpage")
|
||||||
|
with suppress(HomeAssistantError):
|
||||||
|
await self._start()
|
||||||
|
|
||||||
@process_lock
|
@process_lock
|
||||||
async def install(self) -> None:
|
async def install(self) -> None:
|
||||||
|
@ -4,13 +4,15 @@ from datetime import datetime
|
|||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import re
|
import re
|
||||||
from typing import Any, Dict, Optional, Set
|
from typing import Any, Dict, List, Optional, Set
|
||||||
|
|
||||||
|
import attr
|
||||||
import pyudev
|
import pyudev
|
||||||
|
|
||||||
from ..const import ATTR_DEVICES, ATTR_NAME, ATTR_TYPE, CHAN_ID, CHAN_TYPE
|
from ..const import ATTR_DEVICES, ATTR_NAME, ATTR_TYPE, CHAN_ID, CHAN_TYPE
|
||||||
from ..exceptions import HardwareNotSupportedError
|
from ..exceptions import HardwareNotSupportedError
|
||||||
|
|
||||||
|
|
||||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
ASOUND_CARDS: Path = Path("/proc/asound/cards")
|
ASOUND_CARDS: Path = Path("/proc/asound/cards")
|
||||||
@ -26,6 +28,17 @@ GPIO_DEVICES: Path = Path("/sys/class/gpio")
|
|||||||
SOC_DEVICES: Path = Path("/sys/devices/platform/soc")
|
SOC_DEVICES: Path = Path("/sys/devices/platform/soc")
|
||||||
RE_TTY: re.Pattern = re.compile(r"tty[A-Z]+")
|
RE_TTY: re.Pattern = re.compile(r"tty[A-Z]+")
|
||||||
|
|
||||||
|
RE_VIDEO_DEVICES = re.compile(r"^(?:vchiq|cec)")
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(frozen=True)
|
||||||
|
class Device:
|
||||||
|
"""Represent a device."""
|
||||||
|
|
||||||
|
name: str = attr.ib()
|
||||||
|
path: Path = attr.ib()
|
||||||
|
links: List[Path] = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
class Hardware:
|
class Hardware:
|
||||||
"""Representation of an interface to procfs, sysfs and udev."""
|
"""Representation of an interface to procfs, sysfs and udev."""
|
||||||
@ -34,6 +47,33 @@ class Hardware:
|
|||||||
"""Init hardware object."""
|
"""Init hardware object."""
|
||||||
self.context = pyudev.Context()
|
self.context = pyudev.Context()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def devices(self) -> List[Device]:
|
||||||
|
"""Return a list of all available devices."""
|
||||||
|
dev_list: List[Device] = []
|
||||||
|
|
||||||
|
# Exctract all devices
|
||||||
|
for device in self.context.list_devices():
|
||||||
|
dev_list.append(
|
||||||
|
Device(device.sys_name),
|
||||||
|
Path(device.device_node),
|
||||||
|
[Path(node) for node in device.device_links],
|
||||||
|
)
|
||||||
|
|
||||||
|
return dev_list
|
||||||
|
|
||||||
|
@property
|
||||||
|
def video_devices(self) -> List[Device]:
|
||||||
|
"""Return all available video devices."""
|
||||||
|
dev_list: List[Device] = []
|
||||||
|
|
||||||
|
for device in self.devices:
|
||||||
|
if not RE_VIDEO_DEVICES.match(device.name):
|
||||||
|
continue
|
||||||
|
dev_list.append(device)
|
||||||
|
|
||||||
|
return dev_list
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serial_devices(self) -> Set[str]:
|
def serial_devices(self) -> Set[str]:
|
||||||
"""Return all serial and connected devices."""
|
"""Return all serial and connected devices."""
|
||||||
|
@ -75,6 +75,7 @@ function install_cli() {
|
|||||||
function setup_test_env() {
|
function setup_test_env() {
|
||||||
mkdir -p /workspaces/test_hassio
|
mkdir -p /workspaces/test_hassio
|
||||||
|
|
||||||
|
echo "Start Supervisor"
|
||||||
docker run --rm --privileged \
|
docker run --rm --privileged \
|
||||||
--name hassio_supervisor \
|
--name hassio_supervisor \
|
||||||
--security-opt seccomp=unconfined \
|
--security-opt seccomp=unconfined \
|
||||||
@ -88,6 +89,10 @@ function setup_test_env() {
|
|||||||
-e SUPERVISOR_DEV=1 \
|
-e SUPERVISOR_DEV=1 \
|
||||||
-e HOMEASSISTANT_REPOSITORY="homeassistant/qemux86-64-homeassistant" \
|
-e HOMEASSISTANT_REPOSITORY="homeassistant/qemux86-64-homeassistant" \
|
||||||
homeassistant/amd64-hassio-supervisor:latest
|
homeassistant/amd64-hassio-supervisor:latest
|
||||||
|
|
||||||
|
if docker rm homeassistant 2> /dev/null; then
|
||||||
|
echo "Cleanup HomeAssistant instance"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "Start Test-Env"
|
echo "Start Test-Env"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user