Cli rebrand (#1601)
* Rebrand CLI * forward * Fix startup command * add cli api * Add token handling * Fix security check * fix repair * fix lint * Update for new cli * Add watchdog * rename * use s6
This commit is contained in:
parent
24f7801ddc
commit
96c0fbaf10
|
@ -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": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
182
API.md
182
API.md
|
@ -291,9 +291,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 +305,6 @@ return:
|
|||
}
|
||||
```
|
||||
|
||||
- POST `/os/update/cli`
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "optional"
|
||||
}
|
||||
```
|
||||
|
||||
- POST `/os/config/sync`
|
||||
|
||||
Load host configs from a USB stick.
|
||||
|
@ -857,90 +847,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": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
"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 +877,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
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
@ -84,22 +86,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),
|
||||
]
|
||||
)
|
||||
|
||||
|
|
|
@ -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))
|
|
@ -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())
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
"""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 Audio 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)
|
||||
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
|
||||
else:
|
||||
# Cleanup
|
||||
with suppress(DockerAPIError):
|
||||
await self.instance.cleanup()
|
||||
|
||||
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")
|
|
@ -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")
|
||||
|
||||
|
@ -189,9 +190,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."""
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -221,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)
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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."""
|
||||
|
@ -254,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):
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue