diff --git a/hassio/addons/__init__.py b/hassio/addons/__init__.py index 1d25d8dc9..f7c40ab3f 100644 --- a/hassio/addons/__init__.py +++ b/hassio/addons/__init__.py @@ -10,7 +10,7 @@ from ..docker.addon import DockerAddon _LOGGER = logging.getLogger(__name__) -class AddonsManager(object): +class AddonManager(object): """Manage addons inside HassIO.""" def __init__(self, config, loop, dock): @@ -46,7 +46,7 @@ class AddonsManager(object): return False if self.addons.is_installed(addon): - _LOGGER.error("Addon %s is allready installed.", addon) + _LOGGER.error("Addon %s is already installed.", addon) return False if not os.path.isdir(self.addons.path_data(addon)): @@ -59,7 +59,6 @@ class AddonsManager(object): version = version or self.addons.get_version(addon) if not await addon_docker.install(version): - _LOGGER.error("Can't install addon %s version %s.", addon, version) return False self.dockers[addon] = addon_docker @@ -68,8 +67,8 @@ class AddonsManager(object): async def uninstall_addon(self, addon): """Remove a addon.""" - if self.addons.is_installed(addon): - _LOGGER.error("Addon %s is allready installed.", addon) + if not self.addons.is_installed(addon): + _LOGGER.error("Addon %s is already uninstalled.", addon) return False if addon not in self.dockers: @@ -77,7 +76,6 @@ class AddonsManager(object): return False if not await self.dockers[addon].remove(version): - _LOGGER.error("Can't install addon %s.", addon) return False if os.path.isdir(self.addons.path_data(addon)): @@ -88,3 +86,29 @@ class AddonsManager(object): self.dockers.pop(addon) self.addons.set_uninstall_addon(addon) return True + + async def start_addon(self, addon): + """Set options and start addon.""" + if addon not in self.dockers: + _LOGGER.error("No docker found for addon %s.", addon) + return False + + if not self.write_addon_options(addon): + _LOGGER.error("Can't write options for addon %s.", addon) + return False + + if not await self.dockers[addon].run(): + return False + + return True + + async def stop_addon(self, addon): + """Stop addon.""" + if addon not in self.dockers: + _LOGGER.error("No docker found for addon %s.", addon) + return False + + if not await self.dockers[addon].stop(): + return False + + return True diff --git a/hassio/addons/config.py b/hassio/addons/config.py index c90bc9f74..6d7a91983 100644 --- a/hassio/addons/config.py +++ b/hassio/addons/config.py @@ -1,7 +1,6 @@ """Init file for HassIO addons.""" import logging import glob -import json import voluptuous as vol from voluptuous.humanize import humanize_error @@ -11,6 +10,7 @@ from ..const import ( 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) +from ..tools import read_json_file, write_json_file _LOGGER = logging.getLogger(__name__) @@ -48,8 +48,7 @@ class AddonsConfig(Config): for addon in glob.iglob(pattern): try: - with open(addon, 'r') as cfile: - addon_config = json.loads(cfile.read()) + addon_config = read_json_file(addon) addon_config = SCHEMA_ADDON_CONFIG(addon_config) self._addons_data[addon_config[ATTR_SLUG]] = addon_config @@ -76,7 +75,10 @@ class AddonsConfig(Config): def set_install_addon(self, addon, version): """Set addon as installed.""" - self._data[addon] = {ATTR_VERSION: version} + self._data[addon] = { + ATTR_VERSION: version, + ATTR_OPTIONS: {} + } self.save() def set_uninstall_addon(self, addon, version): @@ -84,6 +86,13 @@ class AddonsConfig(Config): self._data.pop(addon, None) self.save() + def get_options(self, addon): + """Return options with local changes.""" + opt = self._addons_data[addon][ATTR_OPTIONS] + if addon in self._data: + opt.update(self._data[addon][ATTR_OPTIONS]) + return opt + def get_image(self, addon): """Return name of addon docker image.""" return "{}/{}-addon-{}".format( @@ -123,3 +132,12 @@ class AddonsConfig(Config): """Return addon data path external for docker.""" return "{}/{}".format(self.config.path_addons_data_docker, self._addons_data[addon][ATTR_SLUG]) + + def path_addon_options(self, addon): + """Return path to addons options.""" + return "{}/options.json".format(self.path_data(addon)) + + def write_addon_options(self, addon): + """Return True if addon options is written to data.""" + return write_json_file( + self.path_addon_options(addon), self.get_options(addon)) diff --git a/hassio/config.py b/hassio/config.py index 65905682c..173e7dee1 100644 --- a/hassio/config.py +++ b/hassio/config.py @@ -4,7 +4,9 @@ import logging import os from .const import FILE_HASSIO_CONFIG, HASSIO_SHARE -from .tools import fetch_current_versions, get_arch_from_image +from .tools import ( + fetch_current_versions, get_arch_from_image, write_json_file, + read_json_file) _LOGGER = logging.getLogger(__name__) @@ -32,20 +34,15 @@ class Config(object): # init or load data if os.path.isfile(self._filename): try: - with open(self._filename, 'r') as cfile: - self._data = json.loads(cfile.read()) + self._data = read_json_file(self._filename) except OSError: _LOGGER.warning("Can't read %s", self._filename) def save(self): """Store data to config file.""" - try: - with open(self._filename, 'w') as conf_file: - conf_file.write(json.dumps(self._data)) - except OSError: + if not write_json_file(self._filename, self._data) _LOGGER.exception("Can't store config in %s", self._filename) return False - return True diff --git a/hassio/core.py b/hassio/core.py index 0b19eff78..e6dd4e9c1 100644 --- a/hassio/core.py +++ b/hassio/core.py @@ -6,6 +6,7 @@ import aiohttp import docker from . import bootstrap +from .addons import AddonManager from .api import RestAPI from .host_controll import HostControll from .const import SOCKET_DOCKER, RUN_UPDATE_INFO_TASKS @@ -38,6 +39,9 @@ class HassIO(object): # init HostControll self.host_controll = HostControll(self.loop) + # init addon system + self.addon_manager = AddonManager(self.config, self.loop, self.dock) + async def setup(self): """Setup HassIO orchestration.""" # supervisor @@ -69,6 +73,9 @@ class HassIO(object): _LOGGER.info("No HomeAssistant docker found.") await self._setup_homeassistant() + # Load addons + await self.addon_manager.prepare() + async def start(self): """Start HassIO orchestration.""" # start api diff --git a/hassio/tools.py b/hassio/tools.py index e0033170e..aedbd6df1 100644 --- a/hassio/tools.py +++ b/hassio/tools.py @@ -1,5 +1,6 @@ """Tools file for HassIO.""" import asyncio +import json import logging import re import socket @@ -64,3 +65,20 @@ def get_local_ip(loop): return socket.gethostbyname(socket.gethostname()) finally: sock.close() + + +def write_json_file(jsonfile, data): + """Write a json file.""" + try: + with open(jsonfile, 'w') as conf_file: + conf_file.write(json.dumps(data)) + except OSError: + return False + + return True + + +def read_json_file(jsonfile): + """Read a json file and return a dict.""" + with open(jsonfile, 'r') as cfile: + return json.loads(cfile.read())