From 76cead72e8500d8f215ec69664f0b9c5ad05d0e0 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 26 Apr 2017 11:15:56 +0200 Subject: [PATCH] Update API for hass api v2 (#14) * Update API for hass api v2 * fix lint * Refactory the old version of host_control * cleanup * Cleanup name inside addons/data * Cleanup name inside addons/data p2 * Rename api list * Fix path bug * Fix wrong config set --- API.md | 38 +++++++------- README.md | 6 +-- hassio/addons/__init__.py | 4 +- hassio/addons/data.py | 51 +++++++++--------- hassio/api/__init__.py | 14 ++--- hassio/api/addons.py | 21 ++++---- hassio/api/homeassistant.py | 6 +-- hassio/api/host.py | 43 +++++++-------- hassio/api/network.py | 10 ++-- hassio/api/supervisor.py | 28 +++++----- hassio/api/util.py | 10 ++-- hassio/config.py | 20 +++---- hassio/const.py | 9 ++-- hassio/core.py | 39 +++++++------- hassio/{host_controll.py => host_control.py} | 55 +++++++++++++------- 15 files changed, 185 insertions(+), 169 deletions(-) rename hassio/{host_controll.py => host_control.py} (56%) diff --git a/API.md b/API.md index 870e26d50..6a8bf0045 100644 --- a/API.md +++ b/API.md @@ -2,7 +2,7 @@ ## HassIO REST API -Interface for HomeAssistant to controll things from supervisor. +Interface for HomeAssistant to control things from supervisor. On error: ```json @@ -29,13 +29,13 @@ On success ```json { "version": "INSTALL_VERSION", - "current": "CURRENT_VERSION", - "beta": "true|false", + "last_version": "CURRENT_VERSION", + "beta_channel": "true|false", "addons": [ { "name": "xy bla", "slug": "xy", - "version": "CURRENT_VERSION", + "version": "LAST_VERSION", "installed": "none|INSTALL_VERSION", "dedicated": "bool", "description": "description" @@ -55,7 +55,7 @@ Optional: - `/supervisor/option` ```json { - "beta": "true|false" + "beta_channel": "true|false" } ``` @@ -74,13 +74,13 @@ Output the raw docker log - `/host/reboot` - `/host/info` -See HostControll info command. +See HostControl info command. ```json { - "os": "", + "type": "", "version": "", - "current": "", - "level": "", + "last_version": "", + "features": ["shutdown", "reboot", "update", "network_info", "network_control"], "hostname": "", } ``` @@ -116,7 +116,7 @@ Optional: ```json { "version": "INSTALL_VERSION", - "current": "CURRENT_VERSION" + "last_version": "LAST_VERSION" } ``` @@ -138,7 +138,7 @@ Output the raw docker log ```json { "version": "VERSION", - "current": "CURRENT_VERSION", + "last_version": "LAST_VERSION", "state": "started|stopped", "boot": "auto|manual", "options": {}, @@ -149,7 +149,7 @@ Output the raw docker log ```json { "boot": "auto|manual", - "options": {}, + "options": {}, } ``` @@ -179,14 +179,14 @@ Optional: Output the raw docker log -## Host Controll +## Host Control Communicate over unix socket with a host daemon. - commands ``` # info --> {'os', 'version', 'current', 'level', 'hostname'} +-> {'type', 'version', 'last_version', 'features', 'hostname'} # reboot # shutdown # host-update [v] @@ -200,10 +200,12 @@ Communicate over unix socket with a host daemon. # network int route xy ``` -level: -- 1: power functions -- 2: host update -- 4: network functions +features: +- shutdown +- reboot +- update +- network_info +- network_control Answer: ``` diff --git a/README.md b/README.md index d89921436..5cb50329c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # HassIO First private cloud solution for home automation. -It is a docker image (supervisor) they manage HomeAssistant docker and give a interface to controll itself over UI. It have a own eco system with addons to extend the functionality in a easy way. +It is a docker image (supervisor) they manage HomeAssistant docker and give a interface to control itself over UI. It have a own eco system with addons to extend the functionality in a easy way. [HassIO-Addons](https://github.com/pvizeli/hassio-addons) | [HassIO-Build](https://github.com/pvizeli/hassio-build) @@ -33,8 +33,8 @@ After extracting the archive, flash it to a drive using [Etcher](https://etcher. ## History - **0.1**: First techpreview with dumy supervisor (ResinOS 2.0.0-RC5) - **0.2**: Fix some bugs and update it to HassIO 0.2 -- **0.3**: Update HostControll and feature for HassIO 0.3 (ResinOS 2.0.0 / need reflash) -- **0.4**: Update HostControll and bring resinos OTA (resinhub) back (ResinOS 2.0.0-rev3) +- **0.3**: Update HostControl and feature for HassIO 0.3 (ResinOS 2.0.0 / need reflash) +- **0.4**: Update HostControl and bring resinos OTA (resinhub) back (ResinOS 2.0.0-rev3) ## Configuring the image You can configure the WiFi network that the image should connect to after flashing using [`resin-device-toolbox`](https://resinos.io/docs/raspberrypi3/gettingstarted/#install-resin-device-toolbox). diff --git a/hassio/addons/__init__.py b/hassio/addons/__init__.py index fc2a6ca69..bf2431b03 100644 --- a/hassio/addons/__init__.py +++ b/hassio/addons/__init__.py @@ -78,7 +78,7 @@ class AddonManager(AddonsData): addon_docker = DockerAddon( self.config, self.loop, self.dock, self, addon) - version = version or self.get_version(addon) + version = version or self.get_last_version(addon) if not await addon_docker.install(version): return False @@ -144,7 +144,7 @@ class AddonManager(AddonsData): _LOGGER.error("No docker found for addon %s", addon) return False - version = version or self.get_version(addon) + version = version or self.get_last_version(addon) is_running = self.dockers[addon].is_running() # update diff --git a/hassio/addons/data.py b/hassio/addons/data.py index 24d24ad8f..93d3f303f 100644 --- a/hassio/addons/data.py +++ b/hassio/addons/data.py @@ -28,7 +28,7 @@ class AddonsData(Config): """Initialize data holder.""" super().__init__(FILE_HASSIO_ADDONS) self.config = config - self._addons_data = self._data.get(SYSTEM, {}) + self._system_data = self._data.get(SYSTEM, {}) self._user_data = self._data.get(USER, {}) self._current_data = {} self.arch = None @@ -37,7 +37,7 @@ class AddonsData(Config): """Store data to config file.""" self._data = { USER: self._user_data, - SYSTEM: self._addons_data, + SYSTEM: self._system_data, } super().save() @@ -69,13 +69,13 @@ class AddonsData(Config): @property def list_installed(self): """Return a list of installed addons.""" - return set(self._addons_data.keys()) + return set(self._system_data.keys()) @property - def list(self): - """Return a list of available addons.""" + def list_api(self): + """Return a list of available addons for api.""" data = [] - all_addons = {**self._addons_data, **self._current_data} + all_addons = {**self._system_data, **self._current_data} dedicated = self.list_removed for addon, values in all_addons.items(): @@ -95,12 +95,12 @@ class AddonsData(Config): def list_startup(self, start_type): """Get list of installed addon with need start by type.""" addon_list = set() - for addon in self._addons_data.keys(): + for addon in self._system_data.keys(): if self.get_boot(addon) != BOOT_AUTO: continue try: - if self._addons_data[addon][ATTR_STARTUP] == start_type: + if self._system_data[addon][ATTR_STARTUP] == start_type: addon_list.add(addon) except KeyError: _LOGGER.warning("Orphaned addon detect %s", addon) @@ -112,7 +112,7 @@ class AddonsData(Config): def list_removed(self): """Return local addons they not support from repo.""" addon_list = set() - for addon in self._addons_data.keys(): + for addon in self._system_data.keys(): if addon not in self._current_data: addon_list.add(addon) @@ -120,22 +120,19 @@ class AddonsData(Config): def exists_addon(self, addon): """Return True if a addon exists.""" - return addon in self._current_data or addon in self._addons_data + return addon in self._current_data or addon in self._system_data def is_installed(self, addon): """Return True if a addon is installed.""" - return addon in self._addons_data + return addon in self._system_data def version_installed(self, addon): """Return installed version.""" - if ATTR_VERSION not in self._user_data[addon]: - return self._addons_data[addon][ATTR_VERSION] - return self._user_data[addon][ATTR_VERSION] def set_addon_install(self, addon, version): """Set addon as installed.""" - self._addons_data[addon] = self._current_data[addon] + self._system_data[addon] = self._current_data[addon] self._user_data[addon] = { ATTR_OPTIONS: {}, ATTR_VERSION: version, @@ -144,13 +141,13 @@ class AddonsData(Config): def set_addon_uninstall(self, addon): """Set addon as uninstalled.""" - self._addons_data.pop(addon, None) + self._system_data.pop(addon, None) self._user_data.pop(addon, None) self.save() def set_addon_update(self, addon, version): """Update version of addon.""" - self._addons_data[addon] = self._current_data[addon] + self._system_data[addon] = self._current_data[addon] self._user_data[addon][ATTR_VERSION] = version self.save() @@ -167,7 +164,7 @@ class AddonsData(Config): def get_options(self, addon): """Return options with local changes.""" return { - **self._addons_data[addon][ATTR_OPTIONS], + **self._system_data[addon][ATTR_OPTIONS], **self._user_data[addon][ATTR_OPTIONS], } @@ -176,17 +173,17 @@ class AddonsData(Config): if ATTR_BOOT in self._user_data[addon]: return self._user_data[addon][ATTR_BOOT] - return self._addons_data[addon][ATTR_BOOT] + return self._system_data[addon][ATTR_BOOT] def get_name(self, addon): """Return name of addon.""" - return self._addons_data[addon][ATTR_NAME] + return self._system_data[addon][ATTR_NAME] def get_description(self, addon): """Return description of addon.""" - return self._addons_data[addon][ATTR_DESCRIPTON] + return self._system_data[addon][ATTR_DESCRIPTON] - def get_version(self, addon): + def get_last_version(self, addon): """Return version of addon.""" if addon not in self._current_data: return self.version_installed(addon) @@ -194,11 +191,11 @@ class AddonsData(Config): def get_ports(self, addon): """Return ports of addon.""" - return self._addons_data[addon].get(ATTR_PORTS) + return self._system_data[addon].get(ATTR_PORTS) def get_image(self, addon): """Return image name of addon.""" - addon_data = self._addons_data.get(addon, self._current_data[addon]) + addon_data = self._system_data.get(addon, self._current_data[addon]) if ATTR_IMAGE not in addon_data: return "{}/{}-addon-{}".format(DOCKER_REPO, self.arch, addon) @@ -207,11 +204,11 @@ class AddonsData(Config): def need_config(self, addon): """Return True if config map is needed.""" - return self._addons_data[addon][ATTR_MAP_CONFIG] + return self._system_data[addon][ATTR_MAP_CONFIG] def need_ssl(self, addon): """Return True if ssl map is needed.""" - return self._addons_data[addon][ATTR_MAP_SSL] + return self._system_data[addon][ATTR_MAP_SSL] def path_data(self, addon): """Return addon data path inside supervisor.""" @@ -241,7 +238,7 @@ class AddonsData(Config): def get_schema(self, addon): """Create a schema for addon options.""" - raw_schema = self._addons_data[addon][ATTR_SCHEMA] + raw_schema = self._system_data[addon][ATTR_SCHEMA] schema = vol.Schema(vol.All(dict, validate_options(raw_schema))) return schema diff --git a/hassio/api/__init__.py b/hassio/api/__init__.py index d345f6b35..835d74aeb 100644 --- a/hassio/api/__init__.py +++ b/hassio/api/__init__.py @@ -25,26 +25,26 @@ class RestAPI(object): self._handler = None self.server = None - def register_host(self, host_controll): - """Register hostcontroll function.""" - api_host = APIHost(self.config, self.loop, host_controll) + def register_host(self, host_control): + """Register hostcontrol function.""" + api_host = APIHost(self.config, self.loop, host_control) self.webapp.router.add_get('/host/info', api_host.info) self.webapp.router.add_get('/host/reboot', api_host.reboot) self.webapp.router.add_get('/host/shutdown', api_host.shutdown) self.webapp.router.add_get('/host/update', api_host.update) - def register_network(self, host_controll): + def register_network(self, host_control): """Register network function.""" - api_net = APINetwork(self.config, self.loop, host_controll) + api_net = APINetwork(self.config, self.loop, host_control) self.webapp.router.add_get('/network/info', api_net.info) self.webapp.router.add_get('/network/options', api_net.options) - def register_supervisor(self, supervisor, addons): + def register_supervisor(self, supervisor, addons, host_control): """Register supervisor function.""" api_supervisor = APISupervisor( - self.config, self.loop, supervisor, addons) + self.config, self.loop, supervisor, addons, host_control) self.webapp.router.add_get('/supervisor/ping', api_supervisor.ping) self.webapp.router.add_get('/supervisor/info', api_supervisor.info) diff --git a/hassio/api/addons.py b/hassio/api/addons.py index 71cfb6452..b1abb499f 100644 --- a/hassio/api/addons.py +++ b/hassio/api/addons.py @@ -7,7 +7,7 @@ from voluptuous.humanize import humanize_error from .util import api_process, api_process_raw, api_validate from ..const import ( - ATTR_VERSION, ATTR_CURRENT, ATTR_STATE, ATTR_BOOT, ATTR_OPTIONS, + ATTR_VERSION, ATTR_LAST_VERSION, ATTR_STATE, ATTR_BOOT, ATTR_OPTIONS, STATE_STOPPED, STATE_STARTED, BOOT_AUTO, BOOT_MANUAL) _LOGGER = logging.getLogger(__name__) @@ -47,14 +47,13 @@ class APIAddons(object): """Return addon information.""" addon = self._extract_addon(request) - info = { + return { ATTR_VERSION: self.addons.version_installed(addon), - ATTR_CURRENT: self.addons.get_version(addon), + ATTR_LAST_VERSION: self.addons.get_last_version(addon), ATTR_STATE: await self.addons.state(addon), ATTR_BOOT: self.addons.get_boot(addon), ATTR_OPTIONS: self.addons.get_options(addon), } - return info @api_process async def options(self, request): @@ -66,12 +65,12 @@ class APIAddons(object): vol.Optional(ATTR_OPTIONS): options_schema, }) - addon_config = await api_validate(addon_schema, request) + body = await api_validate(addon_schema, request) - if ATTR_OPTIONS in addon_config: - self.addons.set_options(addon, addon_config[ATTR_OPTIONS]) - if ATTR_BOOT in addon_config: - self.addons.set_options(addon, addon_config[ATTR_BOOT]) + if ATTR_OPTIONS in body: + self.addons.set_options(addon, body[ATTR_OPTIONS]) + if ATTR_BOOT in body: + self.addons.set_boot(addon, body[ATTR_BOOT]) return True @@ -81,7 +80,7 @@ class APIAddons(object): body = await api_validate(SCHEMA_VERSION, request) addon = self._extract_addon(request, check_installed=False) version = body.get( - ATTR_VERSION, self.addons.get_version(addon)) + ATTR_VERSION, self.addons.get_last_version(addon)) return await asyncio.shield( self.addons.install(addon, version), loop=self.loop) @@ -130,7 +129,7 @@ class APIAddons(object): body = await api_validate(SCHEMA_VERSION, request) addon = self._extract_addon(request) version = body.get( - ATTR_VERSION, self.addons.get_version(addon)) + ATTR_VERSION, self.addons.get_last_version(addon)) if version == self.addons.version_installed(addon): raise RuntimeError("Version is already in use") diff --git a/hassio/api/homeassistant.py b/hassio/api/homeassistant.py index 21b2d2fc1..a4d9786aa 100644 --- a/hassio/api/homeassistant.py +++ b/hassio/api/homeassistant.py @@ -5,7 +5,7 @@ import logging import voluptuous as vol from .util import api_process, api_process_raw, api_validate -from ..const import ATTR_VERSION, ATTR_CURRENT +from ..const import ATTR_VERSION, ATTR_LAST_VERSION _LOGGER = logging.getLogger(__name__) @@ -28,7 +28,7 @@ class APIHomeAssistant(object): """Return host information.""" info = { ATTR_VERSION: self.homeassistant.version, - ATTR_CURRENT: self.config.current_homeassistant, + ATTR_LAST_VERSION: self.config.last_homeassistant, } return info @@ -37,7 +37,7 @@ class APIHomeAssistant(object): async def update(self, request): """Update host OS.""" body = await api_validate(SCHEMA_VERSION, request) - version = body.get(ATTR_VERSION, self.config.current_homeassistant) + version = body.get(ATTR_VERSION, self.config.last_homeassistant) if self.homeassistant.in_progress: raise RuntimeError("Other task is in progress") diff --git a/hassio/api/host.py b/hassio/api/host.py index c799a42cf..d16927198 100644 --- a/hassio/api/host.py +++ b/hassio/api/host.py @@ -3,13 +3,12 @@ import logging import voluptuous as vol -from .util import api_process_hostcontroll, api_process, api_validate -from ..const import ATTR_VERSION +from .util import api_process_hostcontrol, api_process, api_validate +from ..const import ( + ATTR_VERSION, ATTR_LAST_VERSION, ATTR_TYPE, ATTR_HOSTNAME, ATTR_FEATURES) _LOGGER = logging.getLogger(__name__) -UNKNOWN = 'unknown' - SCHEMA_VERSION = vol.Schema({ vol.Optional(ATTR_VERSION): vol.Coerce(str), }) @@ -18,44 +17,40 @@ SCHEMA_VERSION = vol.Schema({ class APIHost(object): """Handle rest api for host functions.""" - def __init__(self, config, loop, host_controll): + def __init__(self, config, loop, host_control): """Initialize host rest api part.""" self.config = config self.loop = loop - self.host_controll = host_controll + self.host_control = host_control @api_process async def info(self, request): """Return host information.""" - if not self.host_controll.active: - info = { - 'os': UNKNOWN, - 'version': UNKNOWN, - 'current': UNKNOWN, - 'level': 0, - 'hostname': UNKNOWN, - } - return info + return { + ATTR_TYPE: self.host_control.type, + ATTR_VERSION: self.host_control.version, + ATTR_LAST_VERSION: self.host_control.last, + ATTR_FEATURES: self.host_control.features, + ATTR_HOSTNAME: self.host_control.hostname, + } - return await self.host_controll.info() - - @api_process_hostcontroll + @api_process_hostcontrol def reboot(self, request): """Reboot host.""" - return self.host_controll.reboot() + return self.host_control.reboot() - @api_process_hostcontroll + @api_process_hostcontrol def shutdown(self, request): """Poweroff host.""" - return self.host_controll.shutdown() + return self.host_control.shutdown() - @api_process_hostcontroll + @api_process_hostcontrol async def update(self, request): """Update host OS.""" body = await api_validate(SCHEMA_VERSION, request) version = body.get(ATTR_VERSION) - if version == self.host_controll.version: + if version == self.host_control.version: raise RuntimeError("Version is already in use") - return await self.host_controll.host_update(version=version) + return await self.host_control.update(version=version) diff --git a/hassio/api/network.py b/hassio/api/network.py index c768bb475..1d622edca 100644 --- a/hassio/api/network.py +++ b/hassio/api/network.py @@ -1,7 +1,7 @@ """Init file for HassIO network rest api.""" import logging -from .util import api_process_hostcontroll +from .util import api_process_hostcontrol _LOGGER = logging.getLogger(__name__) @@ -9,18 +9,18 @@ _LOGGER = logging.getLogger(__name__) class APINetwork(object): """Handle rest api for network functions.""" - def __init__(self, config, loop, host_controll): + def __init__(self, config, loop, host_control): """Initialize network rest api part.""" self.config = config self.loop = loop - self.host_controll = host_controll + self.host_control = host_control - @api_process_hostcontroll + @api_process_hostcontrol def info(self, request): """Show network settings.""" pass - @api_process_hostcontroll + @api_process_hostcontrol def options(self, request): """Edit network settings.""" pass diff --git a/hassio/api/supervisor.py b/hassio/api/supervisor.py index d6e89cf2f..feb83eb16 100644 --- a/hassio/api/supervisor.py +++ b/hassio/api/supervisor.py @@ -6,13 +6,14 @@ import voluptuous as vol from .util import api_process, api_process_raw, api_validate from ..const import ( - ATTR_ADDONS, ATTR_VERSION, ATTR_CURRENT, ATTR_BETA, HASSIO_VERSION) + ATTR_ADDONS, ATTR_VERSION, ATTR_LAST_VERSION, ATTR_BETA_CHANNEL, + HASSIO_VERSION) _LOGGER = logging.getLogger(__name__) SCHEMA_OPTIONS = vol.Schema({ # pylint: disable=no-value-for-parameter - vol.Optional(ATTR_BETA): vol.Boolean(), + vol.Optional(ATTR_BETA_CHANNEL): vol.Boolean(), }) SCHEMA_VERSION = vol.Schema({ @@ -23,12 +24,13 @@ SCHEMA_VERSION = vol.Schema({ class APISupervisor(object): """Handle rest api for supervisor functions.""" - def __init__(self, config, loop, supervisor, addons): + def __init__(self, config, loop, supervisor, addons, host_control): """Initialize supervisor rest api part.""" self.config = config self.loop = loop self.supervisor = supervisor self.addons = addons + self.host_control = host_control @api_process async def ping(self, request): @@ -38,21 +40,20 @@ class APISupervisor(object): @api_process async def info(self, request): """Return host information.""" - info = { + return { ATTR_VERSION: HASSIO_VERSION, - ATTR_CURRENT: self.config.current_hassio, - ATTR_BETA: self.config.upstream_beta, - ATTR_ADDONS: self.addons.list, + ATTR_LAST_VERSION: self.config.last_hassio, + ATTR_BETA_CHANNEL: self.config.upstream_beta, + ATTR_ADDONS: self.addons.list_api, } - return info @api_process async def options(self, request): """Set supervisor options.""" body = await api_validate(SCHEMA_OPTIONS, request) - if ATTR_BETA in body: - self.config.upstream_beta = body[ATTR_BETA] + if ATTR_BETA_CHANNEL in body: + self.config.upstream_beta = body[ATTR_BETA_CHANNEL] return self.config.save() @@ -60,7 +61,7 @@ class APISupervisor(object): async def update(self, request): """Update supervisor OS.""" body = await api_validate(SCHEMA_VERSION, request) - version = body.get(ATTR_VERSION, self.config.current_hassio) + version = body.get(ATTR_VERSION, self.config.last_hassio) if version == self.supervisor.version: raise RuntimeError("Version is already in use") @@ -71,7 +72,10 @@ class APISupervisor(object): @api_process async def reload(self, request): """Reload addons, config ect.""" - tasks = [self.addons.reload(), self.config.fetch_update_infos()] + tasks = [ + self.addons.reload(), self.config.fetch_update_infos(), + self.host_control.load() + ] results, _ = await asyncio.shield( asyncio.wait(tasks, loop=self.loop), loop=self.loop) diff --git a/hassio/api/util.py b/hassio/api/util.py index 54a826765..70a1eee7c 100644 --- a/hassio/api/util.py +++ b/hassio/api/util.py @@ -39,11 +39,11 @@ def api_process(method): return wrap_api -def api_process_hostcontroll(method): - """Wrap HostControll calls to rest api.""" - async def wrap_hostcontroll(api, *args, **kwargs): +def api_process_hostcontrol(method): + """Wrap HostControl calls to rest api.""" + async def wrap_hostcontrol(api, *args, **kwargs): """Return host information.""" - if not api.host_controll.active: + if not api.host_control.active: raise HTTPServiceUnavailable() try: @@ -59,7 +59,7 @@ def api_process_hostcontroll(method): return api_return_ok() return api_return_error() - return wrap_hostcontroll + return wrap_hostcontrol def api_process_raw(method): diff --git a/hassio/config.py b/hassio/config.py index dacdf6440..e5f03bea3 100644 --- a/hassio/config.py +++ b/hassio/config.py @@ -10,10 +10,10 @@ _LOGGER = logging.getLogger(__name__) HOMEASSISTANT_CONFIG = "{}/homeassistant" HOMEASSISTANT_IMAGE = 'homeassistant_image' -HOMEASSISTANT_CURRENT = 'homeassistant_current' +HOMEASSISTANT_LAST = 'homeassistant_last' HASSIO_SSL = "{}/ssl" -HASSIO_CURRENT = 'hassio_current' +HASSIO_LAST = 'hassio_last' HASSIO_CLEANUP = 'hassio_cleanup' ADDONS_REPO = "{}/addons" @@ -67,13 +67,13 @@ class CoreConfig(Config): async def fetch_update_infos(self): """Read current versions from web.""" - current = await fetch_current_versions( + last = await fetch_current_versions( self.websession, beta=self.upstream_beta) - if current: + if last: self._data.update({ - HOMEASSISTANT_CURRENT: current.get('homeassistant_tag'), - HASSIO_CURRENT: current.get('hassio_tag'), + HOMEASSISTANT_LAST: last.get('homeassistant'), + HASSIO_LAST: last.get('hassio'), }) self.save() return True @@ -120,14 +120,14 @@ class CoreConfig(Config): return self._data.get(HOMEASSISTANT_IMAGE) @property - def current_homeassistant(self): + def last_homeassistant(self): """Actual version of homeassistant.""" - return self._data.get(HOMEASSISTANT_CURRENT) + return self._data.get(HOMEASSISTANT_LAST) @property - def current_hassio(self): + def last_hassio(self): """Actual version of hassio.""" - return self._data.get(HASSIO_CURRENT) + return self._data.get(HASSIO_LAST) @property def path_hassio_docker(self): diff --git a/hassio/const.py b/hassio/const.py index 053b1db59..977143168 100644 --- a/hassio/const.py +++ b/hassio/const.py @@ -1,5 +1,5 @@ """Const file for HassIO.""" -HASSIO_VERSION = '0.14' +HASSIO_VERSION = '0.15' URL_HASSIO_VERSION = \ 'https://raw.githubusercontent.com/pvizeli/hassio/master/version.json' @@ -31,10 +31,13 @@ JSON_MESSAGE = 'message' RESULT_ERROR = 'error' RESULT_OK = 'ok' +ATTR_HOSTNAME = 'hostname' +ATTR_TYPE = 'type' +ATTR_FEATURES = 'features' ATTR_ADDONS = 'addons' ATTR_VERSION = 'version' -ATTR_CURRENT = 'current' -ATTR_BETA = 'beta' +ATTR_LAST_VERSION = 'last_version' +ATTR_BETA_CHANNEL = 'beta_channel' ATTR_NAME = 'name' ATTR_SLUG = 'slug' ATTR_DESCRIPTON = 'description' diff --git a/hassio/core.py b/hassio/core.py index 4a280da8c..2e8cf4184 100644 --- a/hassio/core.py +++ b/hassio/core.py @@ -8,7 +8,7 @@ import docker from . import bootstrap from .addons import AddonManager from .api import RestAPI -from .host_controll import HostControll +from .host_control import HostControl from .const import ( SOCKET_DOCKER, RUN_UPDATE_INFO_TASKS, RUN_RELOAD_ADDONS_TASKS, RUN_UPDATE_SUPERVISOR_TASKS, STARTUP_AFTER, STARTUP_BEFORE) @@ -40,8 +40,8 @@ class HassIO(object): self.homeassistant = DockerHomeAssistant( self.config, self.loop, self.dock) - # init HostControll - self.host_controll = HostControll(self.loop) + # init HostControl + self.host_control = HostControl(self.loop) # init addon system self.addons = AddonManager(self.config, self.loop, self.dock) @@ -55,20 +55,19 @@ class HassIO(object): # set api endpoint self.config.api_endpoint = await get_local_ip(self.loop) - # hostcontroll - host_info = await self.host_controll.info() - if host_info: - self.host_controll.version = host_info.get('version') - _LOGGER.info( - "Connected to HostControll. OS: %s Version: %s Hostname: %s " - "Feature-lvl: %d", host_info.get('os'), - host_info.get('version'), host_info.get('hostname'), - host_info.get('level', 0)) + # 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) # rest api views - self.api.register_host(self.host_controll) - self.api.register_network(self.host_controll) - self.api.register_supervisor(self.supervisor, self.addons) + self.api.register_host(self.host_control) + self.api.register_network(self.host_control) + self.api.register_supervisor( + self.supervisor, self.addons, self.host_control) self.api.register_homeassistant(self.homeassistant) self.api.register_addons(self.addons) @@ -130,10 +129,10 @@ class HassIO(object): """Install a homeassistant docker container.""" while True: # read homeassistant tag and install it - if not self.config.current_homeassistant: + if not self.config.last_homeassistant: await self.config.fetch_update_infos() - tag = self.config.current_homeassistant + tag = self.config.last_homeassistant if tag and await self.homeassistant.install(tag): break _LOGGER.warning("Error on setup HomeAssistant. Retry in 60.") @@ -144,9 +143,9 @@ class HassIO(object): async def _hassio_update(self): """Check and run update of supervisor hassio.""" - if self.config.current_hassio == self.supervisor.version: + if self.config.last_hassio == self.supervisor.version: return _LOGGER.info( - "Found new HassIO version %s.", self.config.current_hassio) - await self.supervisor.update(self.config.current_hassio) + "Found new HassIO version %s.", self.config.last_hassio) + await self.supervisor.update(self.config.last_hassio) diff --git a/hassio/host_controll.py b/hassio/host_control.py similarity index 56% rename from hassio/host_controll.py rename to hassio/host_control.py index 7685f9760..e72892f69 100644 --- a/hassio/host_controll.py +++ b/hassio/host_control.py @@ -1,4 +1,4 @@ -"""Host controll for HassIO.""" +"""Host control for HassIO.""" import asyncio import json import logging @@ -7,25 +7,34 @@ import stat import async_timeout -from .const import SOCKET_HC +from .const import ( + SOCKET_HC, ATTR_LAST_VERSION, ATTR_VERSION, ATTR_TYPE, ATTR_FEATURES, + ATTR_HOSTNAME) _LOGGER = logging.getLogger(__name__) TIMEOUT = 15 +UNKNOWN = 'unknown' -LEVEL_POWER = 1 -LEVEL_UPDATE_HOST = 2 -LEVEL_NETWORK = 4 +FEATURES_SHUTDOWN = 'shutdown' +FEATURES_REBOOT = 'reboot' +FEATURES_UPDATE = 'update' +FEATURES_NETWORK_INFO = 'network_info' +FEATURES_NETWORK_CONTROL = 'network_control' -class HostControll(object): - """Client for host controll.""" +class HostControl(object): + """Client for host control.""" def __init__(self, loop): - """Initialize HostControll socket client.""" + """Initialize HostControl socket client.""" self.loop = loop self.active = False - self.version = None + self.version = UNKNOWN + self.last = UNKNOWN + self.type = UNKNOWN + self.features = [] + self.hostname = UNKNOWN mode = os.stat(SOCKET_HC)[stat.ST_MODE] if stat.S_ISSOCK(mode): @@ -44,14 +53,14 @@ class HostControll(object): try: # send - _LOGGER.info("Send '%s' to HostControll.", command) + _LOGGER.info("Send '%s' to HostControl.", command) with async_timeout.timeout(TIMEOUT, loop=self.loop): writer.write("{}\n".format(command).encode()) data = await reader.readline() response = data.decode() - _LOGGER.debug("Receive from HostControll: %s.", response) + _LOGGER.debug("Receive from HostControl: %s.", response) if response == "OK": return True @@ -63,20 +72,28 @@ class HostControll(object): try: return json.loads(response) except json.JSONDecodeError: - _LOGGER.warning("Json parse error from HostControll.") + _LOGGER.warning("Json parse error from HostControl.") except asyncio.TimeoutError: - _LOGGER.error("Timeout from HostControll!") + _LOGGER.error("Timeout from HostControl!") finally: writer.close() - def info(self): - """Return Info from host. + async def load(self): + """Load Info from host. Return a coroutine. """ - return self._send_command("info") + info = await self._send_command("info") + if not info: + return + + self.version = info.get(ATTR_VERSION, UNKNOWN) + self.last = 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) def reboot(self): """Reboot the host system. @@ -92,11 +109,11 @@ class HostControll(object): """ return self._send_command("shutdown") - def host_update(self, version=None): + def update(self, version=None): """Update the host system. Return a coroutine. """ if version: - return self._send_command("host-update {}".format(version)) - return self._send_command("host-update") + return self._send_command("update {}".format(version)) + return self._send_command("update")