From dbe6b860c71fbde796ec5b886a91cc2d2e45995e Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 2 May 2017 02:28:40 +0200 Subject: [PATCH 1/7] Pump version --- hassio/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hassio/const.py b/hassio/const.py index f48178328d8..74597396ba8 100644 --- a/hassio/const.py +++ b/hassio/const.py @@ -1,7 +1,7 @@ """Const file for HassIO.""" from pathlib import Path -HASSIO_VERSION = '0.19' +HASSIO_VERSION = '0.20' URL_HASSIO_VERSION = ('https://raw.githubusercontent.com/home-assistant/' 'hassio/master/version.json') From f6048467adf1ef2550b1ae83e244473b29fb648c Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 2 May 2017 18:50:35 +0200 Subject: [PATCH 2/7] Update api infos (#28) * New cleanup * Cleanup addons data object from api stuff. * Fix lint * Fix repo export * Fix part 2 * Update API.md --- API.md | 12 ++++--- hassio/addons/data.py | 69 +++++++--------------------------------- hassio/addons/util.py | 5 +++ hassio/api/supervisor.py | 50 ++++++++++++++++++++++++++--- hassio/const.py | 1 + 5 files changed, 70 insertions(+), 67 deletions(-) diff --git a/API.md b/API.md index 90b3244c915..84f93fbb463 100644 --- a/API.md +++ b/API.md @@ -37,8 +37,9 @@ The addons from `addons` are only installed one. { "name": "xy bla", "slug": "xy", - "version": "INSTALL_VERSION", - "last_version": "VERSION_FOR_UPDATE", + "repository": "12345678|null", + "version": "LAST_VERSION", + "installed": "INSTALL_VERSION", "detached": "bool", "description": "description" } @@ -59,7 +60,7 @@ Get all available addons { "name": "xy bla", "slug": "xy", - "repository": "12345678|null", + "repository": "core|local|REP_ID", "version": "LAST_VERSION", "installed": "none|INSTALL_VERSION", "detached": "bool", @@ -70,8 +71,9 @@ Get all available addons { "slug": "12345678", "name": "Repitory Name", - "url": "WEBSITE", - "maintainer": "BLA BLU " + "source": "URL_OF_REPOSITORY", + "url": "null|WEBSITE", + "maintainer": "null|BLA BLU " } ] } diff --git a/hassio/addons/data.py b/hassio/addons/data.py index 5248778a6d3..28a7dc08f1c 100644 --- a/hassio/addons/data.py +++ b/hassio/addons/data.py @@ -12,9 +12,8 @@ from ..const import ( FILE_HASSIO_ADDONS, ATTR_NAME, ATTR_VERSION, ATTR_SLUG, ATTR_DESCRIPTON, ATTR_STARTUP, ATTR_BOOT, ATTR_MAP, ATTR_OPTIONS, ATTR_PORTS, BOOT_AUTO, - DOCKER_REPO, ATTR_INSTALLED, ATTR_SCHEMA, ATTR_IMAGE, ATTR_DETACHED, - MAP_CONFIG, MAP_SSL, MAP_ADDONS, MAP_BACKUP, ATTR_REPOSITORY, ATTR_URL, - ATTR_MAINTAINER, ATTR_LAST_VERSION) + DOCKER_REPO, ATTR_SCHEMA, ATTR_IMAGE, MAP_CONFIG, MAP_SSL, MAP_ADDONS, + MAP_BACKUP, ATTR_REPOSITORY) from ..config import Config from ..tools import read_json_file, write_json_file @@ -142,48 +141,12 @@ def list_installed(self): return set(self._system_data.keys()) @property - def list_all_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_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_all(self): + """Return a list of all addons.""" + return { + **self._system_data, + **self._addons_cache + } def list_startup(self, start_type): """Get list of installed addon with need start by type.""" @@ -212,19 +175,9 @@ def list_detached(self): return addon_list @property - def list_repositories_api(self): + def list_repositories(self): """Return list of addon repositories.""" - repositories = [] - - 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 + return list(self._repositories_data.values()) def exists_addon(self, addon): """Return True if a addon exists.""" @@ -236,7 +189,7 @@ def is_installed(self, addon): def version_installed(self, addon): """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): """Set addon as installed.""" diff --git a/hassio/addons/util.py b/hassio/addons/util.py index 152c2886684..c0d097734cc 100644 --- a/hassio/addons/util.py +++ b/hassio/addons/util.py @@ -19,3 +19,8 @@ def extract_hash_from_path(path): if not RE_SHA1.match(repo_dir): return get_hash_from_repository(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} diff --git a/hassio/api/supervisor.py b/hassio/api/supervisor.py index 8fc841af331..289c07c4307 100644 --- a/hassio/api/supervisor.py +++ b/hassio/api/supervisor.py @@ -5,9 +5,12 @@ import voluptuous as vol from .util import api_process, api_process_raw, api_validate +from ..addons.util import create_hash_index_list from ..const import ( 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__) @@ -33,6 +36,42 @@ def __init__(self, config, loop, supervisor, addons, host_control): self.addons = addons 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.""" + repositories = [] + list_id = create_hash_index_list(self.config.addons_repositories) + + for repository in self.addons.list_repositories: + repository.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 repositories + @api_process async def ping(self, request): """Return ok for signal that the api is ready.""" @@ -45,7 +84,7 @@ async def info(self, request): ATTR_VERSION: HASSIO_VERSION, ATTR_LAST_VERSION: self.config.last_hassio, 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, } @@ -53,8 +92,8 @@ async def info(self, request): async def available_addons(self, request): """Return information for all available addons.""" return { - ATTR_ADDONS: self.addons.list_all_api, - ATTR_REPOSITORIES: self.addons.list_repositories_api, + ATTR_ADDONS: self._addons_list(only_installed=False), + ATTR_REPOSITORIES: self._repositories_list(), } @api_process @@ -80,6 +119,9 @@ async def options(self, request): for url in set(old - new): self.addons.drop_git_repository(url) + # read repository + self.addons.read_data_from_repositories() + return True @api_process diff --git a/hassio/const.py b/hassio/const.py index 74597396ba8..8acc3e84cc8 100644 --- a/hassio/const.py +++ b/hassio/const.py @@ -36,6 +36,7 @@ ATTR_HOSTNAME = 'hostname' ATTR_OS = 'os' ATTR_TYPE = 'type' +ATTR_SOURCE = 'source' ATTR_FEATURES = 'features' ATTR_ADDONS = 'addons' ATTR_VERSION = 'version' From 19b72b1a795f5ceb3e559454e04f2185254d5f3b Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 2 May 2017 23:16:58 +0200 Subject: [PATCH 3/7] add logoutbut (#29) * add logoutbut * Protect host update * schedule a hostcontrol update task * change log level * cleanup names --- hassio/api/host.py | 8 +++++--- hassio/config.py | 4 ++-- hassio/core.py | 4 ++++ hassio/host_control.py | 9 +++++---- hassio/tools.py | 2 +- 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/hassio/api/host.py b/hassio/api/host.py index 822985cce8d..ab202119876 100644 --- a/hassio/api/host.py +++ b/hassio/api/host.py @@ -1,4 +1,5 @@ """Init file for HassIO host rest api.""" +import asyncio import logging import voluptuous as vol @@ -30,7 +31,7 @@ async def info(self, request): return { ATTR_TYPE: self.host_control.type, 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_HOSTNAME: self.host_control.hostname, ATTR_OS: self.host_control.os_info, @@ -50,9 +51,10 @@ def shutdown(self, request): async def update(self, request): """Update host OS.""" 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: 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) diff --git a/hassio/config.py b/hassio/config.py index 2e7b8f8a3d1..8965ced69f8 100644 --- a/hassio/config.py +++ b/hassio/config.py @@ -9,7 +9,7 @@ from .const import FILE_HASSIO_CONFIG, HASSIO_SHARE 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__) @@ -87,7 +87,7 @@ def __init__(self, websession): async def fetch_update_infos(self): """Read current versions from web.""" - last = await fetch_current_versions( + last = await fetch_last_versions( self.websession, beta=self.upstream_beta) if last: diff --git a/hassio/core.py b/hassio/core.py index b4fae16ae7f..aabe5c8c4db 100644 --- a/hassio/core.py +++ b/hassio/core.py @@ -63,6 +63,10 @@ async def setup(self): self.host_control.version, self.host_control.hostname, self.host_control.features) + # schedule update info tasks + self.scheduler.register_task( + self.host_control.load(), RUN_UPDATE_INFO_TASKS) + # rest api views self.api.register_host(self.host_control) self.api.register_network(self.host_control) diff --git a/hassio/host_control.py b/hassio/host_control.py index 11a3db13c9b..bceb7f74ae3 100644 --- a/hassio/host_control.py +++ b/hassio/host_control.py @@ -29,7 +29,7 @@ def __init__(self, loop): self.loop = loop self.active = False self.version = UNKNOWN - self.last = UNKNOWN + self.last_version = UNKNOWN self.type = UNKNOWN self.features = [] self.hostname = UNKNOWN @@ -58,7 +58,7 @@ async def _send_command(self, command): data = await reader.readline() response = data.decode() - _LOGGER.debug("Receive from HostControl: %s.", response) + _LOGGER.info("Receive from HostControl: %s.", response) if response == "OK": return True @@ -70,7 +70,8 @@ async def _send_command(self, command): try: return json.loads(response) except json.JSONDecodeError: - _LOGGER.warning("Json parse error from HostControl.") + _LOGGER.warning("Json parse error from HostControl '%s'.", + response) except asyncio.TimeoutError: _LOGGER.error("Timeout from HostControl!") @@ -88,7 +89,7 @@ async def load(self): return 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.features = info.get(ATTR_FEATURES, []) self.hostname = info.get(ATTR_HOSTNAME, UNKNOWN) diff --git a/hassio/tools.py b/hassio/tools.py index f44e2a9ad38..7fc3562bb10 100644 --- a/hassio/tools.py +++ b/hassio/tools.py @@ -16,7 +16,7 @@ _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. Is a coroutine. From 385af5bef589b99fb37c1be507517087a770966e Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 2 May 2017 23:17:23 +0200 Subject: [PATCH 4/7] Update version.json --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index 882bc7a7c52..b2068bad3dc 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { - "hassio": "0.19", + "hassio": "0.20", "homeassistant": "0.43.2", "resinos": "0.6", "resinhup": "0.1", From f76749a9339fab774373194b21c6bcd8f77c6850 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 2 May 2017 23:33:51 +0200 Subject: [PATCH 5/7] Cleanup output --- hassio/core.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/hassio/core.py b/hassio/core.py index aabe5c8c4db..a0e74fa0f60 100644 --- a/hassio/core.py +++ b/hassio/core.py @@ -57,15 +57,10 @@ async def setup(self): # hostcontrol await self.host_control.load() - _LOGGER.info( - "Connected to HostControl. Type: %s Version: %s Hostname: %s " - "Features: %s", self.host_control.type, - self.host_control.version, self.host_control.hostname, - self.host_control.features) # schedule update info tasks self.scheduler.register_task( - self.host_control.load(), RUN_UPDATE_INFO_TASKS) + self.host_control.load, RUN_UPDATE_INFO_TASKS) # rest api views self.api.register_host(self.host_control) From 33a66bee01e1cfc8b828349e310cd5ae60a3e48c Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 2 May 2017 23:35:31 +0200 Subject: [PATCH 6/7] Fix wrong parser --- hassio/host_control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hassio/host_control.py b/hassio/host_control.py index bceb7f74ae3..845742f27ac 100644 --- a/hassio/host_control.py +++ b/hassio/host_control.py @@ -57,7 +57,7 @@ async def _send_command(self, command): writer.write("{}\n".format(command).encode()) data = await reader.readline() - response = data.decode() + response = data.decode().rstrip() _LOGGER.info("Receive from HostControl: %s.", response) if response == "OK": From 046ce0230a3c8c8db7f4e6bb656550ab80ac4292 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 2 May 2017 23:50:39 +0200 Subject: [PATCH 7/7] Fix bug --- hassio/api/supervisor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hassio/api/supervisor.py b/hassio/api/supervisor.py index 289c07c4307..c3049c5df00 100644 --- a/hassio/api/supervisor.py +++ b/hassio/api/supervisor.py @@ -58,11 +58,11 @@ def _addons_list(self, only_installed): def _repositories_list(self): """Return a list of addons repositories.""" - repositories = [] + data = [] list_id = create_hash_index_list(self.config.addons_repositories) for repository in self.addons.list_repositories: - repository.append({ + data.append({ ATTR_SLUG: repository[ATTR_SLUG], ATTR_NAME: repository[ATTR_NAME], ATTR_SOURCE: list_id.get(repository[ATTR_SLUG]), @@ -70,7 +70,7 @@ def _repositories_list(self): ATTR_MAINTAINER: repository.get(ATTR_MAINTAINER), }) - return repositories + return data @api_process async def ping(self, request):