diff --git a/hassio/addons/__init__.py b/hassio/addons/__init__.py index f7c40ab3f..286cdab0c 100644 --- a/hassio/addons/__init__.py +++ b/hassio/addons/__init__.py @@ -112,3 +112,19 @@ class AddonManager(object): return False return True + + async def update_addon(self, addon, version=None): + """Update addon.""" + if self.addons.is_installed(addon): + _LOGGER.error("Addon %s is not installed.", addon) + return False + + if addon not in self.dockers: + _LOGGER.error("No docker found for addon %s.", addon) + return False + + version = version or self.addons.get_version(addon) + if not await self.dockers[addon].update(version): + return False + + return True diff --git a/hassio/addons/config.py b/hassio/addons/config.py index 6d7a91983..31bd92f2f 100644 --- a/hassio/addons/config.py +++ b/hassio/addons/config.py @@ -9,7 +9,7 @@ from ..const import ( FILE_HASSIO_ADDONS, ATTR_NAME, ATTR_VERSION, ATTR_SLUG, ATTR_DESCRIPTON, ATTR_STARTUP, ATTR_BOOT, ATTR_MAP_SSL, ATTR_MAP_CONFIG, ATTR_MAP_DATA, ATTR_OPTIONS, ATTR_PORTS, STARTUP_ONCE, STARTUP_AFTER, STARTUP_BEFORE, - BOOT_AUTO, BOOT_MANUAL, DOCKER_REPO) + BOOT_AUTO, BOOT_MANUAL, DOCKER_REPO, ATTR_INSTALLED) from ..tools import read_json_file, write_json_file _LOGGER = logging.getLogger(__name__) @@ -65,6 +65,26 @@ class AddonsConfig(Config): """Return a list of installed addons.""" return self._data.keys() + @property + def list_all(self): + """Return a list of available addons.""" + return self._addons_data.keys() + + @property + def list(self): + """Return a list of available addons.""" + data = [] + for addon, values in self._addons.items(): + data.append({ + ATTR_NAME: values[ATTR_NAME], + ATTR_SLUG: values[ATTR_SLUG], + ATTR_DESCRIPTON: values[ATTR_DESCRIPTON], + ATTR_VERSION: values[ATTR_VERSION], + ATTR_INSTALLED: self._data.get(addon, {}).get(ATTR_VERSION), + }) + + return data + def exists_addon(self, addon): """Return True if a addon exists.""" return addon in self._addons_data @@ -73,6 +93,10 @@ class AddonsConfig(Config): """Return True if a addon is installed.""" return addon in self._data + def version_installed(self, addon): + """Return installed version.""" + return self._data[addon][ATTR_VERSION] + def set_install_addon(self, addon, version): """Set addon as installed.""" self._data[addon] = { @@ -93,12 +117,27 @@ class AddonsConfig(Config): opt.update(self._data[addon][ATTR_OPTIONS]) return opt + def get_boot(self, addon): + """Return boot config with prio local settings.""" + if ATTR_BOOT in self._data[addon]: + return self._data[addon][ATTR_BOOT] + + return self._addons_data[addon][ATTR_BOOT] + def get_image(self, addon): """Return name of addon docker image.""" return "{}/{}-addon-{}".format( DOCKER_REPO, self.config.hassio_arch, self._addons_data[addon][ATTR_SLUG]) + def get_name(self, addon): + """Return name of addon.""" + return self._addons_data[addon][ATTR_NAME] + + def get_description(self, addon): + """Return description of addon.""" + return self._addons_data[addon][ATTR_DESCRIPTON] + def get_version(self, addon): """Return version of addon.""" return self._addons_data[addon][ATTR_VERSION] diff --git a/hassio/api/__init__.py b/hassio/api/__init__.py index 1bd0d577a..6135530a4 100644 --- a/hassio/api/__init__.py +++ b/hassio/api/__init__.py @@ -3,10 +3,11 @@ import logging from aiohttp import web +from .addons import APIAddonManager +from .homeassistant import APIHomeAssistant from .host import APIHost from .network import APINetwork from .supervisor import APISupervisor -from .homeassistant import APIHomeAssistant _LOGGER = logging.getLogger(__name__) @@ -40,9 +41,10 @@ class RestAPI(object): self.webapp.router.add_get('/network/info', api_net.info) self.webapp.router.add_get('/network/options', api_net.options) - def register_supervisor(self, host_controll): + def register_supervisor(self, host_controll, addon_manager): """Register supervisor function.""" - api_supervisor = APISupervisor(self.config, self.loop, host_controll) + api_supervisor = APISupervisor( + self.config, self.loop, host_controll, addon_manager) self.webapp.router.add_get('/supervisor/ping', api_supervisor.ping) self.webapp.router.add_get('/supervisor/info', api_supervisor.info) @@ -57,6 +59,21 @@ class RestAPI(object): self.webapp.router.add_get('/homeassistant/info', api_hass.info) self.webapp.router.add_get('/homeassistant/update', api_hass.update) + def register_addons(self, addon_manager): + """Register homeassistant function.""" + api_addons = APIAddons(self.config, self.loop, addon_manager) + + self.webapp.router.add_get('/addons/{addon}/info', api_addons.info) + self.webapp.router.add_get( + '/addons/{addon}/install', api_addons.install) + self.webapp.router.add_get( + '/addons/{addon}/uninstall', api_addons.uninstall) + self.webapp.router.add_get('/addons/{addon}/start', api_addons.start) + self.webapp.router.add_get('/addons/{addon}/stop', api_addons.stop) + self.webapp.router.add_get('/addons/{addon}/update', api_addons.update) + self.webapp.router.add_get( + '/addons/{addon}/options', api_addons.options) + async def start(self): """Run rest api webserver.""" self._handler = self.webapp.make_handler(loop=self.loop) diff --git a/hassio/api/addons.py b/hassio/api/addons.py new file mode 100644 index 000000000..e87f3bcbf --- /dev/null +++ b/hassio/api/addons.py @@ -0,0 +1,42 @@ +"""Init file for HassIO homeassistant rest api.""" +import asyncio +import logging + +import voluptuous as vol + +from .util import api_process, api_validate +from ..const import ATTR_VERSION, ATTR_CURRENT + +_LOGGER = logging.getLogger(__name__) + +SCHEMA_VERSION = vol.Schema({ + vol.Optional(ATTR_VERSION): vol.Coerce(str), +}) + + +class APIAddons(object): + """Handle rest api for addons functions.""" + + def __init__(self, config, loop, addon_manager): + """Initialize homeassistant rest api part.""" + self.config = config + self.loop = loop + self.addon_manager = addon_manager + + @api_process + async def info(self, request): + """Return host information.""" + + @api_process + async def update(self, request): + """Update host OS.""" + body = await api_validate(SCHEMA_VERSION, request) + version = body.get(ATTR_VERSION, self.config.current_homeassistant) + + if self.dock_hass.in_progress: + raise RuntimeError("Other task is in progress.") + + if version == self.dock_hass.version: + raise RuntimeError("%s is already in use.", version) + + return await asyncio.shield(self.dock_hass.update(version)) diff --git a/hassio/api/supervisor.py b/hassio/api/supervisor.py index 73ef6ae5b..4cd69c31f 100644 --- a/hassio/api/supervisor.py +++ b/hassio/api/supervisor.py @@ -4,7 +4,8 @@ import logging import voluptuous as vol from .util import api_process, api_process_hostcontroll, api_validate -from ..const import ATTR_VERSION, ATTR_CURRENT, ATTR_BETA, HASSIO_VERSION +from ..const import ( + ATTR_ADDONS, ATTR_VERSION, ATTR_CURRENT, ATTR_BETA, HASSIO_VERSION) _LOGGER = logging.getLogger(__name__) @@ -21,11 +22,12 @@ SCHEMA_VERSION = vol.Schema({ class APISupervisor(object): """Handle rest api for supervisor functions.""" - def __init__(self, config, loop, host_controll): + def __init__(self, config, loop, host_controll, addon_manager): """Initialize supervisor rest api part.""" self.config = config self.loop = loop self.host_controll = host_controll + self.addon_manager = addon_manager @api_process async def ping(self, request): @@ -39,8 +41,8 @@ class APISupervisor(object): ATTR_VERSION: HASSIO_VERSION, ATTR_CURRENT: self.config.current_hassio, ATTR_BETA: self.config.upstream_beta, + ATTR_ADDONS: self.addon_manager.addons.list, } - return info @api_process diff --git a/hassio/const.py b/hassio/const.py index 0cdb868d3..41621b806 100644 --- a/hassio/const.py +++ b/hassio/const.py @@ -27,6 +27,7 @@ JSON_MESSAGE = 'message' RESULT_ERROR = 'error' RESULT_OK = 'ok' +ATTR_ADDONS = 'addons' ATTR_VERSION = 'version' ATTR_CURRENT = 'current' ATTR_BETA = 'beta' diff --git a/hassio/core.py b/hassio/core.py index e6dd4e9c1..176772ddd 100644 --- a/hassio/core.py +++ b/hassio/core.py @@ -60,8 +60,9 @@ class HassIO(object): # rest api views self.api.register_host(self.host_controll) self.api.register_network(self.host_controll) - self.api.register_supervisor(self.host_controll) + self.api.register_supervisor(self.host_controll, self.addon_manager) self.api.register_homeassistant(self.homeassistant) + self.api.register_addons(self.addon_manager) # schedule update info tasks self.scheduler.register_task(