mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-09-26 05:19:29 +00:00
Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2e4b545265 | ||
![]() |
2de1d35dd1 | ||
![]() |
2b082b362d | ||
![]() |
dfdd0d6b4b | ||
![]() |
a00e81c03f | ||
![]() |
776e6bb418 | ||
![]() |
b31fca656e | ||
![]() |
fa783a0d2c | ||
![]() |
96c0fbaf10 | ||
![]() |
24f7801ddc | ||
![]() |
8e83e007e9 | ||
![]() |
d0db466e67 | ||
![]() |
3010bd4eb6 | ||
![]() |
069bed8815 | ||
![]() |
d2088ae5f8 | ||
![]() |
0ca5a241bb | ||
![]() |
dff32a8e84 | ||
![]() |
4a20344652 | ||
![]() |
98b969ef06 | ||
![]() |
c8cb8aecf7 | ||
![]() |
73e8875018 | ||
![]() |
02aed9c084 | ||
![]() |
89148f8fff | ||
![]() |
6bde527f5c |
180
.vscode/tasks.json
vendored
180
.vscode/tasks.json
vendored
@@ -1,92 +1,90 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Run Testenv",
|
||||
"type": "shell",
|
||||
"command": "./scripts/test_env.sh",
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true,
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "new"
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Run Testenv CLI",
|
||||
"type": "shell",
|
||||
"command": "docker run --rm -ti -v /etc/machine-id:/etc/machine-id --network=hassio --add-host hassio:172.30.32.2 homeassistant/amd64-hassio-cli:dev",
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true,
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "new"
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Update UI",
|
||||
"type": "shell",
|
||||
"command": "./scripts/update-frontend.sh",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "new"
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Pytest",
|
||||
"type": "shell",
|
||||
"command": "pytest --timeout=10 tests",
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true,
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "new"
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Flake8",
|
||||
"type": "shell",
|
||||
"command": "flake8 hassio tests",
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true,
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "new"
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Pylint",
|
||||
"type": "shell",
|
||||
"command": "pylint hassio",
|
||||
"dependsOn": [
|
||||
"Install all Requirements"
|
||||
],
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true,
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "new"
|
||||
},
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Run Testenv",
|
||||
"type": "shell",
|
||||
"command": "./scripts/test_env.sh",
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "new"
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Run Testenv CLI",
|
||||
"type": "shell",
|
||||
"command": "docker exec -ti hassio_cli /usr/bin/cli.sh",
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "new"
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Update UI",
|
||||
"type": "shell",
|
||||
"command": "./scripts/update-frontend.sh",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "new"
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Pytest",
|
||||
"type": "shell",
|
||||
"command": "pytest --timeout=10 tests",
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "new"
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Flake8",
|
||||
"type": "shell",
|
||||
"command": "flake8 hassio tests",
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "new"
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Pylint",
|
||||
"type": "shell",
|
||||
"command": "pylint hassio",
|
||||
"dependsOn": ["Install all Requirements"],
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "new"
|
||||
},
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
202
API.md
202
API.md
@@ -38,7 +38,7 @@ The addons from `addons` are only installed one.
|
||||
```json
|
||||
{
|
||||
"version": "INSTALL_VERSION",
|
||||
"last_version": "LAST_VERSION",
|
||||
"version_latest": "version_latest",
|
||||
"arch": "armhf|aarch64|i386|amd64",
|
||||
"channel": "stable|beta|dev",
|
||||
"timezone": "TIMEZONE",
|
||||
@@ -252,6 +252,10 @@ return:
|
||||
}
|
||||
```
|
||||
|
||||
- GET `/host/logs`
|
||||
|
||||
Return host dmesg
|
||||
|
||||
- POST `/host/options`
|
||||
|
||||
```json
|
||||
@@ -291,9 +295,7 @@ return:
|
||||
```json
|
||||
{
|
||||
"version": "2.3",
|
||||
"version_cli": "7",
|
||||
"version_latest": "2.4",
|
||||
"version_cli_latest": "8",
|
||||
"board": "ova|rpi",
|
||||
"boot": "rauc boot slot"
|
||||
}
|
||||
@@ -307,14 +309,6 @@ return:
|
||||
}
|
||||
```
|
||||
|
||||
- POST `/os/update/cli`
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "optional"
|
||||
}
|
||||
```
|
||||
|
||||
- POST `/os/config/sync`
|
||||
|
||||
Load host configs from a USB stick.
|
||||
@@ -369,7 +363,7 @@ Trigger an udev reload
|
||||
```json
|
||||
{
|
||||
"version": "INSTALL_VERSION",
|
||||
"last_version": "LAST_VERSION",
|
||||
"version_latest": "version_latest",
|
||||
"arch": "arch",
|
||||
"machine": "Image machine type",
|
||||
"ip_address": "ip address",
|
||||
@@ -410,7 +404,7 @@ Output is the raw Docker log.
|
||||
```json
|
||||
{
|
||||
"image": "Optional|null",
|
||||
"last_version": "Optional for custom image|null",
|
||||
"version_latest": "Optional for custom image|null",
|
||||
"port": "port for access core",
|
||||
"ssl": "bool",
|
||||
"refresh_token": "",
|
||||
@@ -421,7 +415,7 @@ Output is the raw Docker log.
|
||||
}
|
||||
```
|
||||
|
||||
Image with `null` and last_version with `null` reset this options.
|
||||
Image with `null` and version_latest with `null` reset this options.
|
||||
|
||||
- POST/GET `/core/api`
|
||||
|
||||
@@ -464,7 +458,7 @@ Get all available add-ons.
|
||||
"advanced": "bool",
|
||||
"stage": "stable|experimental|deprecated",
|
||||
"repository": "core|local|REP_ID",
|
||||
"version": "LAST_VERSION",
|
||||
"version": "version_latest",
|
||||
"installed": "none|INSTALL_VERSION",
|
||||
"detached": "bool",
|
||||
"available": "bool",
|
||||
@@ -508,7 +502,7 @@ Get all available add-ons.
|
||||
"homeassistant": "null|min Home Assistant Core version",
|
||||
"repository": "12345678|null",
|
||||
"version": "null|VERSION_INSTALLED",
|
||||
"last_version": "LAST_VERSION",
|
||||
"version_latest": "version_latest",
|
||||
"state": "none|started|stopped",
|
||||
"boot": "auto|manual",
|
||||
"build": "bool",
|
||||
@@ -814,7 +808,7 @@ return:
|
||||
{
|
||||
"host": "ip-address",
|
||||
"version": "1",
|
||||
"latest_version": "2",
|
||||
"version_latest": "2",
|
||||
"servers": ["dns://8.8.8.8"],
|
||||
"locals": ["dns://xy"]
|
||||
}
|
||||
@@ -857,90 +851,29 @@ return:
|
||||
}
|
||||
```
|
||||
|
||||
### Audio
|
||||
### DNS
|
||||
|
||||
- GET `/audio/info`
|
||||
- GET `/dns/info`
|
||||
|
||||
```json
|
||||
{
|
||||
"host": "ip-address",
|
||||
"version": "1",
|
||||
"latest_version": "2",
|
||||
"audio": {
|
||||
"card": [
|
||||
{
|
||||
"name": "...",
|
||||
"index": 1,
|
||||
"driver": "...",
|
||||
"profiles": [
|
||||
{
|
||||
"name": "...",
|
||||
"description": "...",
|
||||
"active": false
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"input": [
|
||||
{
|
||||
"name": "...",
|
||||
"index": 0,
|
||||
"description": "...",
|
||||
"volume": 0.3,
|
||||
"mute": false,
|
||||
"default": false,
|
||||
"card": "null|int",
|
||||
"applications": [
|
||||
{
|
||||
"name": "...",
|
||||
"index": 0,
|
||||
"stream_index": 0,
|
||||
"stream_type": "INPUT",
|
||||
"volume": 0.3,
|
||||
"mute": false,
|
||||
"addon": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"output": [
|
||||
{
|
||||
"name": "...",
|
||||
"index": 0,
|
||||
"description": "...",
|
||||
"volume": 0.3,
|
||||
"mute": false,
|
||||
"default": false,
|
||||
"card": "null|int",
|
||||
"applications": [
|
||||
{
|
||||
"name": "...",
|
||||
"index": 0,
|
||||
"stream_index": 0,
|
||||
"stream_type": "OUTPUT",
|
||||
"volume": 0.3,
|
||||
"mute": false,
|
||||
"addon": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"application": [
|
||||
{
|
||||
"name": "...",
|
||||
"index": 0,
|
||||
"stream_index": 0,
|
||||
"stream_type": "OUTPUT",
|
||||
"volume": 0.3,
|
||||
"mute": false,
|
||||
"addon": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
"version_latest": "2",
|
||||
"servers": ["dns://8.8.8.8"],
|
||||
"locals": ["dns://xy"]
|
||||
}
|
||||
```
|
||||
|
||||
- POST `/audio/update`
|
||||
- POST `/dns/options`
|
||||
|
||||
```json
|
||||
{
|
||||
"servers": ["dns://8.8.8.8"]
|
||||
}
|
||||
```
|
||||
|
||||
- POST `/dns/update`
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -948,92 +881,47 @@ return:
|
||||
}
|
||||
```
|
||||
|
||||
- POST `/audio/restart`
|
||||
- POST `/dns/restart`
|
||||
|
||||
- POST `/audio/reload`
|
||||
- POST `/dns/reset`
|
||||
|
||||
- GET `/audio/logs`
|
||||
- GET `/dns/logs`
|
||||
|
||||
- POST `/audio/volume/input`
|
||||
- GET `/dns/stats`
|
||||
|
||||
```json
|
||||
{
|
||||
"index": "...",
|
||||
"volume": 0.5
|
||||
"cpu_percent": 0.0,
|
||||
"memory_usage": 283123,
|
||||
"memory_limit": 329392,
|
||||
"memory_percent": 1.4,
|
||||
"network_tx": 0,
|
||||
"network_rx": 0,
|
||||
"blk_read": 0,
|
||||
"blk_write": 0
|
||||
}
|
||||
```
|
||||
|
||||
- POST `/audio/volume/output`
|
||||
### CLI
|
||||
|
||||
- GET `/cli/info`
|
||||
|
||||
```json
|
||||
{
|
||||
"index": "...",
|
||||
"volume": 0.5
|
||||
"version": "1",
|
||||
"version_latest": "2"
|
||||
}
|
||||
```
|
||||
|
||||
- POST `/audio/volume/{output|input}/application`
|
||||
- POST `/cli/update`
|
||||
|
||||
```json
|
||||
{
|
||||
"index": "...",
|
||||
"volume": 0.5
|
||||
"version": "VERSION"
|
||||
}
|
||||
```
|
||||
|
||||
- POST `/audio/mute/input`
|
||||
|
||||
```json
|
||||
{
|
||||
"index": "...",
|
||||
"active": false
|
||||
}
|
||||
```
|
||||
|
||||
- POST `/audio/mute/output`
|
||||
|
||||
```json
|
||||
{
|
||||
"index": "...",
|
||||
"active": false
|
||||
}
|
||||
```
|
||||
|
||||
- POST `/audio/mute/{output|input}/application`
|
||||
|
||||
```json
|
||||
{
|
||||
"index": "...",
|
||||
"active": false
|
||||
}
|
||||
```
|
||||
|
||||
- POST `/audio/default/input`
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "..."
|
||||
}
|
||||
```
|
||||
|
||||
- POST `/audio/default/output`
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "..."
|
||||
}
|
||||
```
|
||||
|
||||
- POST `/audio/profile`
|
||||
|
||||
```json
|
||||
{
|
||||
"card": "...",
|
||||
"name": "..."
|
||||
}
|
||||
```
|
||||
|
||||
- GET `/audio/stats`
|
||||
- GET `/cli/stats`
|
||||
|
||||
```json
|
||||
{
|
||||
|
@@ -8,53 +8,19 @@ trigger:
|
||||
pr: none
|
||||
variables:
|
||||
- name: versionWheels
|
||||
value: "1.6-3.7-alpine3.11"
|
||||
- group: wheels
|
||||
value: '1.6.1-3.7-alpine3.11'
|
||||
resources:
|
||||
repositories:
|
||||
- repository: azure
|
||||
type: github
|
||||
name: 'home-assistant/ci-azure'
|
||||
endpoint: 'home-assistant'
|
||||
|
||||
|
||||
jobs:
|
||||
- job: "Wheels"
|
||||
timeoutInMinutes: 360
|
||||
pool:
|
||||
vmImage: "ubuntu-latest"
|
||||
strategy:
|
||||
maxParallel: 5
|
||||
matrix:
|
||||
amd64:
|
||||
buildArch: "amd64"
|
||||
i386:
|
||||
buildArch: "i386"
|
||||
armhf:
|
||||
buildArch: "armhf"
|
||||
armv7:
|
||||
buildArch: "armv7"
|
||||
aarch64:
|
||||
buildArch: "aarch64"
|
||||
steps:
|
||||
- script: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
qemu-user-static \
|
||||
binfmt-support \
|
||||
curl
|
||||
|
||||
sudo mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc
|
||||
sudo update-binfmts --enable qemu-arm
|
||||
sudo update-binfmts --enable qemu-aarch64
|
||||
displayName: "Initial cross build"
|
||||
- script: |
|
||||
mkdir -p .ssh
|
||||
echo -e "-----BEGIN RSA PRIVATE KEY-----\n$(wheelsSSH)\n-----END RSA PRIVATE KEY-----" >> .ssh/id_rsa
|
||||
ssh-keyscan -H $(wheelsHost) >> .ssh/known_hosts
|
||||
chmod 600 .ssh/*
|
||||
displayName: "Install ssh key"
|
||||
- script: sudo docker pull homeassistant/$(buildArch)-wheels:$(versionWheels)
|
||||
displayName: "Install wheels builder"
|
||||
- script: |
|
||||
sudo docker run --rm -v $(pwd):/data:ro -v $(pwd)/.ssh:/root/.ssh:rw \
|
||||
homeassistant/$(buildArch)-wheels:$(versionWheels) \
|
||||
--apk "build-base;libffi-dev;openssl-dev" \
|
||||
--index $(wheelsIndex) \
|
||||
--requirement requirements.txt \
|
||||
--upload rsync \
|
||||
--remote wheels@$(wheelsHost):/opt/wheels
|
||||
displayName: "Run wheels build"
|
||||
- template: templates/azp-job-wheels.yaml@azure
|
||||
parameters:
|
||||
builderVersion: '$(versionWheels)'
|
||||
builderApk: 'build-base;libffi-dev;openssl-dev'
|
||||
builderPip: 'Cython'
|
||||
wheelsRequirement: 'requirements.txt'
|
||||
|
Submodule home-assistant-polymer updated: 8518f774d4...554c0b692d
@@ -1,14 +1,14 @@
|
||||
aiohttp==3.6.1
|
||||
async_timeout==3.0.1
|
||||
attrs==19.3.0
|
||||
cchardet==2.1.5
|
||||
cchardet==2.1.6
|
||||
colorlog==4.1.0
|
||||
cpe==1.2.1
|
||||
cryptography==2.8
|
||||
docker==4.2.0
|
||||
gitpython==3.1.0
|
||||
jinja2==2.11.1
|
||||
packaging==20.1
|
||||
packaging==20.3
|
||||
ptvsd==4.3.2
|
||||
pulsectl==20.2.4
|
||||
pytz==2019.3
|
||||
|
@@ -1,6 +1,6 @@
|
||||
flake8==3.7.9
|
||||
pylint==2.4.4
|
||||
pytest==5.3.5
|
||||
pytest==5.4.1
|
||||
pytest-timeout==1.3.4
|
||||
pytest-aiohttp==0.3.0
|
||||
black==19.10b0
|
||||
|
@@ -2,4 +2,6 @@
|
||||
# ==============================================================================
|
||||
# Start Service service
|
||||
# ==============================================================================
|
||||
export LD_PRELOAD="/usr/local/lib/libjemalloc.so.2"
|
||||
|
||||
exec python3 -m supervisor
|
@@ -81,11 +81,6 @@ function cleanup_docker() {
|
||||
}
|
||||
|
||||
|
||||
function install_cli() {
|
||||
docker pull homeassistant/amd64-hassio-cli:dev
|
||||
}
|
||||
|
||||
|
||||
function setup_test_env() {
|
||||
mkdir -p /workspaces/test_supervisor
|
||||
|
||||
@@ -131,7 +126,6 @@ start_docker
|
||||
trap "stop_docker" ERR
|
||||
|
||||
build_supervisor
|
||||
install_cli
|
||||
cleanup_lastboot
|
||||
cleanup_docker
|
||||
init_dbus
|
||||
|
@@ -59,7 +59,7 @@ class AddonManager(CoreSysAttributes):
|
||||
def from_token(self, token: str) -> Optional[Addon]:
|
||||
"""Return an add-on from Supervisor token."""
|
||||
for addon in self.installed:
|
||||
if token == addon.hassio_token:
|
||||
if token == addon.supervisor_token:
|
||||
return addon
|
||||
return None
|
||||
|
||||
|
@@ -164,7 +164,7 @@ class Addon(AddonModel):
|
||||
return self.persist[ATTR_UUID]
|
||||
|
||||
@property
|
||||
def hassio_token(self) -> Optional[str]:
|
||||
def supervisor_token(self) -> Optional[str]:
|
||||
"""Return access token for Supervisor API."""
|
||||
return self.persist.get(ATTR_ACCESS_TOKEN)
|
||||
|
||||
@@ -393,6 +393,11 @@ class Addon(AddonModel):
|
||||
input_profile=self.audio_input, output_profile=self.audio_output
|
||||
)
|
||||
|
||||
# Cleanup wrong maps
|
||||
if self.path_pulse.is_dir():
|
||||
shutil.rmtree(self.path_pulse, ignore_errors=True)
|
||||
|
||||
# Write pulse config
|
||||
try:
|
||||
with self.path_pulse.open("w") as config_file:
|
||||
config_file.write(pulse_config)
|
||||
@@ -400,11 +405,10 @@ class Addon(AddonModel):
|
||||
_LOGGER.error(
|
||||
"Add-on %s can't write pulse/client.config: %s", self.slug, err
|
||||
)
|
||||
raise AddonsError()
|
||||
|
||||
_LOGGER.debug(
|
||||
"Add-on %s write pulse/client.config: %s", self.slug, self.path_pulse
|
||||
)
|
||||
else:
|
||||
_LOGGER.debug(
|
||||
"Add-on %s write pulse/client.config: %s", self.slug, self.path_pulse
|
||||
)
|
||||
|
||||
async def install_apparmor(self) -> None:
|
||||
"""Install or Update AppArmor profile for Add-on."""
|
||||
|
@@ -137,7 +137,7 @@ class AddonModel(CoreSysAttributes):
|
||||
return None
|
||||
|
||||
@property
|
||||
def hassio_token(self) -> Optional[str]:
|
||||
def supervisor_token(self) -> Optional[str]:
|
||||
"""Return access token for Supervisor API."""
|
||||
return None
|
||||
|
||||
|
@@ -261,7 +261,7 @@ SCHEMA_ADDON_CONFIG = vol.Schema(
|
||||
),
|
||||
vol.Optional(ATTR_IMAGE): vol.Match(RE_DOCKER_IMAGE),
|
||||
vol.Optional(ATTR_TIMEOUT, default=10): vol.All(
|
||||
vol.Coerce(int), vol.Range(min=10, max=120)
|
||||
vol.Coerce(int), vol.Range(min=10, max=300)
|
||||
),
|
||||
},
|
||||
extra=vol.REMOVE_EXTRA,
|
||||
|
@@ -7,11 +7,13 @@ from aiohttp import web
|
||||
|
||||
from ..coresys import CoreSys, CoreSysAttributes
|
||||
from .addons import APIAddons
|
||||
from .audio import APIAudio
|
||||
from .auth import APIAuth
|
||||
from .cli import APICli
|
||||
from .discovery import APIDiscovery
|
||||
from .dns import APICoreDNS
|
||||
from .hardware import APIHardware
|
||||
from .hassos import APIHassOS
|
||||
from .os import APIOS
|
||||
from .homeassistant import APIHomeAssistant
|
||||
from .host import APIHost
|
||||
from .info import APIInfo
|
||||
@@ -21,7 +23,6 @@ from .security import SecurityMiddleware
|
||||
from .services import APIServices
|
||||
from .snapshots import APISnapshots
|
||||
from .supervisor import APISupervisor
|
||||
from .audio import APIAudio
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -49,7 +50,8 @@ class RestAPI(CoreSysAttributes):
|
||||
"""Register REST API Calls."""
|
||||
self._register_supervisor()
|
||||
self._register_host()
|
||||
self._register_hassos()
|
||||
self._register_os()
|
||||
self._register_cli()
|
||||
self._register_hardware()
|
||||
self._register_homeassistant()
|
||||
self._register_proxy()
|
||||
@@ -72,6 +74,7 @@ class RestAPI(CoreSysAttributes):
|
||||
self.webapp.add_routes(
|
||||
[
|
||||
web.get("/host/info", api_host.info),
|
||||
web.get("/host/logs", api_host.logs),
|
||||
web.post("/host/reboot", api_host.reboot),
|
||||
web.post("/host/shutdown", api_host.shutdown),
|
||||
web.post("/host/reload", api_host.reload),
|
||||
@@ -84,22 +87,29 @@ class RestAPI(CoreSysAttributes):
|
||||
]
|
||||
)
|
||||
|
||||
def _register_hassos(self) -> None:
|
||||
"""Register HassOS functions."""
|
||||
api_hassos = APIHassOS()
|
||||
api_hassos.coresys = self.coresys
|
||||
def _register_os(self) -> None:
|
||||
"""Register OS functions."""
|
||||
api_os = APIOS()
|
||||
api_os.coresys = self.coresys
|
||||
|
||||
self.webapp.add_routes(
|
||||
[
|
||||
web.get("/os/info", api_hassos.info),
|
||||
web.post("/os/update", api_hassos.update),
|
||||
web.post("/os/update/cli", api_hassos.update_cli),
|
||||
web.post("/os/config/sync", api_hassos.config_sync),
|
||||
# Remove with old Supervisor fallback
|
||||
web.get("/hassos/info", api_hassos.info),
|
||||
web.post("/hassos/update", api_hassos.update),
|
||||
web.post("/hassos/update/cli", api_hassos.update_cli),
|
||||
web.post("/hassos/config/sync", api_hassos.config_sync),
|
||||
web.get("/os/info", api_os.info),
|
||||
web.post("/os/update", api_os.update),
|
||||
web.post("/os/config/sync", api_os.config_sync),
|
||||
]
|
||||
)
|
||||
|
||||
def _register_cli(self) -> None:
|
||||
"""Register HA cli functions."""
|
||||
api_cli = APICli()
|
||||
api_cli.coresys = self.coresys
|
||||
|
||||
self.webapp.add_routes(
|
||||
[
|
||||
web.get("/cli/info", api_cli.info),
|
||||
web.get("/cli/stats", api_cli.stats),
|
||||
web.post("/cli/update", api_cli.update),
|
||||
]
|
||||
)
|
||||
|
||||
|
@@ -54,7 +54,7 @@ from ..const import (
|
||||
ATTR_INSTALLED,
|
||||
ATTR_IP_ADDRESS,
|
||||
ATTR_KERNEL_MODULES,
|
||||
ATTR_LAST_VERSION,
|
||||
ATTR_VERSION_LATEST,
|
||||
ATTR_LOGO,
|
||||
ATTR_LONG_DESCRIPTION,
|
||||
ATTR_MACHINE,
|
||||
@@ -204,7 +204,7 @@ class APIAddons(CoreSysAttributes):
|
||||
ATTR_AUTO_UPDATE: None,
|
||||
ATTR_REPOSITORY: addon.repository,
|
||||
ATTR_VERSION: None,
|
||||
ATTR_LAST_VERSION: addon.latest_version,
|
||||
ATTR_VERSION_LATEST: addon.latest_version,
|
||||
ATTR_PROTECTED: addon.protected,
|
||||
ATTR_RATING: rating_security(addon),
|
||||
ATTR_BOOT: addon.boot,
|
||||
|
@@ -18,7 +18,7 @@ from ..const import (
|
||||
ATTR_HOST,
|
||||
ATTR_INDEX,
|
||||
ATTR_INPUT,
|
||||
ATTR_LATEST_VERSION,
|
||||
ATTR_VERSION_LATEST,
|
||||
ATTR_MEMORY_LIMIT,
|
||||
ATTR_MEMORY_PERCENT,
|
||||
ATTR_MEMORY_USAGE,
|
||||
@@ -69,7 +69,7 @@ class APIAudio(CoreSysAttributes):
|
||||
"""Return Audio information."""
|
||||
return {
|
||||
ATTR_VERSION: self.sys_audio.version,
|
||||
ATTR_LATEST_VERSION: self.sys_audio.latest_version,
|
||||
ATTR_VERSION_LATEST: self.sys_audio.latest_version,
|
||||
ATTR_HOST: str(self.sys_docker.network.audio),
|
||||
ATTR_AUDIO: {
|
||||
ATTR_CARD: [attr.asdict(card) for card in self.sys_host.sound.cards],
|
||||
@@ -166,5 +166,5 @@ class APIAudio(CoreSysAttributes):
|
||||
body = await api_validate(SCHEMA_PROFILE, request)
|
||||
|
||||
await asyncio.shield(
|
||||
self.sys_host.sound.set_profile(body[ATTR_CARD], body[ATTR_NAME])
|
||||
self.sys_host.sound.ativate_profile(body[ATTR_CARD], body[ATTR_NAME])
|
||||
)
|
||||
|
62
supervisor/api/cli.py
Normal file
62
supervisor/api/cli.py
Normal file
@@ -0,0 +1,62 @@
|
||||
"""Init file for Supervisor HA cli RESTful API."""
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Any, Dict
|
||||
|
||||
from aiohttp import web
|
||||
import voluptuous as vol
|
||||
|
||||
from ..const import (
|
||||
ATTR_VERSION,
|
||||
ATTR_VERSION_LATEST,
|
||||
ATTR_BLK_READ,
|
||||
ATTR_BLK_WRITE,
|
||||
ATTR_CPU_PERCENT,
|
||||
ATTR_MEMORY_LIMIT,
|
||||
ATTR_MEMORY_PERCENT,
|
||||
ATTR_MEMORY_USAGE,
|
||||
ATTR_NETWORK_RX,
|
||||
ATTR_NETWORK_TX,
|
||||
)
|
||||
from ..coresys import CoreSysAttributes
|
||||
from .utils import api_process, api_validate
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
SCHEMA_VERSION = vol.Schema({vol.Optional(ATTR_VERSION): vol.Coerce(str)})
|
||||
|
||||
|
||||
class APICli(CoreSysAttributes):
|
||||
"""Handle RESTful API for HA Cli functions."""
|
||||
|
||||
@api_process
|
||||
async def info(self, request: web.Request) -> Dict[str, Any]:
|
||||
"""Return HA cli information."""
|
||||
return {
|
||||
ATTR_VERSION: self.sys_cli.version,
|
||||
ATTR_VERSION_LATEST: self.sys_cli.latest_version,
|
||||
}
|
||||
|
||||
@api_process
|
||||
async def stats(self, request: web.Request) -> Dict[str, Any]:
|
||||
"""Return resource information."""
|
||||
stats = await self.sys_cli.stats()
|
||||
|
||||
return {
|
||||
ATTR_CPU_PERCENT: stats.cpu_percent,
|
||||
ATTR_MEMORY_USAGE: stats.memory_usage,
|
||||
ATTR_MEMORY_LIMIT: stats.memory_limit,
|
||||
ATTR_MEMORY_PERCENT: stats.memory_percent,
|
||||
ATTR_NETWORK_RX: stats.network_rx,
|
||||
ATTR_NETWORK_TX: stats.network_tx,
|
||||
ATTR_BLK_READ: stats.blk_read,
|
||||
ATTR_BLK_WRITE: stats.blk_write,
|
||||
}
|
||||
|
||||
@api_process
|
||||
async def update(self, request: web.Request) -> None:
|
||||
"""Update HA CLI."""
|
||||
body = await api_validate(SCHEMA_VERSION, request)
|
||||
version = body.get(ATTR_VERSION, self.sys_cli.latest_version)
|
||||
|
||||
await asyncio.shield(self.sys_cli.update(version))
|
@@ -11,7 +11,7 @@ from ..const import (
|
||||
ATTR_BLK_WRITE,
|
||||
ATTR_CPU_PERCENT,
|
||||
ATTR_HOST,
|
||||
ATTR_LATEST_VERSION,
|
||||
ATTR_VERSION_LATEST,
|
||||
ATTR_LOCALS,
|
||||
ATTR_MEMORY_LIMIT,
|
||||
ATTR_MEMORY_PERCENT,
|
||||
@@ -43,7 +43,7 @@ class APICoreDNS(CoreSysAttributes):
|
||||
"""Return DNS information."""
|
||||
return {
|
||||
ATTR_VERSION: self.sys_dns.version,
|
||||
ATTR_LATEST_VERSION: self.sys_dns.latest_version,
|
||||
ATTR_VERSION_LATEST: self.sys_dns.latest_version,
|
||||
ATTR_HOST: str(self.sys_docker.network.dns),
|
||||
ATTR_SERVERS: self.sys_dns.servers,
|
||||
ATTR_LOCALS: self.sys_host.network.dns_servers,
|
||||
|
@@ -17,7 +17,7 @@ from ..const import (
|
||||
ATTR_CUSTOM,
|
||||
ATTR_IMAGE,
|
||||
ATTR_IP_ADDRESS,
|
||||
ATTR_LAST_VERSION,
|
||||
ATTR_VERSION_LATEST,
|
||||
ATTR_MACHINE,
|
||||
ATTR_MEMORY_LIMIT,
|
||||
ATTR_MEMORY_PERCENT,
|
||||
@@ -44,7 +44,7 @@ SCHEMA_OPTIONS = vol.Schema(
|
||||
{
|
||||
vol.Optional(ATTR_BOOT): vol.Boolean(),
|
||||
vol.Inclusive(ATTR_IMAGE, "custom_hass"): vol.Maybe(docker_image),
|
||||
vol.Inclusive(ATTR_LAST_VERSION, "custom_hass"): vol.Maybe(vol.Coerce(str)),
|
||||
vol.Inclusive(ATTR_VERSION_LATEST, "custom_hass"): vol.Maybe(vol.Coerce(str)),
|
||||
vol.Optional(ATTR_PORT): network_port,
|
||||
vol.Optional(ATTR_SSL): vol.Boolean(),
|
||||
vol.Optional(ATTR_WATCHDOG): vol.Boolean(),
|
||||
@@ -66,7 +66,7 @@ class APIHomeAssistant(CoreSysAttributes):
|
||||
"""Return host information."""
|
||||
return {
|
||||
ATTR_VERSION: self.sys_homeassistant.version,
|
||||
ATTR_LAST_VERSION: self.sys_homeassistant.latest_version,
|
||||
ATTR_VERSION_LATEST: self.sys_homeassistant.latest_version,
|
||||
ATTR_MACHINE: self.sys_homeassistant.machine,
|
||||
ATTR_IP_ADDRESS: str(self.sys_homeassistant.ip_address),
|
||||
ATTR_ARCH: self.sys_homeassistant.arch,
|
||||
@@ -79,6 +79,8 @@ class APIHomeAssistant(CoreSysAttributes):
|
||||
ATTR_WAIT_BOOT: self.sys_homeassistant.wait_boot,
|
||||
ATTR_AUDIO_INPUT: self.sys_homeassistant.audio_input,
|
||||
ATTR_AUDIO_OUTPUT: self.sys_homeassistant.audio_output,
|
||||
# Remove end of Q3 2020
|
||||
"last_version": self.sys_homeassistant.latest_version,
|
||||
}
|
||||
|
||||
@api_process
|
||||
@@ -86,9 +88,9 @@ class APIHomeAssistant(CoreSysAttributes):
|
||||
"""Set Home Assistant options."""
|
||||
body = await api_validate(SCHEMA_OPTIONS, request)
|
||||
|
||||
if ATTR_IMAGE in body and ATTR_LAST_VERSION in body:
|
||||
if ATTR_IMAGE in body and ATTR_VERSION_LATEST in body:
|
||||
self.sys_homeassistant.image = body[ATTR_IMAGE]
|
||||
self.sys_homeassistant.latest_version = body[ATTR_LAST_VERSION]
|
||||
self.sys_homeassistant.latest_version = body[ATTR_VERSION_LATEST]
|
||||
|
||||
if ATTR_BOOT in body:
|
||||
self.sys_homeassistant.boot = body[ATTR_BOOT]
|
||||
|
@@ -1,24 +1,27 @@
|
||||
"""Init file for Supervisor host RESTful API."""
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Awaitable
|
||||
|
||||
from aiohttp import web
|
||||
import voluptuous as vol
|
||||
|
||||
from .utils import api_process, api_validate
|
||||
from ..const import (
|
||||
ATTR_HOSTNAME,
|
||||
ATTR_FEATURES,
|
||||
ATTR_KERNEL,
|
||||
ATTR_OPERATING_SYSTEM,
|
||||
ATTR_CHASSIS,
|
||||
ATTR_DEPLOYMENT,
|
||||
ATTR_STATE,
|
||||
ATTR_NAME,
|
||||
ATTR_DESCRIPTON,
|
||||
ATTR_SERVICES,
|
||||
ATTR_CPE,
|
||||
ATTR_DEPLOYMENT,
|
||||
ATTR_DESCRIPTON,
|
||||
ATTR_FEATURES,
|
||||
ATTR_HOSTNAME,
|
||||
ATTR_KERNEL,
|
||||
ATTR_NAME,
|
||||
ATTR_OPERATING_SYSTEM,
|
||||
ATTR_SERVICES,
|
||||
ATTR_STATE,
|
||||
CONTENT_TYPE_BINARY,
|
||||
)
|
||||
from ..coresys import CoreSysAttributes
|
||||
from .utils import api_process, api_process_raw, api_validate
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -107,3 +110,8 @@ class APIHost(CoreSysAttributes):
|
||||
"""Restart a service."""
|
||||
unit = request.match_info.get(SERVICE)
|
||||
return asyncio.shield(self.sys_host.services.restart(unit))
|
||||
|
||||
@api_process_raw(CONTENT_TYPE_BINARY)
|
||||
def logs(self, request: web.Request) -> Awaitable[bytes]:
|
||||
"""Return host kernel logs."""
|
||||
return self.sys_host.info.get_dmesg()
|
||||
|
@@ -10,8 +10,6 @@ from ..const import (
|
||||
ATTR_BOARD,
|
||||
ATTR_BOOT,
|
||||
ATTR_VERSION,
|
||||
ATTR_VERSION_CLI,
|
||||
ATTR_VERSION_CLI_LATEST,
|
||||
ATTR_VERSION_LATEST,
|
||||
)
|
||||
from ..coresys import CoreSysAttributes
|
||||
@@ -22,38 +20,28 @@ _LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
SCHEMA_VERSION = vol.Schema({vol.Optional(ATTR_VERSION): vol.Coerce(str)})
|
||||
|
||||
|
||||
class APIHassOS(CoreSysAttributes):
|
||||
"""Handle RESTful API for HassOS functions."""
|
||||
class APIOS(CoreSysAttributes):
|
||||
"""Handle RESTful API for OS functions."""
|
||||
|
||||
@api_process
|
||||
async def info(self, request: web.Request) -> Dict[str, Any]:
|
||||
"""Return HassOS information."""
|
||||
"""Return OS information."""
|
||||
return {
|
||||
ATTR_VERSION: self.sys_hassos.version,
|
||||
ATTR_VERSION_CLI: self.sys_hassos.version_cli,
|
||||
ATTR_VERSION_LATEST: self.sys_hassos.version_latest,
|
||||
ATTR_VERSION_CLI_LATEST: self.sys_hassos.version_cli_latest,
|
||||
ATTR_VERSION_LATEST: self.sys_hassos.latest_version,
|
||||
ATTR_BOARD: self.sys_hassos.board,
|
||||
ATTR_BOOT: self.sys_dbus.rauc.boot_slot,
|
||||
}
|
||||
|
||||
@api_process
|
||||
async def update(self, request: web.Request) -> None:
|
||||
"""Update HassOS."""
|
||||
"""Update OS."""
|
||||
body = await api_validate(SCHEMA_VERSION, request)
|
||||
version = body.get(ATTR_VERSION, self.sys_hassos.version_latest)
|
||||
version = body.get(ATTR_VERSION, self.sys_hassos.latest_version)
|
||||
|
||||
await asyncio.shield(self.sys_hassos.update(version))
|
||||
|
||||
@api_process
|
||||
async def update_cli(self, request: web.Request) -> None:
|
||||
"""Update HassOS CLI."""
|
||||
body = await api_validate(SCHEMA_VERSION, request)
|
||||
version = body.get(ATTR_VERSION, self.sys_hassos.version_cli_latest)
|
||||
|
||||
await asyncio.shield(self.sys_hassos.update_cli(version))
|
||||
|
||||
@api_process
|
||||
def config_sync(self, request: web.Request) -> Awaitable[None]:
|
||||
"""Trigger config reload on HassOS."""
|
||||
"""Trigger config reload on OS."""
|
||||
return asyncio.shield(self.sys_hassos.config_sync())
|
3
supervisor/api/panel/chunk.05d0c315f85f2fc8c34a.js
Normal file
3
supervisor/api/panel/chunk.05d0c315f85f2fc8c34a.js
Normal file
File diff suppressed because one or more lines are too long
10
supervisor/api/panel/chunk.05d0c315f85f2fc8c34a.js.LICENSE
Normal file
10
supervisor/api/panel/chunk.05d0c315f85f2fc8c34a.js.LICENSE
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at
|
||||
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
|
||||
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
|
||||
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
|
||||
part of the polymer project is also subject to an additional IP rights grant
|
||||
found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
BIN
supervisor/api/panel/chunk.05d0c315f85f2fc8c34a.js.gz
Normal file
BIN
supervisor/api/panel/chunk.05d0c315f85f2fc8c34a.js.gz
Normal file
Binary file not shown.
1
supervisor/api/panel/chunk.05d0c315f85f2fc8c34a.js.map
Normal file
1
supervisor/api/panel/chunk.05d0c315f85f2fc8c34a.js.map
Normal file
File diff suppressed because one or more lines are too long
2
supervisor/api/panel/chunk.381907f8b6a21cec010f.js
Normal file
2
supervisor/api/panel/chunk.381907f8b6a21cec010f.js
Normal file
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/chunk.381907f8b6a21cec010f.js.gz
Normal file
BIN
supervisor/api/panel/chunk.381907f8b6a21cec010f.js.gz
Normal file
Binary file not shown.
1
supervisor/api/panel/chunk.381907f8b6a21cec010f.js.map
Normal file
1
supervisor/api/panel/chunk.381907f8b6a21cec010f.js.map
Normal file
File diff suppressed because one or more lines are too long
2
supervisor/api/panel/chunk.54be96e339b8c82764a0.js
Normal file
2
supervisor/api/panel/chunk.54be96e339b8c82764a0.js
Normal file
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/chunk.54be96e339b8c82764a0.js.gz
Normal file
BIN
supervisor/api/panel/chunk.54be96e339b8c82764a0.js.gz
Normal file
Binary file not shown.
1
supervisor/api/panel/chunk.54be96e339b8c82764a0.js.map
Normal file
1
supervisor/api/panel/chunk.54be96e339b8c82764a0.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["webpack:///./hassio/src/ingress-view/hassio-ingress-view.ts"],"names":["customElement","HassioIngressView","property","this","_addon","html","_templateObject2","name","ingress_url","_templateObject","changedProps","_get","_getPrototypeOf","prototype","call","has","addon","route","path","substr","oldRoute","get","oldAddon","undefined","_fetchData","_callee","addonSlug","_ref","_ref2","regeneratorRuntime","wrap","_context","prev","next","Promise","all","fetchHassioAddonInfo","hass","Error","createHassioSession","sent","_slicedToArray","ingress","t0","console","error","alert","message","history","back","stop","css","_templateObject3","LitElement"],"mappings":"snSAmBCA,YAAc,0CACTC,smBACHC,kEACAA,mEACAA,4EAED,WACE,OAAKC,KAAKC,OAMHC,YAAPC,IAC0BH,KAAKC,OAAOG,KACpBJ,KAAKC,OAAOI,aAPrBH,YAAPI,0CAYJ,SAAkBC,GAGhB,GAFAC,EAAAC,EApBEX,EAoBFY,WAAA,eAAAV,MAAAW,KAAAX,KAAmBO,GAEdA,EAAaK,IAAI,SAAtB,CAIA,IAAMC,EAAQb,KAAKc,MAAMC,KAAKC,OAAO,GAE/BC,EAAWV,EAAaW,IAAI,SAC5BC,EAAWF,EAAWA,EAASF,KAAKC,OAAO,QAAKI,EAElDP,GAASA,IAAUM,GACrBnB,KAAKqB,WAAWR,0FAIpB,SAAAS,EAAyBC,GAAzB,IAAAC,EAAAC,EAAAZ,EAAA,OAAAa,mBAAAC,KAAA,SAAAC,GAAA,cAAAA,EAAAC,KAAAD,EAAAE,MAAA,cAAAF,EAAAC,KAAA,EAAAD,EAAAE,KAAA,EAE0BC,QAAQC,IAAI,CAChCC,YAAqBjC,KAAKkC,KAAMX,GAAhC,MAAiD,WAC/C,MAAM,IAAIY,MAAM,iCAElBC,YAAoBpC,KAAKkC,MAAzB,MAAqC,WACnC,MAAM,IAAIC,MAAM,2CAPxB,UAAAX,EAAAI,EAAAS,KAAAZ,EAAAa,EAAAd,EAAA,IAEWX,EAFXY,EAAA,IAWec,QAXf,CAAAX,EAAAE,KAAA,cAYY,IAAIK,MAAM,wCAZtB,OAeInC,KAAKC,OAASY,EAflBe,EAAAE,KAAA,iBAAAF,EAAAC,KAAA,GAAAD,EAAAY,GAAAZ,EAAA,SAkBIa,QAAQC,MAARd,EAAAY,IACAG,MAAMf,EAAAY,GAAII,SAAW,mCACrBC,QAAQC,OApBZ,yBAAAlB,EAAAmB,SAAAzB,EAAAtB,KAAA,yRAwBA,WACE,OAAOgD,YAAPC,UA7D4BC","file":"chunk.54be96e339b8c82764a0.js","sourcesContent":["import {\n LitElement,\n customElement,\n property,\n TemplateResult,\n html,\n PropertyValues,\n CSSResult,\n css,\n} from \"lit-element\";\nimport { HomeAssistant, Route } from \"../../../src/types\";\nimport { createHassioSession } from \"../../../src/data/hassio/supervisor\";\nimport {\n HassioAddonDetails,\n fetchHassioAddonInfo,\n} from \"../../../src/data/hassio/addon\";\nimport \"../../../src/layouts/hass-loading-screen\";\nimport \"../../../src/layouts/hass-subpage\";\n\n@customElement(\"hassio-ingress-view\")\nclass HassioIngressView extends LitElement {\n @property() public hass!: HomeAssistant;\n @property() public route!: Route;\n @property() private _addon?: HassioAddonDetails;\n\n protected render(): TemplateResult {\n if (!this._addon) {\n return html`\n <hass-loading-screen></hass-loading-screen>\n `;\n }\n\n return html`\n <hass-subpage .header=${this._addon.name} hassio>\n <iframe src=${this._addon.ingress_url}></iframe>\n </hass-subpage>\n `;\n }\n\n protected updated(changedProps: PropertyValues) {\n super.firstUpdated(changedProps);\n\n if (!changedProps.has(\"route\")) {\n return;\n }\n\n const addon = this.route.path.substr(1);\n\n const oldRoute = changedProps.get(\"route\") as this[\"route\"] | undefined;\n const oldAddon = oldRoute ? oldRoute.path.substr(1) : undefined;\n\n if (addon && addon !== oldAddon) {\n this._fetchData(addon);\n }\n }\n\n private async _fetchData(addonSlug: string) {\n try {\n const [addon] = await Promise.all([\n fetchHassioAddonInfo(this.hass, addonSlug).catch(() => {\n throw new Error(\"Failed to fetch add-on info\");\n }),\n createHassioSession(this.hass).catch(() => {\n throw new Error(\"Failed to create an ingress session\");\n }),\n ]);\n\n if (!addon.ingress) {\n throw new Error(\"This add-on does not support ingress\");\n }\n\n this._addon = addon;\n } catch (err) {\n // tslint:disable-next-line\n console.error(err);\n alert(err.message || \"Unknown error starting ingress.\");\n history.back();\n }\n }\n\n static get styles(): CSSResult {\n return css`\n iframe {\n display: block;\n width: 100%;\n height: 100%;\n border: 0;\n }\n paper-icon-button {\n color: var(--text-primary-color);\n }\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"hassio-ingress-view\": HassioIngressView;\n }\n}\n"],"sourceRoot":""}
|
3
supervisor/api/panel/chunk.594ef9f33131c4d33409.js
Normal file
3
supervisor/api/panel/chunk.594ef9f33131c4d33409.js
Normal file
File diff suppressed because one or more lines are too long
28
supervisor/api/panel/chunk.594ef9f33131c4d33409.js.LICENSE
Normal file
28
supervisor/api/panel/chunk.594ef9f33131c4d33409.js.LICENSE
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at
|
||||
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
|
||||
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
|
||||
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
|
||||
part of the polymer project is also subject to an additional IP rights grant
|
||||
found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/*!
|
||||
* The buffer module from node.js, for the browser.
|
||||
*
|
||||
* @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at
|
||||
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
|
||||
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
|
||||
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
|
||||
part of the polymer project is also subject to an additional IP rights grant
|
||||
found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
BIN
supervisor/api/panel/chunk.594ef9f33131c4d33409.js.gz
Normal file
BIN
supervisor/api/panel/chunk.594ef9f33131c4d33409.js.gz
Normal file
Binary file not shown.
1
supervisor/api/panel/chunk.594ef9f33131c4d33409.js.map
Normal file
1
supervisor/api/panel/chunk.594ef9f33131c4d33409.js.map
Normal file
File diff suppressed because one or more lines are too long
3
supervisor/api/panel/chunk.610423483939ffdbfb92.js
Normal file
3
supervisor/api/panel/chunk.610423483939ffdbfb92.js
Normal file
File diff suppressed because one or more lines are too long
20
supervisor/api/panel/chunk.610423483939ffdbfb92.js.LICENSE
Normal file
20
supervisor/api/panel/chunk.610423483939ffdbfb92.js.LICENSE
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at
|
||||
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
|
||||
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
|
||||
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
|
||||
part of the polymer project is also subject to an additional IP rights grant
|
||||
found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
BIN
supervisor/api/panel/chunk.610423483939ffdbfb92.js.gz
Normal file
BIN
supervisor/api/panel/chunk.610423483939ffdbfb92.js.gz
Normal file
Binary file not shown.
1
supervisor/api/panel/chunk.610423483939ffdbfb92.js.map
Normal file
1
supervisor/api/panel/chunk.610423483939ffdbfb92.js.map
Normal file
File diff suppressed because one or more lines are too long
2
supervisor/api/panel/chunk.66f27f84503037304f17.js
Normal file
2
supervisor/api/panel/chunk.66f27f84503037304f17.js
Normal file
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/chunk.66f27f84503037304f17.js.gz
Normal file
BIN
supervisor/api/panel/chunk.66f27f84503037304f17.js.gz
Normal file
Binary file not shown.
1
supervisor/api/panel/chunk.66f27f84503037304f17.js.map
Normal file
1
supervisor/api/panel/chunk.66f27f84503037304f17.js.map
Normal file
File diff suppressed because one or more lines are too long
3
supervisor/api/panel/chunk.7ab69340d9bb40dda79e.js
Normal file
3
supervisor/api/panel/chunk.7ab69340d9bb40dda79e.js
Normal file
File diff suppressed because one or more lines are too long
16
supervisor/api/panel/chunk.7ab69340d9bb40dda79e.js.LICENSE
Normal file
16
supervisor/api/panel/chunk.7ab69340d9bb40dda79e.js.LICENSE
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
@license
|
||||
Copyright 2018 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
BIN
supervisor/api/panel/chunk.7ab69340d9bb40dda79e.js.gz
Normal file
BIN
supervisor/api/panel/chunk.7ab69340d9bb40dda79e.js.gz
Normal file
Binary file not shown.
1
supervisor/api/panel/chunk.7ab69340d9bb40dda79e.js.map
Normal file
1
supervisor/api/panel/chunk.7ab69340d9bb40dda79e.js.map
Normal file
File diff suppressed because one or more lines are too long
3
supervisor/api/panel/chunk.7ac9df67ea34d63f8998.js
Normal file
3
supervisor/api/panel/chunk.7ac9df67ea34d63f8998.js
Normal file
File diff suppressed because one or more lines are too long
189
supervisor/api/panel/chunk.7ac9df67ea34d63f8998.js.LICENSE
Normal file
189
supervisor/api/panel/chunk.7ac9df67ea34d63f8998.js.LICENSE
Normal file
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
||||
* This code may only be used under the BSD style license found at
|
||||
* http://polymer.github.io/LICENSE.txt
|
||||
* The complete set of authors may be found at
|
||||
* http://polymer.github.io/AUTHORS.txt
|
||||
* The complete set of contributors may be found at
|
||||
* http://polymer.github.io/CONTRIBUTORS.txt
|
||||
* Code distributed by Google as part of the polymer project is also
|
||||
* subject to an additional IP rights grant found at
|
||||
* http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2019 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at
|
||||
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
|
||||
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
|
||||
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
|
||||
part of the polymer project is also subject to an additional IP rights grant
|
||||
found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at
|
||||
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
|
||||
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
|
||||
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
|
||||
part of the polymer project is also subject to an additional IP rights grant
|
||||
found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
@license
|
||||
Copyright 2018 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at
|
||||
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
|
||||
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
|
||||
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
|
||||
part of the polymer project is also subject to an additional IP rights grant
|
||||
found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
|
||||
See the Apache Version 2.0 License for specific language governing permissions
|
||||
and limitations under the License.
|
||||
***************************************************************************** */
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright (c) 2018 The Polymer Project Authors. All rights reserved.
|
||||
* This code may only be used under the BSD style license found at
|
||||
* http://polymer.github.io/LICENSE.txt
|
||||
* The complete set of authors may be found at
|
||||
* http://polymer.github.io/AUTHORS.txt
|
||||
* The complete set of contributors may be found at
|
||||
* http://polymer.github.io/CONTRIBUTORS.txt
|
||||
* Code distributed by Google as part of the polymer project is also
|
||||
* subject to an additional IP rights grant found at
|
||||
* http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2016 Google Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at
|
||||
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
|
||||
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
|
||||
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
|
||||
part of the polymer project is also subject to an additional IP rights grant
|
||||
found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at
|
||||
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
|
||||
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
|
||||
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
|
||||
part of the polymer project is also subject to an additional IP rights grant
|
||||
found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Fuse.js v3.4.4 - Lightweight fuzzy-search (http://fusejs.io)
|
||||
*
|
||||
* Copyright (c) 2012-2017 Kirollos Risk (http://kiro.me)
|
||||
* All Rights Reserved. Apache Software License 2.0
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
BIN
supervisor/api/panel/chunk.7ac9df67ea34d63f8998.js.gz
Normal file
BIN
supervisor/api/panel/chunk.7ac9df67ea34d63f8998.js.gz
Normal file
Binary file not shown.
1
supervisor/api/panel/chunk.7ac9df67ea34d63f8998.js.map
Normal file
1
supervisor/api/panel/chunk.7ac9df67ea34d63f8998.js.map
Normal file
File diff suppressed because one or more lines are too long
2
supervisor/api/panel/chunk.7b1a3970d90b4082531e.js
Normal file
2
supervisor/api/panel/chunk.7b1a3970d90b4082531e.js
Normal file
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/chunk.7b1a3970d90b4082531e.js.gz
Normal file
BIN
supervisor/api/panel/chunk.7b1a3970d90b4082531e.js.gz
Normal file
Binary file not shown.
1
supervisor/api/panel/chunk.7b1a3970d90b4082531e.js.map
Normal file
1
supervisor/api/panel/chunk.7b1a3970d90b4082531e.js.map
Normal file
File diff suppressed because one or more lines are too long
3
supervisor/api/panel/chunk.a947da09b1ace1352383.js
Normal file
3
supervisor/api/panel/chunk.a947da09b1ace1352383.js
Normal file
File diff suppressed because one or more lines are too long
10
supervisor/api/panel/chunk.a947da09b1ace1352383.js.LICENSE
Normal file
10
supervisor/api/panel/chunk.a947da09b1ace1352383.js.LICENSE
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at
|
||||
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
|
||||
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
|
||||
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
|
||||
part of the polymer project is also subject to an additional IP rights grant
|
||||
found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
BIN
supervisor/api/panel/chunk.a947da09b1ace1352383.js.gz
Normal file
BIN
supervisor/api/panel/chunk.a947da09b1ace1352383.js.gz
Normal file
Binary file not shown.
1
supervisor/api/panel/chunk.a947da09b1ace1352383.js.map
Normal file
1
supervisor/api/panel/chunk.a947da09b1ace1352383.js.map
Normal file
File diff suppressed because one or more lines are too long
3
supervisor/api/panel/chunk.d402ad0de6ea5da0f9fc.js
Normal file
3
supervisor/api/panel/chunk.d402ad0de6ea5da0f9fc.js
Normal file
File diff suppressed because one or more lines are too long
10
supervisor/api/panel/chunk.d402ad0de6ea5da0f9fc.js.LICENSE
Normal file
10
supervisor/api/panel/chunk.d402ad0de6ea5da0f9fc.js.LICENSE
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at
|
||||
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
|
||||
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
|
||||
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
|
||||
part of the polymer project is also subject to an additional IP rights grant
|
||||
found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
BIN
supervisor/api/panel/chunk.d402ad0de6ea5da0f9fc.js.gz
Normal file
BIN
supervisor/api/panel/chunk.d402ad0de6ea5da0f9fc.js.gz
Normal file
Binary file not shown.
1
supervisor/api/panel/chunk.d402ad0de6ea5da0f9fc.js.map
Normal file
1
supervisor/api/panel/chunk.d402ad0de6ea5da0f9fc.js.map
Normal file
File diff suppressed because one or more lines are too long
2
supervisor/api/panel/chunk.ece210525014eb36073b.js
Normal file
2
supervisor/api/panel/chunk.ece210525014eb36073b.js
Normal file
@@ -0,0 +1,2 @@
|
||||
(self.webpackJsonp=self.webpackJsonp||[]).push([[2],{178:function(e,r,n){"use strict";n.r(r),n.d(r,"codeMirror",function(){return c}),n.d(r,"codeMirrorCss",function(){return i});var a=n(54),o=n.n(a),s=n(170),t=(n(171),n(172),n(11));o.a.commands.save=function(e){Object(t.a)(e.getWrapperElement(),"editor-save")};var c=o.a,i=s.a}}]);
|
||||
//# sourceMappingURL=chunk.ece210525014eb36073b.js.map
|
BIN
supervisor/api/panel/chunk.ece210525014eb36073b.js.gz
Normal file
BIN
supervisor/api/panel/chunk.ece210525014eb36073b.js.gz
Normal file
Binary file not shown.
1
supervisor/api/panel/chunk.ece210525014eb36073b.js.map
Normal file
1
supervisor/api/panel/chunk.ece210525014eb36073b.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["webpack:///./src/resources/codemirror.ts"],"names":["__webpack_require__","r","__webpack_exports__","d","codeMirror","codeMirrorCss","codemirror__WEBPACK_IMPORTED_MODULE_0__","codemirror__WEBPACK_IMPORTED_MODULE_0___default","n","codemirror_lib_codemirror_css__WEBPACK_IMPORTED_MODULE_1__","_common_dom_fire_event__WEBPACK_IMPORTED_MODULE_4__","_CodeMirror","commands","save","cm","fireEvent","getWrapperElement","_codeMirrorCss"],"mappings":"sFAAAA,EAAAC,EAAAC,GAAAF,EAAAG,EAAAD,EAAA,+BAAAE,IAAAJ,EAAAG,EAAAD,EAAA,kCAAAG,IAAA,IAAAC,EAAAN,EAAA,IAAAO,EAAAP,EAAAQ,EAAAF,GAAAG,EAAAT,EAAA,KAAAU,GAAAV,EAAA,KAAAA,EAAA,KAAAA,EAAA,KAQAW,IAAYC,SAASC,KAAO,SAACC,GAC3BC,YAAUD,EAAGE,oBAAqB,gBAE7B,IAAMZ,EAAkBO,IAClBN,EAAqBY","file":"chunk.ece210525014eb36073b.js","sourcesContent":["// @ts-ignore\nimport _CodeMirror, { Editor } from \"codemirror\";\n// @ts-ignore\nimport _codeMirrorCss from \"codemirror/lib/codemirror.css\";\nimport \"codemirror/mode/yaml/yaml\";\nimport \"codemirror/mode/jinja2/jinja2\";\nimport { fireEvent } from \"../common/dom/fire_event\";\n\n_CodeMirror.commands.save = (cm: Editor) => {\n fireEvent(cm.getWrapperElement(), \"editor-save\");\n};\nexport const codeMirror: any = _CodeMirror;\nexport const codeMirrorCss: any = _codeMirrorCss;\n"],"sourceRoot":""}
|
@@ -1,2 +1,2 @@
|
||||
!function(e){function n(n){for(var t,o,a=n[0],i=n[1],c=0,f=[];c<a.length;c++)o=a[c],Object.prototype.hasOwnProperty.call(r,o)&&r[o]&&f.push(r[o][0]),r[o]=0;for(t in i)Object.prototype.hasOwnProperty.call(i,t)&&(e[t]=i[t]);for(u&&u(n);f.length;)f.shift()()}var t={},r={6:0};function o(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,o),r.l=!0,r.exports}o.e=function(e){var n=[],t=r[e];if(0!==t)if(t)n.push(t[2]);else{var a=new Promise(function(n,o){t=r[e]=[n,o]});n.push(t[2]=a);var i,c=document.createElement("script");c.charset="utf-8",c.timeout=120,o.nc&&c.setAttribute("nonce",o.nc),c.src=function(e){return o.p+"chunk."+{0:"b6e61f8340c32e6904ca",1:"9339f70c8bfe2cbef5ad",2:"26756b56961f7bf94974",3:"b2a892416a728ca06e9a",4:"26be881fcb628958e718",5:"ea2041e4c67d4c05b0dd",7:"1a25d23325fed5a4d90b",8:"af76a4db9eb1e2862aae",9:"35929da61d769e57c884",10:"5e32280d595be3742226",11:"a9cf4ae83af78188e158",12:"b2dce600432c76a53d8c",13:"70a435e100109291f210",14:"93a8a2e1dbccae0e07fa",15:"541d0b76b660d8646074"}[e]+".js"}(e);var u=new Error;i=function(n){c.onerror=c.onload=null,clearTimeout(f);var t=r[e];if(0!==t){if(t){var o=n&&("load"===n.type?"missing":n.type),a=n&&n.target&&n.target.src;u.message="Loading chunk "+e+" failed.\n("+o+": "+a+")",u.name="ChunkLoadError",u.type=o,u.request=a,t[1](u)}r[e]=void 0}};var f=setTimeout(function(){i({type:"timeout",target:c})},12e4);c.onerror=c.onload=i,document.head.appendChild(c)}return Promise.all(n)},o.m=e,o.c=t,o.d=function(e,n,t){o.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(e,n){if(1&n&&(e=o(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(o.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)o.d(t,r,function(n){return e[n]}.bind(null,r));return t},o.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(n,"a",n),n},o.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},o.p="/api/hassio/app/",o.oe=function(e){throw console.error(e),e};var a=self.webpackJsonp=self.webpackJsonp||[],i=a.push.bind(a);a.push=n,a=a.slice();for(var c=0;c<a.length;c++)n(a[c]);var u=i;o(o.s=0)}([function(e,n,t){window.loadES5Adapter().then(function(){t.e(12).then(t.t.bind(null,1,7)),Promise.all([t.e(1),t.e(8)]).then(t.bind(null,3)),Promise.all([t.e(1),t.e(15),t.e(10)]).then(t.bind(null,2))});var r=document.createElement("style");r.innerHTML="\nbody {\n font-family: Roboto, sans-serif;\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-weight: 400;\n margin: 0;\n padding: 0;\n height: 100vh;\n}\n",document.head.appendChild(r)}]);
|
||||
!function(e){function n(n){for(var t,o,a=n[0],i=n[1],f=0,u=[];f<a.length;f++)o=a[f],Object.prototype.hasOwnProperty.call(r,o)&&r[o]&&u.push(r[o][0]),r[o]=0;for(t in i)Object.prototype.hasOwnProperty.call(i,t)&&(e[t]=i[t]);for(c&&c(n);u.length;)u.shift()()}var t={},r={6:0};function o(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,o),r.l=!0,r.exports}o.e=function(e){var n=[],t=r[e];if(0!==t)if(t)n.push(t[2]);else{var a=new Promise(function(n,o){t=r[e]=[n,o]});n.push(t[2]=a);var i,f=document.createElement("script");f.charset="utf-8",f.timeout=120,o.nc&&f.setAttribute("nonce",o.nc),f.src=function(e){return o.p+"chunk."+{0:"7ab69340d9bb40dda79e",1:"610423483939ffdbfb92",2:"ece210525014eb36073b",3:"a947da09b1ace1352383",4:"d402ad0de6ea5da0f9fc",5:"05d0c315f85f2fc8c34a",7:"66f27f84503037304f17",8:"af76a4db9eb1e2862aae",9:"54be96e339b8c82764a0",10:"7b1a3970d90b4082531e",11:"381907f8b6a21cec010f",12:"b2dce600432c76a53d8c",13:"70a435e100109291f210",14:"594ef9f33131c4d33409",15:"7ac9df67ea34d63f8998"}[e]+".js"}(e);var c=new Error;i=function(n){f.onerror=f.onload=null,clearTimeout(u);var t=r[e];if(0!==t){if(t){var o=n&&("load"===n.type?"missing":n.type),a=n&&n.target&&n.target.src;c.message="Loading chunk "+e+" failed.\n("+o+": "+a+")",c.name="ChunkLoadError",c.type=o,c.request=a,t[1](c)}r[e]=void 0}};var u=setTimeout(function(){i({type:"timeout",target:f})},12e4);f.onerror=f.onload=i,document.head.appendChild(f)}return Promise.all(n)},o.m=e,o.c=t,o.d=function(e,n,t){o.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(e,n){if(1&n&&(e=o(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(o.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)o.d(t,r,function(n){return e[n]}.bind(null,r));return t},o.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(n,"a",n),n},o.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},o.p="/api/hassio/app/",o.oe=function(e){throw console.error(e),e};var a=self.webpackJsonp=self.webpackJsonp||[],i=a.push.bind(a);a.push=n,a=a.slice();for(var f=0;f<a.length;f++)n(a[f]);var c=i;o(o.s=0)}([function(e,n,t){window.loadES5Adapter().then(function(){t.e(12).then(t.t.bind(null,1,7)),Promise.all([t.e(1),t.e(8)]).then(t.bind(null,3)),Promise.all([t.e(1),t.e(15),t.e(10)]).then(t.bind(null,2))});var r=document.createElement("style");r.innerHTML="\nbody {\n font-family: Roboto, sans-serif;\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-weight: 400;\n margin: 0;\n padding: 0;\n height: 100vh;\n}\n",document.head.appendChild(r)}]);
|
||||
//# sourceMappingURL=entrypoint.js.map
|
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -1,43 +1,43 @@
|
||||
{
|
||||
"vendors~confirmation~hassio-addon-view.js": "/api/hassio/app/chunk.b6e61f8340c32e6904ca.js",
|
||||
"vendors~confirmation~hassio-addon-view.js.map": "/api/hassio/app/chunk.b6e61f8340c32e6904ca.js.map",
|
||||
"vendors~hassio-icons~hassio-main.js": "/api/hassio/app/chunk.9339f70c8bfe2cbef5ad.js",
|
||||
"vendors~hassio-icons~hassio-main.js.map": "/api/hassio/app/chunk.9339f70c8bfe2cbef5ad.js.map",
|
||||
"codemirror.js": "/api/hassio/app/chunk.26756b56961f7bf94974.js",
|
||||
"codemirror.js.map": "/api/hassio/app/chunk.26756b56961f7bf94974.js.map",
|
||||
"confirmation.js": "/api/hassio/app/chunk.b2a892416a728ca06e9a.js",
|
||||
"confirmation.js.map": "/api/hassio/app/chunk.b2a892416a728ca06e9a.js.map",
|
||||
"dialog-hassio-markdown.js": "/api/hassio/app/chunk.26be881fcb628958e718.js",
|
||||
"dialog-hassio-markdown.js.map": "/api/hassio/app/chunk.26be881fcb628958e718.js.map",
|
||||
"dialog-hassio-snapshot.js": "/api/hassio/app/chunk.ea2041e4c67d4c05b0dd.js",
|
||||
"dialog-hassio-snapshot.js.map": "/api/hassio/app/chunk.ea2041e4c67d4c05b0dd.js.map",
|
||||
"vendors~confirmation~hassio-addon-view.js": "/api/hassio/app/chunk.7ab69340d9bb40dda79e.js",
|
||||
"vendors~confirmation~hassio-addon-view.js.map": "/api/hassio/app/chunk.7ab69340d9bb40dda79e.js.map",
|
||||
"vendors~hassio-icons~hassio-main.js": "/api/hassio/app/chunk.610423483939ffdbfb92.js",
|
||||
"vendors~hassio-icons~hassio-main.js.map": "/api/hassio/app/chunk.610423483939ffdbfb92.js.map",
|
||||
"codemirror.js": "/api/hassio/app/chunk.ece210525014eb36073b.js",
|
||||
"codemirror.js.map": "/api/hassio/app/chunk.ece210525014eb36073b.js.map",
|
||||
"confirmation.js": "/api/hassio/app/chunk.a947da09b1ace1352383.js",
|
||||
"confirmation.js.map": "/api/hassio/app/chunk.a947da09b1ace1352383.js.map",
|
||||
"dialog-hassio-markdown.js": "/api/hassio/app/chunk.d402ad0de6ea5da0f9fc.js",
|
||||
"dialog-hassio-markdown.js.map": "/api/hassio/app/chunk.d402ad0de6ea5da0f9fc.js.map",
|
||||
"dialog-hassio-snapshot.js": "/api/hassio/app/chunk.05d0c315f85f2fc8c34a.js",
|
||||
"dialog-hassio-snapshot.js.map": "/api/hassio/app/chunk.05d0c315f85f2fc8c34a.js.map",
|
||||
"entrypoint.js": "/api/hassio/app/entrypoint.js",
|
||||
"entrypoint.js.map": "/api/hassio/app/entrypoint.js.map",
|
||||
"hassio-addon-view.js": "/api/hassio/app/chunk.1a25d23325fed5a4d90b.js",
|
||||
"hassio-addon-view.js.map": "/api/hassio/app/chunk.1a25d23325fed5a4d90b.js.map",
|
||||
"hassio-addon-view.js": "/api/hassio/app/chunk.66f27f84503037304f17.js",
|
||||
"hassio-addon-view.js.map": "/api/hassio/app/chunk.66f27f84503037304f17.js.map",
|
||||
"hassio-icons.js": "/api/hassio/app/chunk.af76a4db9eb1e2862aae.js",
|
||||
"hassio-icons.js.map": "/api/hassio/app/chunk.af76a4db9eb1e2862aae.js.map",
|
||||
"hassio-ingress-view.js": "/api/hassio/app/chunk.35929da61d769e57c884.js",
|
||||
"hassio-ingress-view.js.map": "/api/hassio/app/chunk.35929da61d769e57c884.js.map",
|
||||
"hassio-main.js": "/api/hassio/app/chunk.5e32280d595be3742226.js",
|
||||
"hassio-main.js.map": "/api/hassio/app/chunk.5e32280d595be3742226.js.map",
|
||||
"mdi-icons.js": "/api/hassio/app/chunk.a9cf4ae83af78188e158.js",
|
||||
"mdi-icons.js.map": "/api/hassio/app/chunk.a9cf4ae83af78188e158.js.map",
|
||||
"hassio-ingress-view.js": "/api/hassio/app/chunk.54be96e339b8c82764a0.js",
|
||||
"hassio-ingress-view.js.map": "/api/hassio/app/chunk.54be96e339b8c82764a0.js.map",
|
||||
"hassio-main.js": "/api/hassio/app/chunk.7b1a3970d90b4082531e.js",
|
||||
"hassio-main.js.map": "/api/hassio/app/chunk.7b1a3970d90b4082531e.js.map",
|
||||
"mdi-icons.js": "/api/hassio/app/chunk.381907f8b6a21cec010f.js",
|
||||
"mdi-icons.js.map": "/api/hassio/app/chunk.381907f8b6a21cec010f.js.map",
|
||||
"roboto.js": "/api/hassio/app/chunk.b2dce600432c76a53d8c.js",
|
||||
"roboto.js.map": "/api/hassio/app/chunk.b2dce600432c76a53d8c.js.map",
|
||||
"vendors~codemirror.js": "/api/hassio/app/chunk.70a435e100109291f210.js",
|
||||
"vendors~codemirror.js.map": "/api/hassio/app/chunk.70a435e100109291f210.js.map",
|
||||
"vendors~hassio-addon-view.js": "/api/hassio/app/chunk.93a8a2e1dbccae0e07fa.js",
|
||||
"vendors~hassio-addon-view.js.map": "/api/hassio/app/chunk.93a8a2e1dbccae0e07fa.js.map",
|
||||
"vendors~hassio-main.js": "/api/hassio/app/chunk.541d0b76b660d8646074.js",
|
||||
"vendors~hassio-main.js.map": "/api/hassio/app/chunk.541d0b76b660d8646074.js.map",
|
||||
"vendors~hassio-addon-view.js": "/api/hassio/app/chunk.594ef9f33131c4d33409.js",
|
||||
"vendors~hassio-addon-view.js.map": "/api/hassio/app/chunk.594ef9f33131c4d33409.js.map",
|
||||
"vendors~hassio-main.js": "/api/hassio/app/chunk.7ac9df67ea34d63f8998.js",
|
||||
"vendors~hassio-main.js.map": "/api/hassio/app/chunk.7ac9df67ea34d63f8998.js.map",
|
||||
"a1ebfa0a88593a3b571c.worker.js": "/api/hassio/app/a1ebfa0a88593a3b571c.worker.js",
|
||||
"a1ebfa0a88593a3b571c.worker.js.map": "/api/hassio/app/a1ebfa0a88593a3b571c.worker.js.map",
|
||||
"chunk.26be881fcb628958e718.js.LICENSE": "/api/hassio/app/chunk.26be881fcb628958e718.js.LICENSE",
|
||||
"chunk.541d0b76b660d8646074.js.LICENSE": "/api/hassio/app/chunk.541d0b76b660d8646074.js.LICENSE",
|
||||
"chunk.9339f70c8bfe2cbef5ad.js.LICENSE": "/api/hassio/app/chunk.9339f70c8bfe2cbef5ad.js.LICENSE",
|
||||
"chunk.93a8a2e1dbccae0e07fa.js.LICENSE": "/api/hassio/app/chunk.93a8a2e1dbccae0e07fa.js.LICENSE",
|
||||
"chunk.b2a892416a728ca06e9a.js.LICENSE": "/api/hassio/app/chunk.b2a892416a728ca06e9a.js.LICENSE",
|
||||
"chunk.b6e61f8340c32e6904ca.js.LICENSE": "/api/hassio/app/chunk.b6e61f8340c32e6904ca.js.LICENSE",
|
||||
"chunk.ea2041e4c67d4c05b0dd.js.LICENSE": "/api/hassio/app/chunk.ea2041e4c67d4c05b0dd.js.LICENSE"
|
||||
"chunk.05d0c315f85f2fc8c34a.js.LICENSE": "/api/hassio/app/chunk.05d0c315f85f2fc8c34a.js.LICENSE",
|
||||
"chunk.594ef9f33131c4d33409.js.LICENSE": "/api/hassio/app/chunk.594ef9f33131c4d33409.js.LICENSE",
|
||||
"chunk.610423483939ffdbfb92.js.LICENSE": "/api/hassio/app/chunk.610423483939ffdbfb92.js.LICENSE",
|
||||
"chunk.7ab69340d9bb40dda79e.js.LICENSE": "/api/hassio/app/chunk.7ab69340d9bb40dda79e.js.LICENSE",
|
||||
"chunk.7ac9df67ea34d63f8998.js.LICENSE": "/api/hassio/app/chunk.7ac9df67ea34d63f8998.js.LICENSE",
|
||||
"chunk.a947da09b1ace1352383.js.LICENSE": "/api/hassio/app/chunk.a947da09b1ace1352383.js.LICENSE",
|
||||
"chunk.d402ad0de6ea5da0f9fc.js.LICENSE": "/api/hassio/app/chunk.d402ad0de6ea5da0f9fc.js.LICENSE"
|
||||
}
|
@@ -26,11 +26,11 @@ class APIProxy(CoreSysAttributes):
|
||||
"""Check the Supervisor token."""
|
||||
if AUTHORIZATION in request.headers:
|
||||
bearer = request.headers[AUTHORIZATION]
|
||||
hassio_token = bearer.split(" ")[-1]
|
||||
supervisor_token = bearer.split(" ")[-1]
|
||||
else:
|
||||
hassio_token = request.headers.get(HEADER_HA_ACCESS)
|
||||
supervisor_token = request.headers.get(HEADER_HA_ACCESS)
|
||||
|
||||
addon = self.sys_addons.from_token(hassio_token)
|
||||
addon = self.sys_addons.from_token(supervisor_token)
|
||||
if not addon:
|
||||
_LOGGER.warning("Unknown Home Assistant API access!")
|
||||
elif not addon.access_homeassistant_api:
|
||||
@@ -177,8 +177,10 @@ class APIProxy(CoreSysAttributes):
|
||||
|
||||
# Check API access
|
||||
response = await server.receive_json()
|
||||
hassio_token = response.get("api_password") or response.get("access_token")
|
||||
addon = self.sys_addons.from_token(hassio_token)
|
||||
supervisor_token = response.get("api_password") or response.get(
|
||||
"access_token"
|
||||
)
|
||||
addon = self.sys_addons.from_token(supervisor_token)
|
||||
|
||||
if not addon or not addon.access_homeassistant_api:
|
||||
_LOGGER.warning("Unauthorized WebSocket access!")
|
||||
|
@@ -75,6 +75,7 @@ ADDONS_ROLE_ACCESS = {
|
||||
r"^(?:"
|
||||
r"|/audio/.*"
|
||||
r"|/dns/.*"
|
||||
r"|/cli/.*"
|
||||
r"|/core/.+"
|
||||
r"|/homeassistant/.+"
|
||||
r"|/host/.+"
|
||||
@@ -123,12 +124,13 @@ class SecurityMiddleware(CoreSysAttributes):
|
||||
raise HTTPUnauthorized()
|
||||
|
||||
# Home-Assistant
|
||||
if supervisor_token == self.sys_homeassistant.hassio_token:
|
||||
if supervisor_token == self.sys_homeassistant.supervisor_token:
|
||||
_LOGGER.debug("%s access from Home Assistant", request.path)
|
||||
request_from = self.sys_homeassistant
|
||||
|
||||
# Host
|
||||
if supervisor_token == self.sys_machine_id:
|
||||
# Remove machine_id handling later if all use new CLI
|
||||
if supervisor_token in (self.sys_machine_id, self.sys_cli.supervisor_token):
|
||||
_LOGGER.debug("%s access from Host", request.path)
|
||||
request_from = self.sys_host
|
||||
|
||||
|
@@ -20,7 +20,7 @@ from ..const import (
|
||||
ATTR_ICON,
|
||||
ATTR_INSTALLED,
|
||||
ATTR_IP_ADDRESS,
|
||||
ATTR_LAST_VERSION,
|
||||
ATTR_VERSION_LATEST,
|
||||
ATTR_LOGGING,
|
||||
ATTR_LOGO,
|
||||
ATTR_MEMORY_LIMIT,
|
||||
@@ -92,7 +92,7 @@ class APISupervisor(CoreSysAttributes):
|
||||
|
||||
return {
|
||||
ATTR_VERSION: SUPERVISOR_VERSION,
|
||||
ATTR_LAST_VERSION: self.sys_updater.version_hassio,
|
||||
ATTR_VERSION_LATEST: self.sys_updater.version_hassio,
|
||||
ATTR_CHANNEL: self.sys_updater.channel,
|
||||
ATTR_ARCH: self.sys_supervisor.arch,
|
||||
ATTR_IP_ADDRESS: str(self.sys_supervisor.ip_address),
|
||||
|
@@ -49,7 +49,7 @@ class Audio(JsonConfig, CoreSysAttributes):
|
||||
|
||||
@version.setter
|
||||
def version(self, value: str) -> None:
|
||||
"""Return current version of Audio."""
|
||||
"""Set current version of Audio."""
|
||||
self._data[ATTR_VERSION] = value
|
||||
|
||||
@property
|
||||
|
@@ -14,6 +14,7 @@ from .auth import Auth
|
||||
from .audio import Audio
|
||||
from .const import SOCKET_DOCKER, UpdateChannels
|
||||
from .core import Core
|
||||
from .cli import HaCli
|
||||
from .coresys import CoreSys
|
||||
from .dbus import DBusManager
|
||||
from .discovery import Discovery
|
||||
@@ -67,6 +68,7 @@ async def initialize_coresys():
|
||||
coresys.dbus = DBusManager(coresys)
|
||||
coresys.hassos = HassOS(coresys)
|
||||
coresys.secrets = SecretsManager(coresys)
|
||||
coresys.cli = HaCli(coresys)
|
||||
|
||||
# bootstrap config
|
||||
initialize_system_data(coresys)
|
||||
|
162
supervisor/cli.py
Normal file
162
supervisor/cli.py
Normal file
@@ -0,0 +1,162 @@
|
||||
"""CLI support on supervisor."""
|
||||
import asyncio
|
||||
from contextlib import suppress
|
||||
import logging
|
||||
import secrets
|
||||
from typing import Awaitable, Optional
|
||||
|
||||
from .const import ATTR_ACCESS_TOKEN, ATTR_VERSION, FILE_HASSIO_CLI
|
||||
from .coresys import CoreSys, CoreSysAttributes
|
||||
from .docker.cli import DockerCli
|
||||
from .docker.stats import DockerStats
|
||||
from .exceptions import CliError, CliUpdateError, DockerAPIError
|
||||
from .utils.json import JsonConfig
|
||||
from .validate import SCHEMA_CLI_CONFIG
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HaCli(CoreSysAttributes, JsonConfig):
|
||||
"""HA cli interface inside supervisor."""
|
||||
|
||||
def __init__(self, coresys: CoreSys):
|
||||
"""Initialize cli handler."""
|
||||
super().__init__(FILE_HASSIO_CLI, SCHEMA_CLI_CONFIG)
|
||||
self.coresys: CoreSys = coresys
|
||||
self.instance: DockerCli = DockerCli(coresys)
|
||||
|
||||
@property
|
||||
def version(self) -> Optional[str]:
|
||||
"""Return version of cli."""
|
||||
return self._data.get(ATTR_VERSION)
|
||||
|
||||
@version.setter
|
||||
def version(self, value: str) -> None:
|
||||
"""Set current version of cli."""
|
||||
self._data[ATTR_VERSION] = value
|
||||
|
||||
@property
|
||||
def latest_version(self) -> str:
|
||||
"""Return version of latest cli."""
|
||||
return self.sys_updater.version_cli
|
||||
|
||||
@property
|
||||
def need_update(self) -> bool:
|
||||
"""Return true if a cli update is available."""
|
||||
return self.version != self.latest_version
|
||||
|
||||
@property
|
||||
def supervisor_token(self) -> str:
|
||||
"""Return an access token for the Supervisor API."""
|
||||
return self._data.get(ATTR_ACCESS_TOKEN)
|
||||
|
||||
@property
|
||||
def in_progress(self) -> bool:
|
||||
"""Return True if a task is in progress."""
|
||||
return self.instance.in_progress
|
||||
|
||||
async def load(self) -> None:
|
||||
"""Load cli setup."""
|
||||
# Check cli state
|
||||
try:
|
||||
# Evaluate Version if we lost this information
|
||||
if not self.version:
|
||||
self.version = await self.instance.get_latest_version(key=int)
|
||||
|
||||
await self.instance.attach(tag=self.version)
|
||||
except DockerAPIError:
|
||||
_LOGGER.info("No cli plugin Docker image %s found.", self.instance.image)
|
||||
|
||||
# Install cli
|
||||
with suppress(CliError):
|
||||
await self.install()
|
||||
else:
|
||||
self.version = self.instance.version
|
||||
self.save_data()
|
||||
|
||||
# Run PulseAudio
|
||||
with suppress(CliError):
|
||||
if not await self.instance.is_running():
|
||||
await self.start()
|
||||
|
||||
async def install(self) -> None:
|
||||
"""Install cli."""
|
||||
_LOGGER.info("Setup cli plugin")
|
||||
while True:
|
||||
# read audio tag and install it
|
||||
if not self.latest_version:
|
||||
await self.sys_updater.reload()
|
||||
|
||||
if self.latest_version:
|
||||
with suppress(DockerAPIError):
|
||||
await self.instance.install(self.latest_version, latest=True)
|
||||
break
|
||||
_LOGGER.warning("Error on install cli plugin. Retry in 30sec")
|
||||
await asyncio.sleep(30)
|
||||
|
||||
_LOGGER.info("cli plugin now installed")
|
||||
self.version = self.instance.version
|
||||
self.save_data()
|
||||
|
||||
async def update(self, version: Optional[str] = None) -> None:
|
||||
"""Update local HA cli."""
|
||||
version = version or self.latest_version
|
||||
|
||||
if version == self.version:
|
||||
_LOGGER.warning("Version %s is already installed for cli", version)
|
||||
return
|
||||
|
||||
try:
|
||||
await self.instance.update(version, latest=True)
|
||||
except DockerAPIError:
|
||||
_LOGGER.error("HA cli update fails")
|
||||
raise CliUpdateError() from None
|
||||
|
||||
# Cleanup
|
||||
with suppress(DockerAPIError):
|
||||
await self.instance.cleanup()
|
||||
|
||||
self.version = version
|
||||
self.save_data()
|
||||
|
||||
# Start cli
|
||||
await self.start()
|
||||
|
||||
async def start(self) -> None:
|
||||
"""Run cli."""
|
||||
# Create new API token
|
||||
self._data[ATTR_ACCESS_TOKEN] = secrets.token_hex(56)
|
||||
self.save_data()
|
||||
|
||||
# Start Instance
|
||||
_LOGGER.info("Start cli plugin")
|
||||
try:
|
||||
await self.instance.run()
|
||||
except DockerAPIError:
|
||||
_LOGGER.error("Can't start cli plugin")
|
||||
raise CliError() from None
|
||||
|
||||
async def stats(self) -> DockerStats:
|
||||
"""Return stats of cli."""
|
||||
try:
|
||||
return await self.instance.stats()
|
||||
except DockerAPIError:
|
||||
raise CliError() from None
|
||||
|
||||
def is_running(self) -> Awaitable[bool]:
|
||||
"""Return True if Docker container is running.
|
||||
|
||||
Return a coroutine.
|
||||
"""
|
||||
return self.instance.is_running()
|
||||
|
||||
async def repair(self) -> None:
|
||||
"""Repair cli container."""
|
||||
if await self.instance.exists():
|
||||
return
|
||||
|
||||
_LOGGER.info("Repair HA cli %s", self.version)
|
||||
try:
|
||||
await self.instance.install(self.version, latest=True)
|
||||
except DockerAPIError:
|
||||
_LOGGER.error("Repairing of HA cli fails")
|
@@ -3,7 +3,7 @@ from enum import Enum
|
||||
from ipaddress import ip_network
|
||||
from pathlib import Path
|
||||
|
||||
SUPERVISOR_VERSION = "208"
|
||||
SUPERVISOR_VERSION = "212"
|
||||
|
||||
|
||||
URL_HASSIO_ADDONS = "https://github.com/home-assistant/hassio-addons"
|
||||
@@ -27,6 +27,7 @@ FILE_HASSIO_DISCOVERY = Path(SUPERVISOR_DATA, "discovery.json")
|
||||
FILE_HASSIO_INGRESS = Path(SUPERVISOR_DATA, "ingress.json")
|
||||
FILE_HASSIO_DNS = Path(SUPERVISOR_DATA, "dns.json")
|
||||
FILE_HASSIO_AUDIO = Path(SUPERVISOR_DATA, "audio.json")
|
||||
FILE_HASSIO_CLI = Path(SUPERVISOR_DATA, "cli.json")
|
||||
|
||||
SOCKET_DOCKER = Path("/run/docker.sock")
|
||||
|
||||
@@ -34,7 +35,6 @@ DOCKER_NETWORK = "hassio"
|
||||
DOCKER_NETWORK_MASK = ip_network("172.30.32.0/23")
|
||||
DOCKER_NETWORK_RANGE = ip_network("172.30.33.0/24")
|
||||
|
||||
DNS_SERVERS = ["dns://1.1.1.1", "dns://9.9.9.9"]
|
||||
DNS_SUFFIX = "local.hass.io"
|
||||
|
||||
LABEL_VERSION = "io.hass.version"
|
||||
@@ -93,8 +93,6 @@ ATTR_VERSION = "version"
|
||||
ATTR_VERSION_LATEST = "version_latest"
|
||||
ATTR_AUTO_UART = "auto_uart"
|
||||
ATTR_LAST_BOOT = "last_boot"
|
||||
ATTR_LAST_VERSION = "last_version"
|
||||
ATTR_LATEST_VERSION = "latest_version"
|
||||
ATTR_CHANNEL = "channel"
|
||||
ATTR_NAME = "name"
|
||||
ATTR_SLUG = "slug"
|
||||
@@ -190,9 +188,6 @@ ATTR_DEVICETREE = "devicetree"
|
||||
ATTR_CPE = "cpe"
|
||||
ATTR_BOARD = "board"
|
||||
ATTR_HASSOS = "hassos"
|
||||
ATTR_HASSOS_CLI = "hassos_cli"
|
||||
ATTR_VERSION_CLI = "version_cli"
|
||||
ATTR_VERSION_CLI_LATEST = "version_cli_latest"
|
||||
ATTR_REFRESH_TOKEN = "refresh_token"
|
||||
ATTR_ACCESS_TOKEN = "access_token"
|
||||
ATTR_DOCKER_API = "docker_api"
|
||||
|
@@ -41,7 +41,9 @@ class Core(CoreSysAttributes):
|
||||
await self.sys_host.load()
|
||||
|
||||
# Load Plugins container
|
||||
await asyncio.wait([self.sys_dns.load(), self.sys_audio.load()])
|
||||
await asyncio.wait(
|
||||
[self.sys_dns.load(), self.sys_audio.load(), self.sys_cli.load()]
|
||||
)
|
||||
|
||||
# Load Home Assistant
|
||||
await self.sys_homeassistant.load()
|
||||
@@ -203,13 +205,11 @@ class Core(CoreSysAttributes):
|
||||
|
||||
# Restore core functionality
|
||||
await self.sys_dns.repair()
|
||||
await self.sys_audio.repair()
|
||||
await self.sys_cli.repair()
|
||||
await self.sys_addons.repair()
|
||||
await self.sys_homeassistant.repair()
|
||||
|
||||
# Fix HassOS specific
|
||||
if self.sys_hassos.available:
|
||||
await self.sys_hassos.repair_cli()
|
||||
|
||||
# Tag version for latest
|
||||
await self.sys_supervisor.repair()
|
||||
_LOGGER.info("Finished repairing of Supervisor Environment")
|
||||
|
@@ -18,6 +18,7 @@ if TYPE_CHECKING:
|
||||
from .audio import Audio
|
||||
from .auth import Auth
|
||||
from .core import Core
|
||||
from .cli import HaCli
|
||||
from .dbus import DBusManager
|
||||
from .discovery import Discovery
|
||||
from .dns import CoreDNS
|
||||
@@ -62,6 +63,7 @@ class CoreSys:
|
||||
self._audio: Optional[Audio] = None
|
||||
self._auth: Optional[Auth] = None
|
||||
self._dns: Optional[CoreDNS] = None
|
||||
self._cli: Optional[HaCli] = None
|
||||
self._homeassistant: Optional[HomeAssistant] = None
|
||||
self._supervisor: Optional[Supervisor] = None
|
||||
self._addons: Optional[AddonManager] = None
|
||||
@@ -143,6 +145,18 @@ class CoreSys:
|
||||
raise RuntimeError("Core already set!")
|
||||
self._core = value
|
||||
|
||||
@property
|
||||
def cli(self) -> HaCli:
|
||||
"""Return HaCli object."""
|
||||
return self._cli
|
||||
|
||||
@cli.setter
|
||||
def cli(self, value: HaCli):
|
||||
"""Set a HaCli object."""
|
||||
if self._cli:
|
||||
raise RuntimeError("HaCli already set!")
|
||||
self._cli = value
|
||||
|
||||
@property
|
||||
def arch(self) -> CpuArch:
|
||||
"""Return CpuArch object."""
|
||||
@@ -449,6 +463,11 @@ class CoreSysAttributes:
|
||||
"""Return core object."""
|
||||
return self.coresys.core
|
||||
|
||||
@property
|
||||
def sys_cli(self) -> HaCli:
|
||||
"""Return HaCli object."""
|
||||
return self.coresys.cli
|
||||
|
||||
@property
|
||||
def sys_arch(self) -> CpuArch:
|
||||
"""Return CpuArch object."""
|
||||
|
@@ -1,15 +1,31 @@
|
||||
.:53 {
|
||||
log
|
||||
errors
|
||||
loop
|
||||
hosts /config/hosts {
|
||||
fallthrough
|
||||
}
|
||||
template ANY AAAA local.hass.io hassio {
|
||||
rcode NOERROR
|
||||
}
|
||||
forward . $servers {
|
||||
forward . {{ locals | join(" ") }} dns://127.0.0.1:5353 {
|
||||
except local.hass.io
|
||||
policy sequential
|
||||
health_check 5s
|
||||
}
|
||||
fallback REFUSED . dns://127.0.0.1:5353
|
||||
fallback SERVFAIL . dns://127.0.0.1:5353
|
||||
fallback NXDOMAIN . dns://127.0.0.1:5353
|
||||
cache 10
|
||||
}
|
||||
|
||||
.:5353 {
|
||||
log
|
||||
errors
|
||||
forward . tls://1.1.1.1 tls://1.0.0.1 {
|
||||
tls_servername cloudflare-dns.com
|
||||
except local.hass.io
|
||||
health_check 10s
|
||||
}
|
||||
cache 30
|
||||
}
|
||||
|
@@ -1,11 +0,0 @@
|
||||
"""Discovery service for Home Panel."""
|
||||
import voluptuous as vol
|
||||
|
||||
from supervisor.validate import network_port
|
||||
|
||||
from ..const import ATTR_HOST, ATTR_PORT
|
||||
|
||||
|
||||
SCHEMA = vol.Schema(
|
||||
{vol.Required(ATTR_HOST): vol.Coerce(str), vol.Required(ATTR_PORT): network_port}
|
||||
)
|
@@ -4,13 +4,13 @@ from contextlib import suppress
|
||||
from ipaddress import IPv4Address
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from string import Template
|
||||
from typing import Awaitable, List, Optional
|
||||
|
||||
import attr
|
||||
import jinja2
|
||||
import voluptuous as vol
|
||||
|
||||
from .const import ATTR_SERVERS, ATTR_VERSION, DNS_SERVERS, DNS_SUFFIX, FILE_HASSIO_DNS
|
||||
from .const import ATTR_SERVERS, ATTR_VERSION, DNS_SUFFIX, FILE_HASSIO_DNS
|
||||
from .coresys import CoreSys, CoreSysAttributes
|
||||
from .docker.dns import DockerDNS
|
||||
from .docker.stats import DockerStats
|
||||
@@ -42,8 +42,10 @@ class CoreDNS(JsonConfig, CoreSysAttributes):
|
||||
self.coresys: CoreSys = coresys
|
||||
self.instance: DockerDNS = DockerDNS(coresys)
|
||||
self.forwarder: DNSForward = DNSForward()
|
||||
self.coredns_template: Optional[jinja2.Template] = None
|
||||
|
||||
self._hosts: List[HostEntry] = []
|
||||
self._loop: bool = False
|
||||
|
||||
@property
|
||||
def corefile(self) -> Path:
|
||||
@@ -116,6 +118,12 @@ class CoreDNS(JsonConfig, CoreSysAttributes):
|
||||
# Start DNS forwarder
|
||||
self.sys_create_task(self.forwarder.start(self.sys_docker.network.dns))
|
||||
|
||||
# Initialize CoreDNS Template
|
||||
try:
|
||||
self.coredns_template = jinja2.Template(COREDNS_TMPL.read_text())
|
||||
except OSError as err:
|
||||
_LOGGER.error("Can't read coredns.tmpl: %s", err)
|
||||
|
||||
# Run CoreDNS
|
||||
with suppress(CoreDNSError):
|
||||
if await self.instance.is_running():
|
||||
@@ -157,15 +165,16 @@ class CoreDNS(JsonConfig, CoreSysAttributes):
|
||||
_LOGGER.warning("Version %s is already installed for CoreDNS", version)
|
||||
return
|
||||
|
||||
# Update
|
||||
try:
|
||||
await self.instance.update(version)
|
||||
except DockerAPIError:
|
||||
_LOGGER.error("CoreDNS update fails")
|
||||
raise CoreDNSUpdateError() from None
|
||||
else:
|
||||
# Cleanup
|
||||
with suppress(DockerAPIError):
|
||||
await self.instance.cleanup()
|
||||
|
||||
# Cleanup
|
||||
with suppress(DockerAPIError):
|
||||
await self.instance.cleanup()
|
||||
|
||||
self.version = version
|
||||
self.save_data()
|
||||
@@ -202,30 +211,43 @@ class CoreDNS(JsonConfig, CoreSysAttributes):
|
||||
self.hosts.unlink()
|
||||
self._init_hosts()
|
||||
|
||||
# Reset loop protection
|
||||
self._loop = False
|
||||
|
||||
await self.sys_addons.sync_dns()
|
||||
|
||||
async def loop_detection(self) -> None:
|
||||
"""Check if there was a loop found."""
|
||||
log = await self.instance.logs()
|
||||
|
||||
# Check the log for loop plugin output
|
||||
if b"plugin/loop: Loop" in log:
|
||||
_LOGGER.error("Detect a DNS loop in local Network!")
|
||||
self._loop = True
|
||||
else:
|
||||
self._loop = False
|
||||
|
||||
def _write_corefile(self) -> None:
|
||||
"""Write CoreDNS config."""
|
||||
dns_servers: List[str] = []
|
||||
|
||||
# Load Template
|
||||
try:
|
||||
corefile_template: Template = Template(COREDNS_TMPL.read_text())
|
||||
except OSError as err:
|
||||
_LOGGER.error("Can't read coredns template file: %s", err)
|
||||
raise CoreDNSError() from None
|
||||
local_dns: List[str] = []
|
||||
servers: List[str] = []
|
||||
|
||||
# Prepare DNS serverlist: Prio 1 Manual, Prio 2 Local, Prio 3 Fallback
|
||||
local_dns: List[str] = self.sys_host.network.dns_servers or ["dns://127.0.0.11"]
|
||||
servers: List[str] = self.servers + local_dns + DNS_SERVERS
|
||||
if not self._loop:
|
||||
local_dns = self.sys_host.network.dns_servers or ["dns://127.0.0.11"]
|
||||
servers = self.servers + local_dns
|
||||
else:
|
||||
_LOGGER.warning("Ignore user DNS settings because of loop")
|
||||
|
||||
# Print some usefully debug data
|
||||
_LOGGER.debug(
|
||||
"config-dns = %s, local-dns = %s , backup-dns = %s",
|
||||
"config-dns = %s, local-dns = %s , backup-dns = CloudFlare DoT",
|
||||
self.servers,
|
||||
local_dns,
|
||||
DNS_SERVERS,
|
||||
)
|
||||
|
||||
# Make sure, they are valid
|
||||
for server in servers:
|
||||
try:
|
||||
dns_url(server)
|
||||
@@ -235,7 +257,7 @@ class CoreDNS(JsonConfig, CoreSysAttributes):
|
||||
_LOGGER.warning("Ignore invalid DNS Server: %s", server)
|
||||
|
||||
# Generate config file
|
||||
data = corefile_template.safe_substitute(servers=" ".join(dns_servers))
|
||||
data = self.coredns_template.render(locals=dns_servers)
|
||||
|
||||
try:
|
||||
self.corefile.write_text(data)
|
||||
@@ -339,7 +361,6 @@ class CoreDNS(JsonConfig, CoreSysAttributes):
|
||||
|
||||
def is_fails(self) -> Awaitable[bool]:
|
||||
"""Return True if a Docker container is fails state.
|
||||
|
||||
Return a coroutine.
|
||||
"""
|
||||
return self.instance.is_fails()
|
||||
|
@@ -117,8 +117,8 @@ class DockerAddon(DockerInterface):
|
||||
return {
|
||||
**addon_env,
|
||||
ENV_TIME: self.sys_timezone,
|
||||
ENV_TOKEN: self.addon.hassio_token,
|
||||
ENV_TOKEN_OLD: self.addon.hassio_token,
|
||||
ENV_TOKEN: self.addon.supervisor_token,
|
||||
ENV_TOKEN_OLD: self.addon.supervisor_token,
|
||||
}
|
||||
|
||||
@property
|
||||
|
64
supervisor/docker/cli.py
Normal file
64
supervisor/docker/cli.py
Normal file
@@ -0,0 +1,64 @@
|
||||
"""HA Cli docker object."""
|
||||
from contextlib import suppress
|
||||
import logging
|
||||
|
||||
from ..coresys import CoreSysAttributes
|
||||
from ..exceptions import DockerAPIError
|
||||
from .interface import DockerInterface
|
||||
from ..const import ENV_TIME, ENV_TOKEN
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
CLI_DOCKER_NAME: str = "hassio_cli"
|
||||
|
||||
|
||||
class DockerCli(DockerInterface, CoreSysAttributes):
|
||||
"""Docker Supervisor wrapper for HA cli."""
|
||||
|
||||
@property
|
||||
def image(self):
|
||||
"""Return name of HA cli image."""
|
||||
return f"homeassistant/{self.sys_arch.supervisor}-hassio-cli"
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return name of Docker container."""
|
||||
return CLI_DOCKER_NAME
|
||||
|
||||
def _run(self) -> None:
|
||||
"""Run Docker image.
|
||||
|
||||
Need run inside executor.
|
||||
"""
|
||||
if self._is_running():
|
||||
return
|
||||
|
||||
# Cleanup
|
||||
with suppress(DockerAPIError):
|
||||
self._stop()
|
||||
|
||||
# Create & Run container
|
||||
docker_container = self.sys_docker.run(
|
||||
self.image,
|
||||
entrypoint=["/init"],
|
||||
command=["/bin/bash", "-c", "sleep infinity"],
|
||||
version=self.sys_cli.version,
|
||||
init=False,
|
||||
ipv4=self.sys_docker.network.cli,
|
||||
name=self.name,
|
||||
hostname=self.name.replace("_", "-"),
|
||||
detach=True,
|
||||
extra_hosts={"supervisor": self.sys_docker.network.supervisor},
|
||||
environment={
|
||||
ENV_TIME: self.sys_timezone,
|
||||
ENV_TOKEN: self.sys_cli.supervisor_token,
|
||||
},
|
||||
)
|
||||
|
||||
self._meta = docker_container.attrs
|
||||
_LOGGER.info(
|
||||
"Start CLI %s with version %s - %s",
|
||||
self.image,
|
||||
self.version,
|
||||
self.sys_docker.network.audio,
|
||||
)
|
@@ -1,38 +0,0 @@
|
||||
"""HassOS Cli docker object."""
|
||||
import logging
|
||||
|
||||
import docker
|
||||
|
||||
from ..coresys import CoreSysAttributes
|
||||
from .interface import DockerInterface
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DockerHassOSCli(DockerInterface, CoreSysAttributes):
|
||||
"""Docker Supervisor wrapper for HassOS Cli."""
|
||||
|
||||
@property
|
||||
def image(self):
|
||||
"""Return name of HassOS CLI image."""
|
||||
return f"homeassistant/{self.sys_arch.supervisor}-hassio-cli"
|
||||
|
||||
def _stop(self, remove_container=True):
|
||||
"""Don't need stop."""
|
||||
return True
|
||||
|
||||
def _attach(self, tag: str):
|
||||
"""Attach to running Docker container.
|
||||
Need run inside executor.
|
||||
"""
|
||||
try:
|
||||
image = self.sys_docker.images.get(f"{self.image}:{tag}")
|
||||
|
||||
except docker.errors.DockerException:
|
||||
_LOGGER.warning("Can't find a HassOS CLI %s", self.image)
|
||||
|
||||
else:
|
||||
self._meta = image.attrs
|
||||
_LOGGER.info(
|
||||
"Found HassOS CLI %s with version %s", self.image, self.version
|
||||
)
|
@@ -112,8 +112,8 @@ class DockerHomeAssistant(DockerInterface):
|
||||
"HASSIO": self.sys_docker.network.supervisor,
|
||||
"SUPERVISOR": self.sys_docker.network.supervisor,
|
||||
ENV_TIME: self.sys_timezone,
|
||||
ENV_TOKEN: self.sys_homeassistant.hassio_token,
|
||||
ENV_TOKEN_OLD: self.sys_homeassistant.hassio_token,
|
||||
ENV_TOKEN: self.sys_homeassistant.supervisor_token,
|
||||
ENV_TOKEN_OLD: self.sys_homeassistant.supervisor_token,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -135,7 +135,17 @@ class DockerHomeAssistant(DockerInterface):
|
||||
detach=True,
|
||||
stdout=True,
|
||||
stderr=True,
|
||||
volumes=self.volumes,
|
||||
volumes={
|
||||
str(self.sys_config.path_extern_homeassistant): {
|
||||
"bind": "/config",
|
||||
"mode": "rw",
|
||||
},
|
||||
str(self.sys_config.path_extern_ssl): {"bind": "/ssl", "mode": "ro"},
|
||||
str(self.sys_config.path_extern_share): {
|
||||
"bind": "/share",
|
||||
"mode": "ro",
|
||||
},
|
||||
},
|
||||
environment={ENV_TIME: self.sys_timezone},
|
||||
)
|
||||
|
||||
|
@@ -53,6 +53,11 @@ class DockerNetwork:
|
||||
"""Return audio of the network."""
|
||||
return DOCKER_NETWORK_MASK[4]
|
||||
|
||||
@property
|
||||
def cli(self) -> IPv4Address:
|
||||
"""Return cli of the network."""
|
||||
return DOCKER_NETWORK_MASK[5]
|
||||
|
||||
def _get_network(self) -> docker.models.networks.Network:
|
||||
"""Get supervisor network."""
|
||||
try:
|
||||
|
@@ -54,6 +54,17 @@ class HassOSNotSupportedError(HassioNotSupportedError):
|
||||
"""Function not supported by HassOS."""
|
||||
|
||||
|
||||
# HaCli
|
||||
|
||||
|
||||
class CliError(HassioError):
|
||||
"""HA cli exception."""
|
||||
|
||||
|
||||
class CliUpdateError(HassOSError):
|
||||
"""Error on update of a HA cli."""
|
||||
|
||||
|
||||
# DNS
|
||||
|
||||
|
||||
|
@@ -1,6 +1,5 @@
|
||||
"""HassOS support on supervisor."""
|
||||
import asyncio
|
||||
from contextlib import suppress
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Awaitable, Optional
|
||||
@@ -10,12 +9,10 @@ from cpe import CPE
|
||||
|
||||
from .const import URL_HASSOS_OTA
|
||||
from .coresys import CoreSysAttributes, CoreSys
|
||||
from .docker.hassos_cli import DockerHassOSCli
|
||||
from .exceptions import (
|
||||
DBusError,
|
||||
HassOSNotSupportedError,
|
||||
HassOSUpdateError,
|
||||
DockerAPIError,
|
||||
)
|
||||
from .dbus.rauc import RaucState
|
||||
|
||||
@@ -28,7 +25,6 @@ class HassOS(CoreSysAttributes):
|
||||
def __init__(self, coresys: CoreSys):
|
||||
"""Initialize HassOS handler."""
|
||||
self.coresys: CoreSys = coresys
|
||||
self.instance: DockerHassOSCli = DockerHassOSCli(coresys)
|
||||
self._available: bool = False
|
||||
self._version: Optional[str] = None
|
||||
self._board: Optional[str] = None
|
||||
@@ -44,29 +40,14 @@ class HassOS(CoreSysAttributes):
|
||||
return self._version
|
||||
|
||||
@property
|
||||
def version_cli(self) -> Optional[str]:
|
||||
"""Return version of HassOS cli."""
|
||||
return self.instance.version
|
||||
|
||||
@property
|
||||
def version_latest(self) -> str:
|
||||
def latest_version(self) -> str:
|
||||
"""Return version of HassOS."""
|
||||
return self.sys_updater.version_hassos
|
||||
|
||||
@property
|
||||
def version_cli_latest(self) -> str:
|
||||
"""Return version of HassOS."""
|
||||
return self.sys_updater.version_cli
|
||||
|
||||
@property
|
||||
def need_update(self) -> bool:
|
||||
"""Return true if a HassOS update is available."""
|
||||
return self.version != self.version_latest
|
||||
|
||||
@property
|
||||
def need_cli_update(self) -> bool:
|
||||
"""Return true if a HassOS cli update is available."""
|
||||
return self.version_cli != self.version_cli_latest
|
||||
return self.version != self.latest_version
|
||||
|
||||
@property
|
||||
def board(self) -> Optional[str]:
|
||||
@@ -134,8 +115,6 @@ class HassOS(CoreSysAttributes):
|
||||
_LOGGER.info(
|
||||
"Detect HassOS %s / BootSlot %s", self.version, self.sys_dbus.rauc.boot_slot
|
||||
)
|
||||
with suppress(DockerAPIError):
|
||||
await self.instance.attach(tag="latest")
|
||||
|
||||
def config_sync(self) -> Awaitable[None]:
|
||||
"""Trigger a host config reload from usb.
|
||||
@@ -149,7 +128,7 @@ class HassOS(CoreSysAttributes):
|
||||
|
||||
async def update(self, version: Optional[str] = None) -> None:
|
||||
"""Update HassOS system."""
|
||||
version = version or self.version_latest
|
||||
version = version or self.latest_version
|
||||
|
||||
# Check installed version
|
||||
self._check_host()
|
||||
@@ -183,35 +162,6 @@ class HassOS(CoreSysAttributes):
|
||||
_LOGGER.error("HassOS update fails with: %s", self.sys_dbus.rauc.last_error)
|
||||
raise HassOSUpdateError()
|
||||
|
||||
async def update_cli(self, version: Optional[str] = None) -> None:
|
||||
"""Update local HassOS cli."""
|
||||
version = version or self.version_cli_latest
|
||||
|
||||
if version == self.version_cli:
|
||||
_LOGGER.warning("Version %s is already installed for CLI", version)
|
||||
return
|
||||
|
||||
try:
|
||||
await self.instance.update(version, latest=True)
|
||||
except DockerAPIError:
|
||||
_LOGGER.error("HassOS CLI update fails")
|
||||
raise HassOSUpdateError() from None
|
||||
else:
|
||||
# Cleanup
|
||||
with suppress(DockerAPIError):
|
||||
await self.instance.cleanup()
|
||||
|
||||
async def repair_cli(self) -> None:
|
||||
"""Repair CLI container."""
|
||||
if await self.instance.exists():
|
||||
return
|
||||
|
||||
_LOGGER.info("Repair HassOS CLI %s", self.version_cli)
|
||||
try:
|
||||
await self.instance.install(self.version_cli, latest=True)
|
||||
except DockerAPIError:
|
||||
_LOGGER.error("Repairing of HassOS CLI fails")
|
||||
|
||||
async def mark_healthy(self):
|
||||
"""Set booted partition as good for rauc."""
|
||||
try:
|
||||
|
@@ -8,6 +8,7 @@ import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import secrets
|
||||
import shutil
|
||||
import time
|
||||
from typing import Any, AsyncContextManager, Awaitable, Dict, Optional
|
||||
from uuid import UUID
|
||||
@@ -23,7 +24,7 @@ from .const import (
|
||||
ATTR_AUDIO_OUTPUT,
|
||||
ATTR_BOOT,
|
||||
ATTR_IMAGE,
|
||||
ATTR_LAST_VERSION,
|
||||
ATTR_VERSION_LATEST,
|
||||
ATTR_PORT,
|
||||
ATTR_REFRESH_TOKEN,
|
||||
ATTR_SSL,
|
||||
@@ -163,16 +164,16 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
||||
def latest_version(self) -> str:
|
||||
"""Return last available version of Home Assistant."""
|
||||
if self.is_custom_image:
|
||||
return self._data.get(ATTR_LAST_VERSION)
|
||||
return self._data.get(ATTR_VERSION_LATEST)
|
||||
return self.sys_updater.version_homeassistant
|
||||
|
||||
@latest_version.setter
|
||||
def latest_version(self, value: str):
|
||||
"""Set last available version of Home Assistant."""
|
||||
if value:
|
||||
self._data[ATTR_LAST_VERSION] = value
|
||||
self._data[ATTR_VERSION_LATEST] = value
|
||||
else:
|
||||
self._data.pop(ATTR_LAST_VERSION, None)
|
||||
self._data.pop(ATTR_VERSION_LATEST, None)
|
||||
|
||||
@property
|
||||
def image(self) -> str:
|
||||
@@ -192,7 +193,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
||||
@property
|
||||
def is_custom_image(self) -> bool:
|
||||
"""Return True if a custom image is used."""
|
||||
return all(attr in self._data for attr in (ATTR_IMAGE, ATTR_LAST_VERSION))
|
||||
return all(attr in self._data for attr in (ATTR_IMAGE, ATTR_VERSION_LATEST))
|
||||
|
||||
@property
|
||||
def version(self) -> Optional[str]:
|
||||
@@ -220,7 +221,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
||||
return self._data[ATTR_UUID]
|
||||
|
||||
@property
|
||||
def hassio_token(self) -> str:
|
||||
def supervisor_token(self) -> str:
|
||||
"""Return an access token for the Supervisor API."""
|
||||
return self._data.get(ATTR_ACCESS_TOKEN)
|
||||
|
||||
@@ -237,12 +238,12 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
||||
@property
|
||||
def path_pulse(self):
|
||||
"""Return path to asound config."""
|
||||
return Path(self.sys_config.path_tmp, f"homeassistant_pulse")
|
||||
return Path(self.sys_config.path_tmp, "homeassistant_pulse")
|
||||
|
||||
@property
|
||||
def path_extern_pulse(self):
|
||||
"""Return path to asound config for Docker."""
|
||||
return Path(self.sys_config.path_extern_tmp, f"homeassistant_pulse")
|
||||
return Path(self.sys_config.path_extern_tmp, "homeassistant_pulse")
|
||||
|
||||
@property
|
||||
def audio_output(self) -> Optional[str]:
|
||||
@@ -464,13 +465,16 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
||||
"python3 -m homeassistant -c /config --script check_config"
|
||||
)
|
||||
|
||||
# if not valid
|
||||
# If not valid
|
||||
if result.exit_code is None:
|
||||
_LOGGER.error("Fatal error on config check!")
|
||||
raise HomeAssistantError()
|
||||
|
||||
# parse output
|
||||
# Convert output
|
||||
log = convert_to_ascii(result.output)
|
||||
_LOGGER.debug("Result config check: %s", log.strip())
|
||||
|
||||
# Parse output
|
||||
if result.exit_code != 0 or RE_YAML_ERROR.search(log):
|
||||
_LOGGER.error("Invalid Home Assistant config found!")
|
||||
return ConfigResult(False, log)
|
||||
@@ -644,11 +648,15 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
||||
input_profile=self.audio_input, output_profile=self.audio_output
|
||||
)
|
||||
|
||||
# Cleanup wrong maps
|
||||
if self.path_pulse.is_dir():
|
||||
shutil.rmtree(self.path_pulse, ignore_errors=True)
|
||||
|
||||
# Write pulse config
|
||||
try:
|
||||
with self.path_pulse.open("w") as config_file:
|
||||
config_file.write(pulse_config)
|
||||
except OSError as err:
|
||||
_LOGGER.error("Home Assistant can't write pulse/client.config: %s", err)
|
||||
raise HomeAssistantError()
|
||||
|
||||
_LOGGER.debug("Home Assistant write pulse/client.config: %s", self.path_pulse)
|
||||
else:
|
||||
_LOGGER.info("Update pulse/client.config: %s", self.path_pulse)
|
||||
|
@@ -1,9 +1,15 @@
|
||||
"""Info control for host."""
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from ..coresys import CoreSysAttributes
|
||||
from ..exceptions import HostNotSupportedError, DBusNotConnectedError, DBusError
|
||||
from ..exceptions import (
|
||||
HostNotSupportedError,
|
||||
HostError,
|
||||
DBusNotConnectedError,
|
||||
DBusError,
|
||||
)
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -45,6 +51,21 @@ class InfoCenter(CoreSysAttributes):
|
||||
"""Return local CPE."""
|
||||
return self.sys_dbus.hostname.cpe
|
||||
|
||||
async def get_dmesg(self) -> bytes:
|
||||
"""Return host dmesg output."""
|
||||
proc = await asyncio.create_subprocess_shell(
|
||||
"dmesg", stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT
|
||||
)
|
||||
|
||||
# Get kernel log
|
||||
try:
|
||||
stdout, _ = await proc.communicate()
|
||||
except OSError as err:
|
||||
_LOGGER.error("Can't read kernel log: %s", err)
|
||||
raise HostError()
|
||||
|
||||
return stdout
|
||||
|
||||
async def update(self):
|
||||
"""Update properties over dbus."""
|
||||
_LOGGER.info("Update local host information")
|
||||
|
@@ -28,7 +28,7 @@ class HwMonitor(CoreSysAttributes):
|
||||
self.monitor = pyudev.Monitor.from_netlink(self.context)
|
||||
self.observer = pyudev.MonitorObserver(self.monitor, self._udev_events)
|
||||
except OSError:
|
||||
_LOGGER.fatal("Not privileged to run udev. Update your installation!")
|
||||
_LOGGER.fatal("Not privileged to run udev monitor!")
|
||||
else:
|
||||
self.observer.start()
|
||||
_LOGGER.info("Started Supervisor hardware monitor")
|
||||
|
@@ -24,7 +24,7 @@ from ..const import (
|
||||
ATTR_FOLDERS,
|
||||
ATTR_HOMEASSISTANT,
|
||||
ATTR_IMAGE,
|
||||
ATTR_LAST_VERSION,
|
||||
ATTR_VERSION_LATEST,
|
||||
ATTR_NAME,
|
||||
ATTR_PORT,
|
||||
ATTR_PROTECTED,
|
||||
@@ -435,7 +435,7 @@ class Snapshot(CoreSysAttributes):
|
||||
if self.sys_homeassistant.is_custom_image:
|
||||
self.homeassistant[ATTR_IMAGE] = self.sys_homeassistant.image
|
||||
self.homeassistant[
|
||||
ATTR_LAST_VERSION
|
||||
ATTR_VERSION_LATEST
|
||||
] = self.sys_homeassistant.latest_version
|
||||
|
||||
# API/Proxy
|
||||
@@ -459,7 +459,7 @@ class Snapshot(CoreSysAttributes):
|
||||
if self.homeassistant.get(ATTR_IMAGE):
|
||||
self.sys_homeassistant.image = self.homeassistant[ATTR_IMAGE]
|
||||
self.sys_homeassistant.latest_version = self.homeassistant[
|
||||
ATTR_LAST_VERSION
|
||||
ATTR_VERSION_LATEST
|
||||
]
|
||||
|
||||
# API/Proxy
|
||||
|
@@ -11,7 +11,7 @@ from ..const import (
|
||||
ATTR_FOLDERS,
|
||||
ATTR_HOMEASSISTANT,
|
||||
ATTR_IMAGE,
|
||||
ATTR_LAST_VERSION,
|
||||
ATTR_VERSION_LATEST,
|
||||
ATTR_NAME,
|
||||
ATTR_PORT,
|
||||
ATTR_PROTECTED,
|
||||
@@ -61,7 +61,7 @@ SCHEMA_SNAPSHOT = vol.Schema(
|
||||
{
|
||||
vol.Optional(ATTR_VERSION): vol.Coerce(str),
|
||||
vol.Inclusive(ATTR_IMAGE, "custom_hass"): docker_image,
|
||||
vol.Inclusive(ATTR_LAST_VERSION, "custom_hass"): vol.Coerce(str),
|
||||
vol.Inclusive(ATTR_VERSION_LATEST, "custom_hass"): vol.Coerce(str),
|
||||
vol.Optional(ATTR_BOOT, default=True): vol.Boolean(),
|
||||
vol.Optional(ATTR_SSL, default=False): vol.Boolean(),
|
||||
vol.Optional(ATTR_PORT, default=8123): network_port,
|
||||
|
@@ -25,7 +25,8 @@ RUN_WATCHDOG_HOMEASSISTANT_DOCKER = 15
|
||||
RUN_WATCHDOG_HOMEASSISTANT_API = 300
|
||||
|
||||
RUN_WATCHDOG_DNS_DOCKER = 20
|
||||
RUN_WATCHDOG_AUDIO_DOCKER = 20
|
||||
RUN_WATCHDOG_AUDIO_DOCKER = 30
|
||||
RUN_WATCHDOG_CLI_DOCKER = 40
|
||||
|
||||
|
||||
class Tasks(CoreSysAttributes):
|
||||
@@ -102,6 +103,11 @@ class Tasks(CoreSysAttributes):
|
||||
self._watchdog_audio_docker, RUN_WATCHDOG_AUDIO_DOCKER
|
||||
)
|
||||
)
|
||||
self.jobs.add(
|
||||
self.sys_scheduler.register_task(
|
||||
self._watchdog_cli_docker, RUN_WATCHDOG_CLI_DOCKER
|
||||
)
|
||||
)
|
||||
|
||||
_LOGGER.info("All core tasks are scheduled")
|
||||
|
||||
@@ -202,12 +208,12 @@ class Tasks(CoreSysAttributes):
|
||||
self._cache[HASS_WATCHDOG_API] = 0
|
||||
|
||||
async def _update_cli(self):
|
||||
"""Check and run update of CLI."""
|
||||
if not self.sys_hassos.need_cli_update:
|
||||
"""Check and run update of cli."""
|
||||
if not self.sys_cli.need_update:
|
||||
return
|
||||
|
||||
_LOGGER.info("Found new CLI version")
|
||||
await self.sys_hassos.update_cli()
|
||||
_LOGGER.info("Found new cli version")
|
||||
await self.sys_cli.update()
|
||||
|
||||
async def _update_dns(self):
|
||||
"""Check and run update of CoreDNS plugin."""
|
||||
@@ -232,9 +238,11 @@ class Tasks(CoreSysAttributes):
|
||||
return
|
||||
_LOGGER.warning("Watchdog found a problem with CoreDNS plugin!")
|
||||
|
||||
# Reset of fails
|
||||
if await self.sys_dns.is_fails():
|
||||
_LOGGER.warning("CoreDNS plugin is in fails state / Reset config")
|
||||
_LOGGER.error("CoreDNS plugin is in fails state / Reset config")
|
||||
await self.sys_dns.reset()
|
||||
await self.sys_dns.loop_detection()
|
||||
|
||||
try:
|
||||
await self.sys_dns.start()
|
||||
@@ -252,3 +260,15 @@ class Tasks(CoreSysAttributes):
|
||||
await self.sys_audio.start()
|
||||
except CoreDNSError:
|
||||
_LOGGER.error("Watchdog PulseAudio reanimation fails!")
|
||||
|
||||
async def _watchdog_cli_docker(self):
|
||||
"""Check running state of Docker and start if they is close."""
|
||||
# if cli plugin is active
|
||||
if await self.sys_cli.is_running() or self.sys_cli.in_progress:
|
||||
return
|
||||
_LOGGER.warning("Watchdog found a problem with cli plugin!")
|
||||
|
||||
try:
|
||||
await self.sys_cli.start()
|
||||
except CoreDNSError:
|
||||
_LOGGER.error("Watchdog cli reanimation fails!")
|
||||
|
@@ -208,7 +208,7 @@ class DBus:
|
||||
raise exception()
|
||||
|
||||
# General
|
||||
_LOGGER.error("DBus return error: %s", error)
|
||||
_LOGGER.error("DBus return error: %s", error.strip())
|
||||
raise DBusFatalError()
|
||||
|
||||
def attach_signals(self, filters=None):
|
||||
|
@@ -22,7 +22,7 @@ from .const import (
|
||||
ATTR_HOMEASSISTANT,
|
||||
ATTR_IMAGE,
|
||||
ATTR_LAST_BOOT,
|
||||
ATTR_LAST_VERSION,
|
||||
ATTR_VERSION_LATEST,
|
||||
ATTR_LOGGING,
|
||||
ATTR_PORT,
|
||||
ATTR_PORTS,
|
||||
@@ -105,7 +105,7 @@ SCHEMA_HASS_CONFIG = vol.Schema(
|
||||
vol.Optional(ATTR_ACCESS_TOKEN): token,
|
||||
vol.Optional(ATTR_BOOT, default=True): vol.Boolean(),
|
||||
vol.Inclusive(ATTR_IMAGE, "custom_hass"): docker_image,
|
||||
vol.Inclusive(ATTR_LAST_VERSION, "custom_hass"): vol.Coerce(str),
|
||||
vol.Inclusive(ATTR_VERSION_LATEST, "custom_hass"): vol.Coerce(str),
|
||||
vol.Optional(ATTR_PORT, default=8123): network_port,
|
||||
vol.Optional(ATTR_REFRESH_TOKEN): vol.Maybe(vol.Coerce(str)),
|
||||
vol.Optional(ATTR_SSL, default=False): vol.Boolean(),
|
||||
@@ -182,3 +182,12 @@ SCHEMA_DNS_CONFIG = vol.Schema(
|
||||
SCHEMA_AUDIO_CONFIG = vol.Schema(
|
||||
{vol.Optional(ATTR_VERSION): vol.Maybe(vol.Coerce(str))}, extra=vol.REMOVE_EXTRA,
|
||||
)
|
||||
|
||||
|
||||
SCHEMA_CLI_CONFIG = vol.Schema(
|
||||
{
|
||||
vol.Optional(ATTR_VERSION): vol.Maybe(vol.Coerce(str)),
|
||||
vol.Optional(ATTR_ACCESS_TOKEN): token,
|
||||
},
|
||||
extra=vol.REMOVE_EXTRA,
|
||||
)
|
||||
|
@@ -1,19 +0,0 @@
|
||||
"""Test adguard discovery."""
|
||||
|
||||
import voluptuous as vol
|
||||
import pytest
|
||||
|
||||
from supervisor.discovery.validate import valid_discovery_config
|
||||
|
||||
|
||||
def test_good_config():
|
||||
"""Test good deconz config."""
|
||||
|
||||
valid_discovery_config("home_panel", {"host": "test", "port": 3812})
|
||||
|
||||
|
||||
def test_bad_config():
|
||||
"""Test good adguard config."""
|
||||
|
||||
with pytest.raises(vol.Invalid):
|
||||
valid_discovery_config("home_panel", {"host": "test"})
|
Reference in New Issue
Block a user