mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-28 03:26:32 +00:00
commit
79cc23e273
12
API.md
12
API.md
@ -37,8 +37,9 @@ The addons from `addons` are only installed one.
|
|||||||
{
|
{
|
||||||
"name": "xy bla",
|
"name": "xy bla",
|
||||||
"slug": "xy",
|
"slug": "xy",
|
||||||
"version": "INSTALL_VERSION",
|
"repository": "12345678|null",
|
||||||
"last_version": "VERSION_FOR_UPDATE",
|
"version": "LAST_VERSION",
|
||||||
|
"installed": "INSTALL_VERSION",
|
||||||
"detached": "bool",
|
"detached": "bool",
|
||||||
"description": "description"
|
"description": "description"
|
||||||
}
|
}
|
||||||
@ -59,7 +60,7 @@ Get all available addons
|
|||||||
{
|
{
|
||||||
"name": "xy bla",
|
"name": "xy bla",
|
||||||
"slug": "xy",
|
"slug": "xy",
|
||||||
"repository": "12345678|null",
|
"repository": "core|local|REP_ID",
|
||||||
"version": "LAST_VERSION",
|
"version": "LAST_VERSION",
|
||||||
"installed": "none|INSTALL_VERSION",
|
"installed": "none|INSTALL_VERSION",
|
||||||
"detached": "bool",
|
"detached": "bool",
|
||||||
@ -70,8 +71,9 @@ Get all available addons
|
|||||||
{
|
{
|
||||||
"slug": "12345678",
|
"slug": "12345678",
|
||||||
"name": "Repitory Name",
|
"name": "Repitory Name",
|
||||||
"url": "WEBSITE",
|
"source": "URL_OF_REPOSITORY",
|
||||||
"maintainer": "BLA BLU <fla@dld.ch>"
|
"url": "null|WEBSITE",
|
||||||
|
"maintainer": "null|BLA BLU <fla@dld.ch>"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,8 @@ from .validate import (
|
|||||||
from ..const import (
|
from ..const import (
|
||||||
FILE_HASSIO_ADDONS, ATTR_NAME, ATTR_VERSION, ATTR_SLUG, ATTR_DESCRIPTON,
|
FILE_HASSIO_ADDONS, ATTR_NAME, ATTR_VERSION, ATTR_SLUG, ATTR_DESCRIPTON,
|
||||||
ATTR_STARTUP, ATTR_BOOT, ATTR_MAP, ATTR_OPTIONS, ATTR_PORTS, BOOT_AUTO,
|
ATTR_STARTUP, ATTR_BOOT, ATTR_MAP, ATTR_OPTIONS, ATTR_PORTS, BOOT_AUTO,
|
||||||
DOCKER_REPO, ATTR_INSTALLED, ATTR_SCHEMA, ATTR_IMAGE, ATTR_DETACHED,
|
DOCKER_REPO, ATTR_SCHEMA, ATTR_IMAGE, MAP_CONFIG, MAP_SSL, MAP_ADDONS,
|
||||||
MAP_CONFIG, MAP_SSL, MAP_ADDONS, MAP_BACKUP, ATTR_REPOSITORY, ATTR_URL,
|
MAP_BACKUP, ATTR_REPOSITORY)
|
||||||
ATTR_MAINTAINER, ATTR_LAST_VERSION)
|
|
||||||
from ..config import Config
|
from ..config import Config
|
||||||
from ..tools import read_json_file, write_json_file
|
from ..tools import read_json_file, write_json_file
|
||||||
|
|
||||||
@ -142,48 +141,12 @@ class AddonsData(Config):
|
|||||||
return set(self._system_data.keys())
|
return set(self._system_data.keys())
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def list_all_api(self):
|
def list_all(self):
|
||||||
"""Return a list of available addons for api."""
|
"""Return a list of all addons."""
|
||||||
data = []
|
return {
|
||||||
all_addons = {**self._system_data, **self._addons_cache}
|
**self._system_data,
|
||||||
detached = self.list_detached
|
**self._addons_cache
|
||||||
|
}
|
||||||
for addon, values in all_addons.items():
|
|
||||||
i_version = self._user_data.get(addon, {}).get(ATTR_VERSION)
|
|
||||||
|
|
||||||
data.append({
|
|
||||||
ATTR_NAME: values[ATTR_NAME],
|
|
||||||
ATTR_SLUG: addon,
|
|
||||||
ATTR_DESCRIPTON: values[ATTR_DESCRIPTON],
|
|
||||||
ATTR_VERSION: values[ATTR_VERSION],
|
|
||||||
ATTR_INSTALLED: i_version,
|
|
||||||
ATTR_DETACHED: addon in detached,
|
|
||||||
ATTR_REPOSITORY: values[ATTR_REPOSITORY],
|
|
||||||
})
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
@property
|
|
||||||
def list_installed_api(self):
|
|
||||||
"""Return a list of available addons for api."""
|
|
||||||
data = []
|
|
||||||
all_addons = {**self._system_data, **self._addons_cache}
|
|
||||||
detached = self.list_detached
|
|
||||||
|
|
||||||
for addon, values in all_addons.items():
|
|
||||||
i_version = self._user_data.get(addon, {}).get(ATTR_VERSION)
|
|
||||||
|
|
||||||
data.append({
|
|
||||||
ATTR_NAME: values[ATTR_NAME],
|
|
||||||
ATTR_SLUG: addon,
|
|
||||||
ATTR_DESCRIPTON: values[ATTR_DESCRIPTON],
|
|
||||||
ATTR_VERSION: values[ATTR_VERSION],
|
|
||||||
ATTR_LAST_VERSION: values[ATTR_VERSION],
|
|
||||||
ATTR_INSTALLED: i_version,
|
|
||||||
ATTR_DETACHED: addon in detached
|
|
||||||
})
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
def list_startup(self, start_type):
|
def list_startup(self, start_type):
|
||||||
"""Get list of installed addon with need start by type."""
|
"""Get list of installed addon with need start by type."""
|
||||||
@ -212,19 +175,9 @@ class AddonsData(Config):
|
|||||||
return addon_list
|
return addon_list
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def list_repositories_api(self):
|
def list_repositories(self):
|
||||||
"""Return list of addon repositories."""
|
"""Return list of addon repositories."""
|
||||||
repositories = []
|
return list(self._repositories_data.values())
|
||||||
|
|
||||||
for slug, data in self._repositories_data.items():
|
|
||||||
repositories.append({
|
|
||||||
ATTR_SLUG: slug,
|
|
||||||
ATTR_NAME: data[ATTR_NAME],
|
|
||||||
ATTR_URL: data.get(ATTR_URL),
|
|
||||||
ATTR_MAINTAINER: data.get(ATTR_MAINTAINER),
|
|
||||||
})
|
|
||||||
|
|
||||||
return repositories
|
|
||||||
|
|
||||||
def exists_addon(self, addon):
|
def exists_addon(self, addon):
|
||||||
"""Return True if a addon exists."""
|
"""Return True if a addon exists."""
|
||||||
@ -236,7 +189,7 @@ class AddonsData(Config):
|
|||||||
|
|
||||||
def version_installed(self, addon):
|
def version_installed(self, addon):
|
||||||
"""Return installed version."""
|
"""Return installed version."""
|
||||||
return self._user_data[addon][ATTR_VERSION]
|
return self._user_data.get(addon, {}).get(ATTR_VERSION)
|
||||||
|
|
||||||
def set_addon_install(self, addon, version):
|
def set_addon_install(self, addon, version):
|
||||||
"""Set addon as installed."""
|
"""Set addon as installed."""
|
||||||
|
@ -19,3 +19,8 @@ def extract_hash_from_path(path):
|
|||||||
if not RE_SHA1.match(repo_dir):
|
if not RE_SHA1.match(repo_dir):
|
||||||
return get_hash_from_repository(repo_dir)
|
return get_hash_from_repository(repo_dir)
|
||||||
return repo_dir
|
return repo_dir
|
||||||
|
|
||||||
|
|
||||||
|
def create_hash_index_list(name_list):
|
||||||
|
"""Create a dict with hash from repositories list."""
|
||||||
|
return {get_hash_from_repository(repo): repo for repo in name_list}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
"""Init file for HassIO host rest api."""
|
"""Init file for HassIO host rest api."""
|
||||||
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
@ -30,7 +31,7 @@ class APIHost(object):
|
|||||||
return {
|
return {
|
||||||
ATTR_TYPE: self.host_control.type,
|
ATTR_TYPE: self.host_control.type,
|
||||||
ATTR_VERSION: self.host_control.version,
|
ATTR_VERSION: self.host_control.version,
|
||||||
ATTR_LAST_VERSION: self.host_control.last,
|
ATTR_LAST_VERSION: self.host_control.last_version,
|
||||||
ATTR_FEATURES: self.host_control.features,
|
ATTR_FEATURES: self.host_control.features,
|
||||||
ATTR_HOSTNAME: self.host_control.hostname,
|
ATTR_HOSTNAME: self.host_control.hostname,
|
||||||
ATTR_OS: self.host_control.os_info,
|
ATTR_OS: self.host_control.os_info,
|
||||||
@ -50,9 +51,10 @@ class APIHost(object):
|
|||||||
async def update(self, request):
|
async def update(self, request):
|
||||||
"""Update host OS."""
|
"""Update host OS."""
|
||||||
body = await api_validate(SCHEMA_VERSION, request)
|
body = await api_validate(SCHEMA_VERSION, request)
|
||||||
version = body.get(ATTR_VERSION)
|
version = body.get(ATTR_VERSION, self.host_control.last_version)
|
||||||
|
|
||||||
if version == self.host_control.version:
|
if version == self.host_control.version:
|
||||||
raise RuntimeError("Version is already in use")
|
raise RuntimeError("Version is already in use")
|
||||||
|
|
||||||
return await self.host_control.update(version=version)
|
return await asyncio.shield(
|
||||||
|
self.host_control.update(version=version), loop=self.loop)
|
||||||
|
@ -5,9 +5,12 @@ import logging
|
|||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from .util import api_process, api_process_raw, api_validate
|
from .util import api_process, api_process_raw, api_validate
|
||||||
|
from ..addons.util import create_hash_index_list
|
||||||
from ..const import (
|
from ..const import (
|
||||||
ATTR_ADDONS, ATTR_VERSION, ATTR_LAST_VERSION, ATTR_BETA_CHANNEL,
|
ATTR_ADDONS, ATTR_VERSION, ATTR_LAST_VERSION, ATTR_BETA_CHANNEL,
|
||||||
HASSIO_VERSION, ATTR_ADDONS_REPOSITORIES, ATTR_REPOSITORIES)
|
HASSIO_VERSION, ATTR_ADDONS_REPOSITORIES, ATTR_REPOSITORIES,
|
||||||
|
ATTR_REPOSITORY, ATTR_DESCRIPTON, ATTR_NAME, ATTR_SLUG, ATTR_INSTALLED,
|
||||||
|
ATTR_DETACHED, ATTR_SOURCE, ATTR_MAINTAINER, ATTR_URL)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -33,6 +36,42 @@ class APISupervisor(object):
|
|||||||
self.addons = addons
|
self.addons = addons
|
||||||
self.host_control = host_control
|
self.host_control = host_control
|
||||||
|
|
||||||
|
def _addons_list(self, only_installed):
|
||||||
|
"""Return a list of addons."""
|
||||||
|
data = []
|
||||||
|
detached = self.addons.list_detached
|
||||||
|
|
||||||
|
for addon, values in self.addons.list_all.items():
|
||||||
|
i_version = self.addons.version_installed(addon)
|
||||||
|
|
||||||
|
data.append({
|
||||||
|
ATTR_NAME: values[ATTR_NAME],
|
||||||
|
ATTR_SLUG: addon,
|
||||||
|
ATTR_DESCRIPTON: values[ATTR_DESCRIPTON],
|
||||||
|
ATTR_VERSION: values[ATTR_VERSION],
|
||||||
|
ATTR_INSTALLED: i_version,
|
||||||
|
ATTR_DETACHED: addon in detached,
|
||||||
|
ATTR_REPOSITORY: values[ATTR_REPOSITORY],
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def _repositories_list(self):
|
||||||
|
"""Return a list of addons repositories."""
|
||||||
|
data = []
|
||||||
|
list_id = create_hash_index_list(self.config.addons_repositories)
|
||||||
|
|
||||||
|
for repository in self.addons.list_repositories:
|
||||||
|
data.append({
|
||||||
|
ATTR_SLUG: repository[ATTR_SLUG],
|
||||||
|
ATTR_NAME: repository[ATTR_NAME],
|
||||||
|
ATTR_SOURCE: list_id.get(repository[ATTR_SLUG]),
|
||||||
|
ATTR_URL: repository.get(ATTR_URL),
|
||||||
|
ATTR_MAINTAINER: repository.get(ATTR_MAINTAINER),
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
@api_process
|
@api_process
|
||||||
async def ping(self, request):
|
async def ping(self, request):
|
||||||
"""Return ok for signal that the api is ready."""
|
"""Return ok for signal that the api is ready."""
|
||||||
@ -45,7 +84,7 @@ class APISupervisor(object):
|
|||||||
ATTR_VERSION: HASSIO_VERSION,
|
ATTR_VERSION: HASSIO_VERSION,
|
||||||
ATTR_LAST_VERSION: self.config.last_hassio,
|
ATTR_LAST_VERSION: self.config.last_hassio,
|
||||||
ATTR_BETA_CHANNEL: self.config.upstream_beta,
|
ATTR_BETA_CHANNEL: self.config.upstream_beta,
|
||||||
ATTR_ADDONS: self.addons.list_installed_api,
|
ATTR_ADDONS: self._addons_list(only_installed=True),
|
||||||
ATTR_ADDONS_REPOSITORIES: self.config.addons_repositories,
|
ATTR_ADDONS_REPOSITORIES: self.config.addons_repositories,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,8 +92,8 @@ class APISupervisor(object):
|
|||||||
async def available_addons(self, request):
|
async def available_addons(self, request):
|
||||||
"""Return information for all available addons."""
|
"""Return information for all available addons."""
|
||||||
return {
|
return {
|
||||||
ATTR_ADDONS: self.addons.list_all_api,
|
ATTR_ADDONS: self._addons_list(only_installed=False),
|
||||||
ATTR_REPOSITORIES: self.addons.list_repositories_api,
|
ATTR_REPOSITORIES: self._repositories_list(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@api_process
|
@api_process
|
||||||
@ -80,6 +119,9 @@ class APISupervisor(object):
|
|||||||
for url in set(old - new):
|
for url in set(old - new):
|
||||||
self.addons.drop_git_repository(url)
|
self.addons.drop_git_repository(url)
|
||||||
|
|
||||||
|
# read repository
|
||||||
|
self.addons.read_data_from_repositories()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@api_process
|
@api_process
|
||||||
|
@ -9,7 +9,7 @@ from voluptuous.humanize import humanize_error
|
|||||||
|
|
||||||
from .const import FILE_HASSIO_CONFIG, HASSIO_SHARE
|
from .const import FILE_HASSIO_CONFIG, HASSIO_SHARE
|
||||||
from .tools import (
|
from .tools import (
|
||||||
fetch_current_versions, write_json_file, read_json_file)
|
fetch_last_versions, write_json_file, read_json_file)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ class CoreConfig(Config):
|
|||||||
|
|
||||||
async def fetch_update_infos(self):
|
async def fetch_update_infos(self):
|
||||||
"""Read current versions from web."""
|
"""Read current versions from web."""
|
||||||
last = await fetch_current_versions(
|
last = await fetch_last_versions(
|
||||||
self.websession, beta=self.upstream_beta)
|
self.websession, beta=self.upstream_beta)
|
||||||
|
|
||||||
if last:
|
if last:
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""Const file for HassIO."""
|
"""Const file for HassIO."""
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
HASSIO_VERSION = '0.19'
|
HASSIO_VERSION = '0.20'
|
||||||
|
|
||||||
URL_HASSIO_VERSION = ('https://raw.githubusercontent.com/home-assistant/'
|
URL_HASSIO_VERSION = ('https://raw.githubusercontent.com/home-assistant/'
|
||||||
'hassio/master/version.json')
|
'hassio/master/version.json')
|
||||||
@ -36,6 +36,7 @@ RESULT_OK = 'ok'
|
|||||||
ATTR_HOSTNAME = 'hostname'
|
ATTR_HOSTNAME = 'hostname'
|
||||||
ATTR_OS = 'os'
|
ATTR_OS = 'os'
|
||||||
ATTR_TYPE = 'type'
|
ATTR_TYPE = 'type'
|
||||||
|
ATTR_SOURCE = 'source'
|
||||||
ATTR_FEATURES = 'features'
|
ATTR_FEATURES = 'features'
|
||||||
ATTR_ADDONS = 'addons'
|
ATTR_ADDONS = 'addons'
|
||||||
ATTR_VERSION = 'version'
|
ATTR_VERSION = 'version'
|
||||||
|
@ -57,11 +57,10 @@ class HassIO(object):
|
|||||||
|
|
||||||
# hostcontrol
|
# hostcontrol
|
||||||
await self.host_control.load()
|
await self.host_control.load()
|
||||||
_LOGGER.info(
|
|
||||||
"Connected to HostControl. Type: %s Version: %s Hostname: %s "
|
# schedule update info tasks
|
||||||
"Features: %s", self.host_control.type,
|
self.scheduler.register_task(
|
||||||
self.host_control.version, self.host_control.hostname,
|
self.host_control.load, RUN_UPDATE_INFO_TASKS)
|
||||||
self.host_control.features)
|
|
||||||
|
|
||||||
# rest api views
|
# rest api views
|
||||||
self.api.register_host(self.host_control)
|
self.api.register_host(self.host_control)
|
||||||
|
@ -29,7 +29,7 @@ class HostControl(object):
|
|||||||
self.loop = loop
|
self.loop = loop
|
||||||
self.active = False
|
self.active = False
|
||||||
self.version = UNKNOWN
|
self.version = UNKNOWN
|
||||||
self.last = UNKNOWN
|
self.last_version = UNKNOWN
|
||||||
self.type = UNKNOWN
|
self.type = UNKNOWN
|
||||||
self.features = []
|
self.features = []
|
||||||
self.hostname = UNKNOWN
|
self.hostname = UNKNOWN
|
||||||
@ -57,8 +57,8 @@ class HostControl(object):
|
|||||||
writer.write("{}\n".format(command).encode())
|
writer.write("{}\n".format(command).encode())
|
||||||
data = await reader.readline()
|
data = await reader.readline()
|
||||||
|
|
||||||
response = data.decode()
|
response = data.decode().rstrip()
|
||||||
_LOGGER.debug("Receive from HostControl: %s.", response)
|
_LOGGER.info("Receive from HostControl: %s.", response)
|
||||||
|
|
||||||
if response == "OK":
|
if response == "OK":
|
||||||
return True
|
return True
|
||||||
@ -70,7 +70,8 @@ class HostControl(object):
|
|||||||
try:
|
try:
|
||||||
return json.loads(response)
|
return json.loads(response)
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
_LOGGER.warning("Json parse error from HostControl.")
|
_LOGGER.warning("Json parse error from HostControl '%s'.",
|
||||||
|
response)
|
||||||
|
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
_LOGGER.error("Timeout from HostControl!")
|
_LOGGER.error("Timeout from HostControl!")
|
||||||
@ -88,7 +89,7 @@ class HostControl(object):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self.version = info.get(ATTR_VERSION, UNKNOWN)
|
self.version = info.get(ATTR_VERSION, UNKNOWN)
|
||||||
self.last = info.get(ATTR_LAST_VERSION, UNKNOWN)
|
self.last_version = info.get(ATTR_LAST_VERSION, UNKNOWN)
|
||||||
self.type = info.get(ATTR_TYPE, UNKNOWN)
|
self.type = info.get(ATTR_TYPE, UNKNOWN)
|
||||||
self.features = info.get(ATTR_FEATURES, [])
|
self.features = info.get(ATTR_FEATURES, [])
|
||||||
self.hostname = info.get(ATTR_HOSTNAME, UNKNOWN)
|
self.hostname = info.get(ATTR_HOSTNAME, UNKNOWN)
|
||||||
|
@ -16,7 +16,7 @@ _RE_VERSION = re.compile(r"VERSION=(.*)")
|
|||||||
_IMAGE_ARCH = re.compile(r".*/([a-z0-9]*)-hassio-supervisor")
|
_IMAGE_ARCH = re.compile(r".*/([a-z0-9]*)-hassio-supervisor")
|
||||||
|
|
||||||
|
|
||||||
async def fetch_current_versions(websession, beta=False):
|
async def fetch_last_versions(websession, beta=False):
|
||||||
"""Fetch current versions from github.
|
"""Fetch current versions from github.
|
||||||
|
|
||||||
Is a coroutine.
|
Is a coroutine.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"hassio": "0.19",
|
"hassio": "0.20",
|
||||||
"homeassistant": "0.43.2",
|
"homeassistant": "0.43.2",
|
||||||
"resinos": "0.6",
|
"resinos": "0.6",
|
||||||
"resinhup": "0.1",
|
"resinhup": "0.1",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user