From a14917e017ecf1dd02465ddf15954693cad1a9da Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 7 Jul 2017 14:21:41 +0200 Subject: [PATCH 01/18] Pump version 0.43 --- hassio/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hassio/const.py b/hassio/const.py index da9ac8e93ff..f63515f1447 100644 --- a/hassio/const.py +++ b/hassio/const.py @@ -1,7 +1,7 @@ """Const file for HassIO.""" from pathlib import Path -HASSIO_VERSION = '0.42' +HASSIO_VERSION = '0.43' URL_HASSIO_VERSION = ('https://raw.githubusercontent.com/home-assistant/' 'hassio/master/version.json') From 02c8baef68252abbb5a57cbfffc6ef2dc88ba13b Mon Sep 17 00:00:00 2001 From: pvizeli Date: Tue, 11 Jul 2017 12:26:58 +0200 Subject: [PATCH 02/18] Add landingpage / cleanup homeassistant core handling --- hassio/addons/data.py | 38 ++-------------------- hassio/config.py | 61 ++---------------------------------- hassio/const.py | 1 + hassio/dock/homeassistant.py | 9 +++--- hassio/homeassistant.py | 58 ++++++++++++++++++++++++++++++++++ hassio/tools.py | 42 +++++++++++++++++++++++++ hassio/validate.py | 9 ++++++ 7 files changed, 121 insertions(+), 97 deletions(-) create mode 100644 hassio/homeassistant.py diff --git a/hassio/addons/data.py b/hassio/addons/data.py index 35c1cddce53..51c652cb1eb 100644 --- a/hassio/addons/data.py +++ b/hassio/addons/data.py @@ -15,54 +15,22 @@ from ..const import ( FILE_HASSIO_ADDONS, ATTR_VERSION, ATTR_SLUG, ATTR_REPOSITORY, ATTR_LOCATON, REPOSITORY_CORE, REPOSITORY_LOCAL, ATTR_USER, ATTR_SYSTEM) -from ..tools import read_json_file, write_json_file +from ..tools import JsonConfig _LOGGER = logging.getLogger(__name__) RE_VOLUME = re.compile(MAP_VOLUME) -class Data(object): +class Data(JsonConfig): """Hold data for addons inside HassIO.""" def __init__(self, config): """Initialize data holder.""" - self._file = FILE_HASSIO_ADDONS - self._data = {} + super().__init__(FILE_HASSIO_ADDONS, SCHEMA_ADDON_FILE) self.config = config - self._cache = {} self._repositories = {} - # init or load data - if self._file.is_file(): - try: - self._data = read_json_file(self._file) - except (OSError, json.JSONDecodeError): - _LOGGER.warning("Can't read %s", self._file) - self._data = {} - - # validate - try: - self._data = SCHEMA_ADDON_FILE(self._data) - except vol.Invalid as ex: - _LOGGER.error("Can't parse addons.json -> %s", - humanize_error(self._data, ex)) - - def save(self): - """Store data to config file.""" - # validate - try: - self._data = SCHEMA_ADDON_FILE(self._data) - except vol.Invalid as ex: - _LOGGER.error("Can't parse addons data -> %s", - humanize_error(self._data, ex)) - return False - - if not write_json_file(self._file, self._data): - _LOGGER.error("Can't store config in %s", self._file) - return False - return True - @property def user(self): """Return local addon user data.""" diff --git a/hassio/config.py b/hassio/config.py index 02fe0e3a49c..bc57e9865be 100644 --- a/hassio/config.py +++ b/hassio/config.py @@ -6,11 +6,9 @@ from pathlib import Path, PurePath import voluptuous as vol -from voluptuous.humanize import humanize_error from .const import FILE_HASSIO_CONFIG, HASSIO_DATA -from .tools import ( - fetch_last_versions, write_json_file, read_json_file, validate_timezone) +from .tools import fetch_last_versions, JsonConfig, validate_timezone from .validate import HASS_DEVICES _LOGGER = logging.getLogger(__name__) @@ -19,7 +17,6 @@ HOMEASSISTANT_CONFIG = PurePath("homeassistant") HOMEASSISTANT_LAST = 'homeassistant_last' -HOMEASSISTANT_DEVICES = 'homeassistant_devices' HASSIO_SSL = PurePath("ssl") HASSIO_LAST = 'hassio_last' @@ -50,7 +47,6 @@ vol.Optional(API_ENDPOINT): vol.Coerce(str), vol.Optional(TIMEZONE, default='UTC'): validate_timezone, vol.Optional(HOMEASSISTANT_LAST): vol.Coerce(str), - vol.Optional(HOMEASSISTANT_DEVICES, default=[]): HASS_DEVICES, vol.Optional(HASSIO_LAST): vol.Coerce(str), vol.Optional(ADDONS_CUSTOM_LIST, default=[]): [vol.Url()], vol.Optional(SECURITY_INITIALIZE, default=False): vol.Boolean(), @@ -61,48 +57,13 @@ }, extra=vol.REMOVE_EXTRA) -class CoreConfig(object): +class CoreConfig(JsonConfig): """Hold all core config data.""" def __init__(self): """Initialize config object.""" + super().__init__(FILE_HASSIO_CONFIG, SCHEMA_CONFIG) self.arch = None - self._file = FILE_HASSIO_CONFIG - self._data = {} - - # init or load data - if self._file.is_file(): - try: - self._data = read_json_file(self._file) - except (OSError, json.JSONDecodeError): - _LOGGER.warning("Can't read %s", self._file) - self._data = {} - - # validate data - if not self._validate_config(): - self._data = SCHEMA_CONFIG({}) - - def _validate_config(self): - """Validate config and return True or False.""" - # validate data - try: - self._data = SCHEMA_CONFIG(self._data) - except vol.Invalid as ex: - _LOGGER.warning( - "Invalid config %s", humanize_error(self._data, ex)) - return False - - return True - - def save(self): - """Store data to config file.""" - if not self._validate_config(): - return False - - if not write_json_file(self._file, self._data): - _LOGGER.error("Can't store config in %s", self._file) - return False - return True async def fetch_update_infos(self, websession): """Read current versions from web.""" @@ -150,22 +111,6 @@ def timezone(self, value): self._data[TIMEZONE] = value self.save() - @property - def homeassistant_devices(self): - """Return list of special device to map into homeassistant.""" - return self._data[HOMEASSISTANT_DEVICES] - - @homeassistant_devices.setter - def homeassistant_devices(self, value): - """Set list of special device.""" - self._data[HOMEASSISTANT_DEVICES] = value - self.save() - - @property - def homeassistant_image(self): - """Return docker homeassistant repository.""" - return os.environ['HOMEASSISTANT_REPOSITORY'] - @property def last_homeassistant(self): """Actual version of homeassistant.""" diff --git a/hassio/const.py b/hassio/const.py index f63515f1447..c8ee16ad9de 100644 --- a/hassio/const.py +++ b/hassio/const.py @@ -24,6 +24,7 @@ FILE_HASSIO_ADDONS = Path(HASSIO_DATA, "addons.json") FILE_HASSIO_CONFIG = Path(HASSIO_DATA, "config.json") +FILE_HASSIO_HOMEASSISTANT = Path(HASSIO_DATA, "homeassistant.json") SOCKET_DOCKER = Path("/var/run/docker.sock") SOCKET_HC = Path("/var/run/hassio-hc.sock") diff --git a/hassio/dock/homeassistant.py b/hassio/dock/homeassistant.py index 7b361bf5b79..d86202b66b9 100644 --- a/hassio/dock/homeassistant.py +++ b/hassio/dock/homeassistant.py @@ -13,9 +13,10 @@ class DockerHomeAssistant(DockerBase): """Docker hassio wrapper for HomeAssistant.""" - def __init__(self, config, loop, dock): + def __init__(self, config, loop, dock, data): """Initialize docker homeassistant wrapper.""" - super().__init__(config, loop, dock, image=config.homeassistant_image) + super().__init__(config, loop, dock, image=data.image) + self.data = data @property def name(self): @@ -25,11 +26,11 @@ def name(self): @property def devices(self): """Create list of special device to map into docker.""" - if not self.config.homeassistant_devices: + if not self.data.devices: return devices = [] - for device in self.config.homeassistant_devices: + for device in self.data.devices: devices.append("/dev/{0}:/dev/{0}:rwm".format(device)) return devices diff --git a/hassio/homeassistant.py b/hassio/homeassistant.py new file mode 100644 index 00000000000..82525e5f53f --- /dev/null +++ b/hassio/homeassistant.py @@ -0,0 +1,58 @@ +"""HomeAssistant control object.""" +import logging +import os + +from .const import FILE_HASSIO_HOMEASSISTANT, ATTR_DEVICES, ATTR_IMAGE +from .dock.homeassistant import DockerHomeAssistant +from .tools import JsonConfig +from .validate import SCHEMA_HASS_CONFIG + + +class HomeAssistant(JsonConfig): + """Hass core object for handle it.""" + + def __init__(self, config, loop, dock): + """Initialize hass object.""" + super().__init__(FILE_HASSIO_HOMEASSISTANT, SCHEMA_HASS_CONFIG) + self.config = config + self.loop = loop + + async def prepare(self): + """Prepare HomeAssistant object.""" + + @property + def version(self): + """Return version of running homeassistant.""" + return self.docker.version + + @property + def last_version(self): + """Return last available version of homeassistant.""" + return self.config.last_homeassistant + + @property + def image(self): + """Return image name of hass containter.""" + if ATTR_IMAGE in self._data: + return self._data[ATTR_IMAGE] + return os.environ['HOMEASSISTANT_REPOSITORY'] + + @image.setter + def image(self, value): + """Set image name of hass containter.""" + if value is None: + self._data.pop(ATTR_IMAGE, None) + else: + self._data[ATTR_IMAGE] = value + self.save() + + @property + def devices(self): + """Return extend device mapping.""" + return self._data[ATTR_DEVICES] + + @devices.setter + def devices(self, value): + """Set extend device mapping.""" + self._data[ATTR_DEVICES] = value + self.save() diff --git a/hassio/tools.py b/hassio/tools.py index 7147fe62d83..975cfc3032b 100644 --- a/hassio/tools.py +++ b/hassio/tools.py @@ -9,6 +9,7 @@ import async_timeout import pytz import voluptuous as vol +from voluptuous.humanize import humanize_error from .const import URL_HASSIO_VERSION, URL_HASSIO_VERSION_BETA @@ -98,3 +99,44 @@ async def fetch_timezone(websession): data = await request.json() return data.get('time_zone', 'UTC') + + +class JsonConfig(object): + """Hass core object for handle it.""" + + def __init__(self, json_file, schema): + """Initialize hass object.""" + self._file = json_file + self._schema = schema + self._data = {} + + # init or load data + if self._file.is_file(): + try: + self._data = read_json_file(self._file) + except (OSError, json.JSONDecodeError): + _LOGGER.warning("Can't read %s", self._file) + self._data = {} + + # validate + try: + self._data = self._schema(self._data) + except vol.Invalid as ex: + _LOGGER.error("Can't parse %s -> %s", + self._file, humanize_error(self._data, ex)) + + def save(self): + """Store data to config file.""" + # validate + try: + self._data = self._schema(self._data) + except vol.Invalid as ex: + _LOGGER.error("Can't parse data -> %s", + humanize_error(self._data, ex)) + return False + + # write + if not write_json_file(self._file, self._data): + _LOGGER.error("Can't store config in %s", self._file) + return False + return True diff --git a/hassio/validate.py b/hassio/validate.py index 4a97a54b7ba..f339a934847 100644 --- a/hassio/validate.py +++ b/hassio/validate.py @@ -1,6 +1,9 @@ """Validate functions.""" import voluptuous as vol +from .const import ATTR_DEVICES, ATTR_IMAGE + + NETWORK_PORT = vol.All(vol.Coerce(int), vol.Range(min=1, max=65535)) HASS_DEVICES = [vol.Match(r"^[^/]*$")] @@ -30,3 +33,9 @@ def convert_to_docker_ports(data): vol.All(vol.Coerce(str), vol.Match(r"^\d+(?:/tcp|/udp)?$")): convert_to_docker_ports, }) + + +SCHEMA_HASS_CONFIG = vol.Schema({ + vol.Optional(ATTR_DEVICES, default=[]): HASS_DEVICES, + vol.Optional(ATTR_IMAGE): vol.Coerce(str) +}) From 906c4e03fb9fecba8d229c1605cfbac4f024fc71 Mon Sep 17 00:00:00 2001 From: pvizeli Date: Tue, 11 Jul 2017 16:19:24 +0200 Subject: [PATCH 03/18] Finish new homeassistant handling --- API.md | 8 +++- hassio/api/homeassistant.py | 18 ++++++-- hassio/const.py | 1 + hassio/core.py | 22 +++++----- hassio/homeassistant.py | 84 +++++++++++++++++++++++++++++++----- hassio/snapshots/__init__.py | 8 ++-- hassio/tasks.py | 17 -------- hassio/validate.py | 5 ++- 8 files changed, 113 insertions(+), 50 deletions(-) diff --git a/API.md b/API.md index bb38ffe0fe9..1951dc0f63b 100644 --- a/API.md +++ b/API.md @@ -269,7 +269,9 @@ Optional: { "version": "INSTALL_VERSION", "last_version": "LAST_VERSION", - "devices": [] + "devices": [""], + "image": "str", + "custom": "bool -> if custom image" } ``` @@ -291,9 +293,13 @@ Output the raw docker log ```json { "devices": [], + "image": "Optional|null", + "last_version": "Optional for custom image|null" } ``` +Image with `null` and last_version with `null` reset this options. + ### REST API addons - POST `/addons/reload` diff --git a/hassio/api/homeassistant.py b/hassio/api/homeassistant.py index 6ba15511a60..cc791b4ce14 100644 --- a/hassio/api/homeassistant.py +++ b/hassio/api/homeassistant.py @@ -5,7 +5,8 @@ import voluptuous as vol from .util import api_process, api_process_raw, api_validate -from ..const import ATTR_VERSION, ATTR_LAST_VERSION, ATTR_DEVICES +from ..const import ( + ATTR_VERSION, ATTR_LAST_VERSION, ATTR_DEVICES, ATTR_IMAGE, ATTR_CUSTOM) from ..validate import HASS_DEVICES _LOGGER = logging.getLogger(__name__) @@ -13,6 +14,9 @@ SCHEMA_OPTIONS = vol.Schema({ vol.Optional(ATTR_DEVICES): HASS_DEVICES, + vol.Inclusive(ATTR_IMAGE, 'custom_hass'): vol.Any(None, vol.Coerce(str)), + vol.Inclusive(ATTR_LAST_VERSION, 'custom_hass'): + vol.Any(None, vol.Coerce(str)), }) SCHEMA_VERSION = vol.Schema({ @@ -34,8 +38,10 @@ async def info(self, request): """Return host information.""" return { ATTR_VERSION: self.homeassistant.version, - ATTR_LAST_VERSION: self.config.last_homeassistant, - ATTR_DEVICES: self.config.homeassistant_devices, + ATTR_LAST_VERSION: self.homeassistant.last_version, + ATTR_IMAGE: self.homeassistant.image, + ATTR_DEVICES: self.homeassistant.devices, + ATTR_CUSTOM: self.homeassistant.is_custom_image, } @api_process @@ -44,7 +50,11 @@ async def options(self, request): body = await api_validate(SCHEMA_OPTIONS, request) if ATTR_DEVICES in body: - self.config.homeassistant_devices = body[ATTR_DEVICES] + self.homeassistant.devices = body[ATTR_DEVICES] + + if ATTR_IMAGE in body: + self.homeassistant.set_custom( + body[ATTR_IMAGE], body[ATTR_LAST_VERSION]) return True diff --git a/hassio/const.py b/hassio/const.py index c8ee16ad9de..88ca1be6b75 100644 --- a/hassio/const.py +++ b/hassio/const.py @@ -95,6 +95,7 @@ ATTR_TYPE = 'type' ATTR_TIMEOUT = 'timeout' ATTR_AUTO_UPDATE = 'auto_update' +ATTR_CUSTOM = 'custom' STARTUP_INITIALIZE = 'initialize' STARTUP_BEFORE = 'before' diff --git a/hassio/core.py b/hassio/core.py index eaba036e93b..6a74386ccef 100644 --- a/hassio/core.py +++ b/hassio/core.py @@ -13,13 +13,12 @@ RUN_UPDATE_SUPERVISOR_TASKS, RUN_WATCHDOG_HOMEASSISTANT, RUN_CLEANUP_API_SESSIONS, STARTUP_AFTER, STARTUP_BEFORE, STARTUP_INITIALIZE, RUN_RELOAD_SNAPSHOTS_TASKS, RUN_UPDATE_ADDONS_TASKS) +from .homeassistant import HomeAssistant from .scheduler import Scheduler -from .dock.homeassistant import DockerHomeAssistant from .dock.supervisor import DockerSupervisor from .snapshots import SnapshotsManager from .tasks import ( - hassio_update, homeassistant_watchdog, homeassistant_setup, - api_sessions_cleanup, addons_update) + hassio_update, homeassistant_watchdog, api_sessions_cleanup, addons_update) from .tools import get_local_ip, fetch_timezone _LOGGER = logging.getLogger(__name__) @@ -41,7 +40,9 @@ def __init__(self, loop, config): # init basic docker container self.supervisor = DockerSupervisor(config, loop, self.dock, self.stop) - self.homeassistant = DockerHomeAssistant(config, loop, self.dock) + + # init homeassistant + self.homeassistant = HomeAssistant(config, loop, self.dock) # init HostControl self.host_control = HostControl(loop) @@ -94,13 +95,8 @@ async def setup(self): api_sessions_cleanup(self.config), RUN_CLEANUP_API_SESSIONS, now=True) - # first start of supervisor? - if not await self.homeassistant.exists(): - _LOGGER.info("No HomeAssistant docker found.") - await homeassistant_setup( - self.config, self.loop, self.homeassistant, self.websession) - else: - await self.homeassistant.attach() + # Load homeassistant + await self.homeassistant.prepare(): # Load addons await self.addons.prepare() @@ -132,6 +128,10 @@ async def start(self): loop=self.loop ) + # If laningpage / run upgrade in background + if self.homeassistant.version == 'landingpage': + self.loop.create_task(self.homeassistant.install()) + # start api await self.api.start() _LOGGER.info("Start hassio api on %s", self.config.api_endpoint) diff --git a/hassio/homeassistant.py b/hassio/homeassistant.py index 82525e5f53f..e7c6721e365 100644 --- a/hassio/homeassistant.py +++ b/hassio/homeassistant.py @@ -1,8 +1,10 @@ """HomeAssistant control object.""" +import asyncio import logging import os -from .const import FILE_HASSIO_HOMEASSISTANT, ATTR_DEVICES, ATTR_IMAGE +from .const import ( + FILE_HASSIO_HOMEASSISTANT, ATTR_DEVICES, ATTR_IMAGE, ATTR_LAST_VERSION) from .dock.homeassistant import DockerHomeAssistant from .tools import JsonConfig from .validate import SCHEMA_HASS_CONFIG @@ -11,14 +13,24 @@ class HomeAssistant(JsonConfig): """Hass core object for handle it.""" - def __init__(self, config, loop, dock): + def __init__(self, config, loop, dock, websession): """Initialize hass object.""" super().__init__(FILE_HASSIO_HOMEASSISTANT, SCHEMA_HASS_CONFIG) self.config = config self.loop = loop + self.websession = websession + self.docker = DockerHomeAssistant(config, loop, dock, self) async def prepare(self): """Prepare HomeAssistant object.""" + if not await self.docker.exists(): + _LOGGER.info("No HomeAssistant docker %s found.", self.image) + if self.is_custom_image: + await self.install() + else: + await self.install_landingpage() + else: + await self.docker.attach() @property def version(self): @@ -28,6 +40,8 @@ def version(self): @property def last_version(self): """Return last available version of homeassistant.""" + if self.is_custom_image: + return self._data.get(ATTR_LAST_VERSION) return self.config.last_homeassistant @property @@ -37,14 +51,10 @@ def image(self): return self._data[ATTR_IMAGE] return os.environ['HOMEASSISTANT_REPOSITORY'] - @image.setter - def image(self, value): - """Set image name of hass containter.""" - if value is None: - self._data.pop(ATTR_IMAGE, None) - else: - self._data[ATTR_IMAGE] = value - self.save() + @property + def is_custom_image(self): + """Return True if a custom image is used.""" + return ATTR_IMAGE in self._data @property def devices(self): @@ -56,3 +66,57 @@ def devices(self, value): """Set extend device mapping.""" self._data[ATTR_DEVICES] = value self.save() + + def set_custom(self, image, version): + """Set a custom image for homeassistant.""" + # reset + if image is None and version is None: + self._data.pop(ATTR_IMAGE, None) + self._data.pop(ATTR_VERSION, None) + else: + if image: + self._data[ATTR_IMAGE] = image + if version: + self._data[ATTR_VERSION] = version + self.save() + + async def install_landingpage(self): + """Install a landingpage.""" + _LOGGER.info("Setup HomeAssistant landingpage") + while True: + if self.docker.install('landingpage'): + break + _LOGGER.warning("Fails install landingpage, retry after 60sec") + await asyncio.sleep(60, loop=self.loop) + + async def install(self): + """Install a landingpage.""" + _LOGGER.info("Setup HomeAssistant") + while True: + # read homeassistant tag and install it + if not self.last_version: + await self.config.fetch_update_infos(websession) + + tag = self.last_version + if tag and await self.docker.install(tag): + break + _LOGGER.warning("Error on install HomeAssistant. Retry in 60sec") + await asyncio.sleep(60, loop=loop) + + # store version + _LOGGER.info("HomeAssistant docker now installed") + + async def update(self, version=None): + """Update HomeAssistant version.""" + version = version or self.last_version + if version == self.version: + return True + + return self.docker.update(version) + + def run(self): + """Run HomeAssistant docker. + + Return a coroutine. + """ + return self.docker.run() diff --git a/hassio/snapshots/__init__.py b/hassio/snapshots/__init__.py index 29f3b277a54..8d2bd3f9140 100644 --- a/hassio/snapshots/__init__.py +++ b/hassio/snapshots/__init__.py @@ -47,7 +47,7 @@ def _create_snapshot(self, name, sys_type): # set general data snapshot.homeassistant_version = self.homeassistant.version - snapshot.homeassistant_devices = self.config.homeassistant_devices + snapshot.homeassistant_devices = self.homeassistant.devices snapshot.repositories = self.config.addons_repositories return snapshot @@ -198,8 +198,7 @@ async def do_restore_full(self, snapshot): await snapshot.restore_folders() # start homeassistant restore - self.config.homeassistant_devices = \ - snapshot.homeassistant_devices + self.homeassistant.devices = snapshot.homeassistant_devices task_hass = self.loop.create_task( self.homeassistant.update(snapshot.homeassistant_version)) @@ -281,8 +280,7 @@ async def do_restore_partial(self, snapshot, homeassistant=False, await snapshot.restore_folders(folders) if homeassistant: - self.config.homeassistant_devices = \ - snapshot.homeassistant_devices + self.homeassistant.devices = snapshot.homeassistant_devices tasks.append(self.homeassistant.update( snapshot.homeassistant_version)) diff --git a/hassio/tasks.py b/hassio/tasks.py index 25409e16ed4..a3421b5608c 100644 --- a/hassio/tasks.py +++ b/hassio/tasks.py @@ -66,20 +66,3 @@ async def _homeassistant_watchdog(): loop.create_task(homeassistant.run()) return _homeassistant_watchdog - - -async def homeassistant_setup(config, loop, homeassistant, websession): - """Install a homeassistant docker container.""" - while True: - # read homeassistant tag and install it - if not config.last_homeassistant: - await config.fetch_update_infos(websession) - - tag = config.last_homeassistant - if tag and await homeassistant.install(tag): - break - _LOGGER.warning("Error on setup HomeAssistant. Retry in 60.") - await asyncio.sleep(60, loop=loop) - - # store version - _LOGGER.info("HomeAssistant docker now installed.") diff --git a/hassio/validate.py b/hassio/validate.py index f339a934847..88c2cf2ca3b 100644 --- a/hassio/validate.py +++ b/hassio/validate.py @@ -1,7 +1,7 @@ """Validate functions.""" import voluptuous as vol -from .const import ATTR_DEVICES, ATTR_IMAGE +from .const import ATTR_DEVICES, ATTR_IMAGE, ATTR_LAST_VERSION NETWORK_PORT = vol.All(vol.Coerce(int), vol.Range(min=1, max=65535)) @@ -37,5 +37,6 @@ def convert_to_docker_ports(data): SCHEMA_HASS_CONFIG = vol.Schema({ vol.Optional(ATTR_DEVICES, default=[]): HASS_DEVICES, - vol.Optional(ATTR_IMAGE): vol.Coerce(str) + vol.inclusive(ATTR_IMAGE, 'custom_hass'): vol.Coerce(str), + vol.inclusive(ATTR_LAST_VERSION, 'custom_hass'): vol.Coerce(str), }) From e5fc6846e0ab8e8a152d47d053f7cdd847631e20 Mon Sep 17 00:00:00 2001 From: pvizeli Date: Tue, 11 Jul 2017 16:52:16 +0200 Subject: [PATCH 04/18] fix snapshot with custom image --- hassio/homeassistant.py | 3 +++ hassio/snapshots/__init__.py | 7 +++---- hassio/snapshots/snapshot.py | 31 ++++++++++++++++++++++++++++++- hassio/snapshots/validate.py | 3 ++- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/hassio/homeassistant.py b/hassio/homeassistant.py index e7c6721e365..c062a6be8d5 100644 --- a/hassio/homeassistant.py +++ b/hassio/homeassistant.py @@ -73,9 +73,12 @@ def set_custom(self, image, version): if image is None and version is None: self._data.pop(ATTR_IMAGE, None) self._data.pop(ATTR_VERSION, None) + + self.docker.image = self.image else: if image: self._data[ATTR_IMAGE] = image + self.docker.image = image if version: self._data[ATTR_VERSION] = version self.save() diff --git a/hassio/snapshots/__init__.py b/hassio/snapshots/__init__.py index 8d2bd3f9140..bc8603a9b75 100644 --- a/hassio/snapshots/__init__.py +++ b/hassio/snapshots/__init__.py @@ -46,8 +46,7 @@ def _create_snapshot(self, name, sys_type): snapshot.create(slug, name, date_str, sys_type) # set general data - snapshot.homeassistant_version = self.homeassistant.version - snapshot.homeassistant_devices = self.homeassistant.devices + snapshot.snapshot_homeassistant(self.homeassistant) snapshot.repositories = self.config.addons_repositories return snapshot @@ -198,7 +197,7 @@ async def do_restore_full(self, snapshot): await snapshot.restore_folders() # start homeassistant restore - self.homeassistant.devices = snapshot.homeassistant_devices + snapshot.restore_homeassistant(self.homeassistant) task_hass = self.loop.create_task( self.homeassistant.update(snapshot.homeassistant_version)) @@ -280,7 +279,7 @@ async def do_restore_partial(self, snapshot, homeassistant=False, await snapshot.restore_folders(folders) if homeassistant: - self.homeassistant.devices = snapshot.homeassistant_devices + snapshot.restore_homeassistant(self.homeassistant) tasks.append(self.homeassistant.update( snapshot.homeassistant_version)) diff --git a/hassio/snapshots/snapshot.py b/hassio/snapshots/snapshot.py index 1882a5f0747..a8730e20a84 100644 --- a/hassio/snapshots/snapshot.py +++ b/hassio/snapshots/snapshot.py @@ -13,7 +13,8 @@ from .util import remove_folder from ..const import ( ATTR_SLUG, ATTR_NAME, ATTR_DATE, ATTR_ADDONS, ATTR_REPOSITORIES, - ATTR_HOMEASSISTANT, ATTR_FOLDERS, ATTR_VERSION, ATTR_TYPE, ATTR_DEVICES) + ATTR_HOMEASSISTANT, ATTR_FOLDERS, ATTR_VERSION, ATTR_TYPE, ATTR_DEVICES, + ATTR_IMAGE) from ..tools import write_json_file _LOGGER = logging.getLogger(__name__) @@ -90,6 +91,16 @@ def homeassistant_devices(self, value): """Set snapshot homeassistant devices.""" self._data[ATTR_HOMEASSISTANT][ATTR_DEVICES] = value + @property + def homeassistant_image(self): + """Return snapshot homeassistant custom image.""" + return self._data[ATTR_HOMEASSISTANT].get(ATTR_IMAGE) + + @homeassistant_image.setter + def homeassistant_image(self, value): + """Set snapshot homeassistant custom image.""" + self._data[ATTR_HOMEASSISTANT][ATTR_IMAGE] = value + @property def size(self): """Return snapshot size.""" @@ -111,6 +122,24 @@ def create(self, slug, name, date, sys_type): self._data[ATTR_REPOSITORIES] = [] self._data[ATTR_FOLDERS] = [] + def snapshot_homeassistant(self, homeassistant): + """Read all data from homeassistant object.""" + self.homeassistant_version = homeassistant.version + self.homeassistant_devices = homeassistant.devices + + # custom image + if self.homeassistant.is_custom_image: + self.homeassistant_image = homeassistant.image + + def restore_homeassistant(self, homeassistant): + """Write all data to homeassistant object.""" + homeassistant.devices = self.homeassistant_devices + + # custom image + if self.homeassistant_image: + homeassistant.set_custom( + self.homeassistant_image, self.homeassistant_version) + async def load(self): """Read snapshot.json from tar file.""" if not self.tar_file.is_file(): diff --git a/hassio/snapshots/validate.py b/hassio/snapshots/validate.py index 2259009d508..e0a497d76fb 100644 --- a/hassio/snapshots/validate.py +++ b/hassio/snapshots/validate.py @@ -5,7 +5,7 @@ from ..const import ( ATTR_REPOSITORIES, ATTR_ADDONS, ATTR_NAME, ATTR_SLUG, ATTR_DATE, ATTR_VERSION, ATTR_HOMEASSISTANT, ATTR_FOLDERS, ATTR_TYPE, ATTR_DEVICES, - FOLDER_SHARE, FOLDER_HOMEASSISTANT, FOLDER_ADDONS, FOLDER_SSL, + ATTR_IMAGE, FOLDER_SHARE, FOLDER_HOMEASSISTANT, FOLDER_ADDONS, FOLDER_SSL, SNAPSHOT_FULL, SNAPSHOT_PARTIAL) from ..validate import HASS_DEVICES @@ -20,6 +20,7 @@ vol.Required(ATTR_HOMEASSISTANT): vol.Schema({ vol.Required(ATTR_VERSION): vol.Coerce(str), vol.Optional(ATTR_DEVICES, default=[]): HASS_DEVICES, + vol.Optional(ATTR_IMAGE): vol.Coerce(str), }), vol.Optional(ATTR_FOLDERS, default=[]): [vol.In(ALL_FOLDERS)], vol.Optional(ATTR_ADDONS, default=[]): [vol.Schema({ From 5ebf2068b278760622cb3e02212af5647f11b05f Mon Sep 17 00:00:00 2001 From: pvizeli Date: Tue, 11 Jul 2017 16:57:03 +0200 Subject: [PATCH 05/18] Update interface, allow update every time --- hassio/api/homeassistant.py | 3 --- hassio/homeassistant.py | 31 ++++++++++++++++++++++++++----- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/hassio/api/homeassistant.py b/hassio/api/homeassistant.py index cc791b4ce14..9b10497ca1b 100644 --- a/hassio/api/homeassistant.py +++ b/hassio/api/homeassistant.py @@ -67,9 +67,6 @@ async def update(self, request): if self.homeassistant.in_progress: raise RuntimeError("Other task is in progress") - if version == self.homeassistant.version: - raise RuntimeError("Version is already in use") - return await asyncio.shield( self.homeassistant.update(version), loop=self.loop) diff --git a/hassio/homeassistant.py b/hassio/homeassistant.py index c062a6be8d5..63e27795ace 100644 --- a/hassio/homeassistant.py +++ b/hassio/homeassistant.py @@ -109,12 +109,12 @@ async def install(self): # store version _LOGGER.info("HomeAssistant docker now installed") - async def update(self, version=None): - """Update HomeAssistant version.""" - version = version or self.last_version - if version == self.version: - return True + def update(self, version=None): + """Update HomeAssistant version. + Return a coroutine. + """ + version = version or self.last_version return self.docker.update(version) def run(self): @@ -123,3 +123,24 @@ def run(self): Return a coroutine. """ return self.docker.run() + + def stop(self): + """Stop HomeAssistant docker. + + Return a coroutine. + """ + return self.docker.stop() + + def restart(self): + """Restart HomeAssistant docker. + + Return a coroutine. + """ + return self.docker.restart() + + def logs(self): + """Get HomeAssistant docker logs. + + Return a coroutine. + """ + return self.docker.logs() From 421b380043c4b7f069dc800a65124d53870cc66a Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 11 Jul 2017 22:11:25 +0200 Subject: [PATCH 06/18] fix lint --- hassio/core.py | 2 +- hassio/dock/__init__.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/hassio/core.py b/hassio/core.py index 6a74386ccef..f21e9daf050 100644 --- a/hassio/core.py +++ b/hassio/core.py @@ -96,7 +96,7 @@ async def setup(self): now=True) # Load homeassistant - await self.homeassistant.prepare(): + await self.homeassistant.prepare() # Load addons await self.addons.prepare() diff --git a/hassio/dock/__init__.py b/hassio/dock/__init__.py index 733bbce67b1..7fb7adb4ddf 100644 --- a/hassio/dock/__init__.py +++ b/hassio/dock/__init__.py @@ -260,9 +260,13 @@ def _update(self, tag): if not self._install(tag): return False - # cleanup old stuff + # run or cleanup container if was_running: self._run() + else: + self._stop() + + # cleanup images self._cleanup() return True From 40e8f411ff9d5bc5ab36a0d8abc09f55327ff2d0 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 11 Jul 2017 22:19:22 +0200 Subject: [PATCH 07/18] fix lint p2 --- hassio/api/addons.py | 1 + hassio/config.py | 2 -- hassio/core.py | 3 ++- hassio/homeassistant.py | 5 ++++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/hassio/api/addons.py b/hassio/api/addons.py index 1f9e9666301..b03e582a43d 100644 --- a/hassio/api/addons.py +++ b/hassio/api/addons.py @@ -11,6 +11,7 @@ ATTR_URL, ATTR_DESCRIPTON, ATTR_DETACHED, ATTR_NAME, ATTR_REPOSITORY, ATTR_BUILD, ATTR_AUTO_UPDATE, ATTR_NETWORK, ATTR_HOST_NETWORK, BOOT_AUTO, BOOT_MANUAL) +from ..tools import read_json_file from ..validate import DOCKER_PORTS _LOGGER = logging.getLogger(__name__) diff --git a/hassio/config.py b/hassio/config.py index bc57e9865be..b8a2630fb12 100644 --- a/hassio/config.py +++ b/hassio/config.py @@ -1,7 +1,6 @@ """Bootstrap HassIO.""" from datetime import datetime import logging -import json import os from pathlib import Path, PurePath @@ -9,7 +8,6 @@ from .const import FILE_HASSIO_CONFIG, HASSIO_DATA from .tools import fetch_last_versions, JsonConfig, validate_timezone -from .validate import HASS_DEVICES _LOGGER = logging.getLogger(__name__) diff --git a/hassio/core.py b/hassio/core.py index f21e9daf050..cb39d9f040f 100644 --- a/hassio/core.py +++ b/hassio/core.py @@ -42,7 +42,8 @@ def __init__(self, loop, config): self.supervisor = DockerSupervisor(config, loop, self.dock, self.stop) # init homeassistant - self.homeassistant = HomeAssistant(config, loop, self.dock) + self.homeassistant = HomeAssistant( + config, loop, self.dock, self.websession) # init HostControl self.host_control = HostControl(loop) diff --git a/hassio/homeassistant.py b/hassio/homeassistant.py index 63e27795ace..6f4464459b3 100644 --- a/hassio/homeassistant.py +++ b/hassio/homeassistant.py @@ -4,11 +4,14 @@ import os from .const import ( - FILE_HASSIO_HOMEASSISTANT, ATTR_DEVICES, ATTR_IMAGE, ATTR_LAST_VERSION) + FILE_HASSIO_HOMEASSISTANT, ATTR_DEVICES, ATTR_IMAGE, ATTR_LAST_VERSION, + ATTR_VERSION) from .dock.homeassistant import DockerHomeAssistant from .tools import JsonConfig from .validate import SCHEMA_HASS_CONFIG +_LOGGER = logging.getLogger(__name__) + class HomeAssistant(JsonConfig): """Hass core object for handle it.""" From ebf4daf4cce96e69d7629b9d251f3ce232fd8ace Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 11 Jul 2017 22:38:07 +0200 Subject: [PATCH 08/18] fix lint --- hassio/addons/data.py | 2 +- hassio/api/addons.py | 1 - hassio/homeassistant.py | 11 +++++++++-- hassio/snapshots/snapshot.py | 2 +- hassio/validate.py | 4 ++-- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/hassio/addons/data.py b/hassio/addons/data.py index 51c652cb1eb..58d7452d194 100644 --- a/hassio/addons/data.py +++ b/hassio/addons/data.py @@ -15,7 +15,7 @@ from ..const import ( FILE_HASSIO_ADDONS, ATTR_VERSION, ATTR_SLUG, ATTR_REPOSITORY, ATTR_LOCATON, REPOSITORY_CORE, REPOSITORY_LOCAL, ATTR_USER, ATTR_SYSTEM) -from ..tools import JsonConfig +from ..tools import JsonConfig, read_json_file _LOGGER = logging.getLogger(__name__) diff --git a/hassio/api/addons.py b/hassio/api/addons.py index b03e582a43d..1f9e9666301 100644 --- a/hassio/api/addons.py +++ b/hassio/api/addons.py @@ -11,7 +11,6 @@ ATTR_URL, ATTR_DESCRIPTON, ATTR_DETACHED, ATTR_NAME, ATTR_REPOSITORY, ATTR_BUILD, ATTR_AUTO_UPDATE, ATTR_NETWORK, ATTR_HOST_NETWORK, BOOT_AUTO, BOOT_MANUAL) -from ..tools import read_json_file from ..validate import DOCKER_PORTS _LOGGER = logging.getLogger(__name__) diff --git a/hassio/homeassistant.py b/hassio/homeassistant.py index 6f4464459b3..627856f1995 100644 --- a/hassio/homeassistant.py +++ b/hassio/homeassistant.py @@ -101,13 +101,13 @@ async def install(self): while True: # read homeassistant tag and install it if not self.last_version: - await self.config.fetch_update_infos(websession) + await self.config.fetch_update_infos(self.websession) tag = self.last_version if tag and await self.docker.install(tag): break _LOGGER.warning("Error on install HomeAssistant. Retry in 60sec") - await asyncio.sleep(60, loop=loop) + await asyncio.sleep(60, loop=self.loop) # store version _LOGGER.info("HomeAssistant docker now installed") @@ -147,3 +147,10 @@ def logs(self): Return a coroutine. """ return self.docker.logs() + + def is_running(self): + """Return True if docker container is running. + + Return a coroutine. + """ + return self.docker.is_running() diff --git a/hassio/snapshots/snapshot.py b/hassio/snapshots/snapshot.py index a8730e20a84..41a03a8f311 100644 --- a/hassio/snapshots/snapshot.py +++ b/hassio/snapshots/snapshot.py @@ -128,7 +128,7 @@ def snapshot_homeassistant(self, homeassistant): self.homeassistant_devices = homeassistant.devices # custom image - if self.homeassistant.is_custom_image: + if homeassistant.is_custom_image: self.homeassistant_image = homeassistant.image def restore_homeassistant(self, homeassistant): diff --git a/hassio/validate.py b/hassio/validate.py index 88c2cf2ca3b..ae0cb5a6061 100644 --- a/hassio/validate.py +++ b/hassio/validate.py @@ -37,6 +37,6 @@ def convert_to_docker_ports(data): SCHEMA_HASS_CONFIG = vol.Schema({ vol.Optional(ATTR_DEVICES, default=[]): HASS_DEVICES, - vol.inclusive(ATTR_IMAGE, 'custom_hass'): vol.Coerce(str), - vol.inclusive(ATTR_LAST_VERSION, 'custom_hass'): vol.Coerce(str), + vol.Inclusive(ATTR_IMAGE, 'custom_hass'): vol.Coerce(str), + vol.Inclusive(ATTR_LAST_VERSION, 'custom_hass'): vol.Coerce(str), }) From cf154b57f34faa60738f7ba9f64e3703da5fafa7 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 11 Jul 2017 22:45:09 +0200 Subject: [PATCH 09/18] fix lint p4 --- hassio/addons/data.py | 1 + 1 file changed, 1 insertion(+) diff --git a/hassio/addons/data.py b/hassio/addons/data.py index 58d7452d194..7d8e00e79bc 100644 --- a/hassio/addons/data.py +++ b/hassio/addons/data.py @@ -30,6 +30,7 @@ def __init__(self, config): super().__init__(FILE_HASSIO_ADDONS, SCHEMA_ADDON_FILE) self.config = config self._repositories = {} + self._cache = {} @property def user(self): From 55ec1a84faad200dba9881659b26d2400a2371fc Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 11 Jul 2017 22:58:05 +0200 Subject: [PATCH 10/18] expose running state of docker --- hassio/homeassistant.py | 1 + 1 file changed, 1 insertion(+) diff --git a/hassio/homeassistant.py b/hassio/homeassistant.py index 627856f1995..3ee5090ae58 100644 --- a/hassio/homeassistant.py +++ b/hassio/homeassistant.py @@ -148,6 +148,7 @@ def logs(self): """ return self.docker.logs() + @property def is_running(self): """Return True if docker container is running. From e40963a6869baf072b22821606a64f2404e5a551 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 11 Jul 2017 23:02:20 +0200 Subject: [PATCH 11/18] fix calling --- hassio/homeassistant.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hassio/homeassistant.py b/hassio/homeassistant.py index 3ee5090ae58..5ed90845476 100644 --- a/hassio/homeassistant.py +++ b/hassio/homeassistant.py @@ -154,4 +154,4 @@ def is_running(self): Return a coroutine. """ - return self.docker.is_running() + return self.docker.is_running From d978ec00aa72653e78ecc3d13bb7bb6a5aa69cb2 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 11 Jul 2017 23:08:47 +0200 Subject: [PATCH 12/18] fix progress v2 --- hassio/homeassistant.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hassio/homeassistant.py b/hassio/homeassistant.py index 5ed90845476..0e00542bb23 100644 --- a/hassio/homeassistant.py +++ b/hassio/homeassistant.py @@ -148,10 +148,14 @@ def logs(self): """ return self.docker.logs() - @property def is_running(self): """Return True if docker container is running. Return a coroutine. """ - return self.docker.is_running + return self.docker.is_running() + + @property + def in_progress(self): + """Return True if a task is in progress.""" + return self.docker.in_progress From a0a1fd4875077c67bb5d36c199b744dbf363e580 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 11 Jul 2017 23:29:38 +0200 Subject: [PATCH 13/18] fix coro --- hassio/homeassistant.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hassio/homeassistant.py b/hassio/homeassistant.py index 0e00542bb23..9c94c41842c 100644 --- a/hassio/homeassistant.py +++ b/hassio/homeassistant.py @@ -90,7 +90,7 @@ async def install_landingpage(self): """Install a landingpage.""" _LOGGER.info("Setup HomeAssistant landingpage") while True: - if self.docker.install('landingpage'): + if await self.docker.install('landingpage'): break _LOGGER.warning("Fails install landingpage, retry after 60sec") await asyncio.sleep(60, loop=self.loop) From dbcd09024460146736774824520fb4e72899d861 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 11 Jul 2017 23:45:29 +0200 Subject: [PATCH 14/18] fix flow --- hassio/core.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hassio/core.py b/hassio/core.py index cb39d9f040f..0544b7d7617 100644 --- a/hassio/core.py +++ b/hassio/core.py @@ -129,10 +129,6 @@ async def start(self): loop=self.loop ) - # If laningpage / run upgrade in background - if self.homeassistant.version == 'landingpage': - self.loop.create_task(self.homeassistant.install()) - # start api await self.api.start() _LOGGER.info("Start hassio api on %s", self.config.api_endpoint) @@ -158,6 +154,10 @@ async def start(self): homeassistant_watchdog(self.loop, self.homeassistant), RUN_WATCHDOG_HOMEASSISTANT) + # If landingpage / run upgrade in background + if self.homeassistant.version == 'landingpage': + self.loop.create_task(self.homeassistant.install()) + async def stop(self, exit_code=0): """Stop a running orchestration.""" # don't process scheduler anymore From 2bbe7e7dc153cc07aff61369e3401a84cbe0caab Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 12 Jul 2017 00:09:03 +0200 Subject: [PATCH 15/18] cleanup after install --- hassio/homeassistant.py | 1 + 1 file changed, 1 insertion(+) diff --git a/hassio/homeassistant.py b/hassio/homeassistant.py index 9c94c41842c..70bcf05c6fe 100644 --- a/hassio/homeassistant.py +++ b/hassio/homeassistant.py @@ -111,6 +111,7 @@ async def install(self): # store version _LOGGER.info("HomeAssistant docker now installed") + await self.docker.cleanup() def update(self, version=None): """Update HomeAssistant version. From 772709dd7546590cda222eb88dcba9ad3a56434a Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 12 Jul 2017 01:38:15 +0200 Subject: [PATCH 16/18] Update API / fix isoformat --- API.md | 84 ++++++++++++++++++++---------------- hassio/api/__init__.py | 6 +++ hassio/api/addons.py | 41 +++++++++++++++++- hassio/api/snapshots.py | 40 +++++++++++------ hassio/api/supervisor.py | 14 +----- hassio/snapshots/__init__.py | 2 +- 6 files changed, 124 insertions(+), 63 deletions(-) diff --git a/API.md b/API.md index 1951dc0f63b..8d15758c809 100644 --- a/API.md +++ b/API.md @@ -51,48 +51,13 @@ The addons from `addons` are only installed one. ], "addons_repositories": [ "REPO_URL" - ], - "snapshots": [ - { - "slug": "SLUG", - "data": "ISO", - "name": "Custom name" - } ] } ``` - GET `/supervisor/addons` -Get all available addons - -```json -{ - "addons": [ - { - "name": "xy bla", - "slug": "xy", - "description": "description", - "arch": ["armhf", "aarch64", "i386", "amd64"], - "repository": "core|local|REP_ID", - "version": "LAST_VERSION", - "installed": "none|INSTALL_VERSION", - "detached": "bool", - "build": "bool", - "url": "null|url" - } - ], - "repositories": [ - { - "slug": "12345678", - "name": "Repitory Name|unknown", - "source": "URL_OF_REPOSITORY", - "url": "WEBSITE|REPOSITORY", - "maintainer": "BLA BLU |unknown" - } - ] -} -``` +Get all available addons. Will be delete soon. Look to `/addons` - POST `/supervisor/update` Optional: @@ -157,6 +122,21 @@ Return QR-Code ### Backup/Snapshot +- GET `/snapshots` +```json +{ + "snapshots": [ + { + "slug": "SLUG", + "date": "ISO", + "name": "Custom name" + } + ] +} +``` + +- POST `/snapshots/reload` + - POST `/snapshots/new/full` ```json { @@ -302,6 +282,38 @@ Image with `null` and last_version with `null` reset this options. ### REST API addons +- GET `/addons` + +Get all available addons + +```json +{ + "addons": [ + { + "name": "xy bla", + "slug": "xy", + "description": "description", + "arch": ["armhf", "aarch64", "i386", "amd64"], + "repository": "core|local|REP_ID", + "version": "LAST_VERSION", + "installed": "none|INSTALL_VERSION", + "detached": "bool", + "build": "bool", + "url": "null|url" + } + ], + "repositories": [ + { + "slug": "12345678", + "name": "Repitory Name|unknown", + "source": "URL_OF_REPOSITORY", + "url": "WEBSITE|REPOSITORY", + "maintainer": "BLA BLU |unknown" + } + ] +} +``` + - POST `/addons/reload` - GET `/addons/{addon}/info` diff --git a/hassio/api/__init__.py b/hassio/api/__init__.py index 3d3e954652d..1fbcbc40ae4 100644 --- a/hassio/api/__init__.py +++ b/hassio/api/__init__.py @@ -77,6 +77,9 @@ def register_addons(self, addons): """Register homeassistant function.""" api_addons = APIAddons(self.config, self.loop, addons) + self.webapp.router.add_get('/addons', api_addons.list) + self.webapp.router.add_post('/addons/reload', api_addons.reload) + self.webapp.router.add_get('/addons/{addon}/info', api_addons.info) self.webapp.router.add_post( '/addons/{addon}/install', api_addons.install) @@ -105,6 +108,9 @@ def register_snapshots(self, snapshots): """Register snapshots function.""" api_snapshots = APISnapshots(self.config, self.loop, snapshots) + self.webapp.router.add_get('/snapshots', api_snapshots.list) + self.webapp.router.add_post('/snapshots/reload', api_snapshots.reload) + self.webapp.router.add_post( '/snapshots/new/full', api_snapshots.snapshot_full) self.webapp.router.add_post( diff --git a/hassio/api/addons.py b/hassio/api/addons.py index 1f9e9666301..35f4ef423f2 100644 --- a/hassio/api/addons.py +++ b/hassio/api/addons.py @@ -9,7 +9,8 @@ from ..const import ( ATTR_VERSION, ATTR_LAST_VERSION, ATTR_STATE, ATTR_BOOT, ATTR_OPTIONS, ATTR_URL, ATTR_DESCRIPTON, ATTR_DETACHED, ATTR_NAME, ATTR_REPOSITORY, - ATTR_BUILD, ATTR_AUTO_UPDATE, ATTR_NETWORK, ATTR_HOST_NETWORK, + ATTR_BUILD, ATTR_AUTO_UPDATE, ATTR_NETWORK, ATTR_HOST_NETWORK, ATTR_SLUG, + ATTR_SOURCE, ATTR_REPOSITORY, ATTR_REPOSITORIES, ATTR_ADDONS, BOOT_AUTO, BOOT_MANUAL) from ..validate import DOCKER_PORTS @@ -47,6 +48,44 @@ def _extract_addon(self, request, check_installed=True): return addon + @api_process + async def list(self, request): + """Return all addons / repositories .""" + data_addons = [] + for addon in self.addons.list_addons: + data_addons.append({ + ATTR_NAME: addon.name, + ATTR_SLUG: addon.slug, + ATTR_DESCRIPTON: addon.description, + ATTR_VERSION: addon.last_version, + ATTR_INSTALLED: addon.version_installed, + ATTR_ARCH: addon.supported_arch, + ATTR_DETACHED: addon.is_detached, + ATTR_REPOSITORY: addon.repository, + ATTR_BUILD: addon.need_build, + ATTR_URL: addon.url, + }) + + data_repositories = [] + for repository in self.addons.list_repositories: + data_repositories.append({ + ATTR_SLUG: repository.slug, + ATTR_NAME: repository.name, + ATTR_SOURCE: repository.source, + ATTR_URL: repository.url, + ATTR_MAINTAINER: repository.maintainer, + }) + + return { + ATTR_ADDONS: data_addons, + ATTR_REPOSITORIES: data_repositories, + } + + @api_process + def reload(self, request): + """Reload all addons data.""" + return self.addons.reload() + @api_process async def info(self, request): """Return addon information.""" diff --git a/hassio/api/snapshots.py b/hassio/api/snapshots.py index 0fdab2fec2b..e79d3a2e709 100644 --- a/hassio/api/snapshots.py +++ b/hassio/api/snapshots.py @@ -9,7 +9,7 @@ from ..const import ( ATTR_NAME, ATTR_SLUG, ATTR_DATE, ATTR_ADDONS, ATTR_REPOSITORIES, ATTR_HOMEASSISTANT, ATTR_VERSION, ATTR_SIZE, ATTR_FOLDERS, ATTR_TYPE, - ATTR_DEVICES) + ATTR_DEVICES, ATTR_SNAPSHOTS) _LOGGER = logging.getLogger(__name__) @@ -47,23 +47,39 @@ def _extract_snapshot(self, request): raise RuntimeError("Snapshot not exists") return snapshot - @staticmethod - def _addons_list(snapshot): - """Generate a list with addons data.""" - data = [] - for addon_data in snapshot.addons: - data.append({ - ATTR_SLUG: addon_data[ATTR_SLUG], - ATTR_NAME: addon_data[ATTR_NAME], - ATTR_VERSION: addon_data[ATTR_VERSION], + @api_process + async def list(self, request): + """Return snapshot list.""" + data_snapshots = [] + for snapshot in self.snapshots.list_snapshots: + data_snapshots.append({ + ATTR_SLUG: snapshot.slug, + ATTR_NAME: snapshot.name, + ATTR_DATE: snapshot.date, }) - return data + + return { + ATTR_SNAPSHOTS: data_snapshots, + } + + @api_process + def reload(self, request): + """Reload snapshot list.""" + return asyncio.shield(self.snapshots.reload(), loop=self.loop) @api_process async def info(self, request): """Return snapshot info.""" snapshot = self._extract_snapshot(request) + data_addons = [] + for addon_data in snapshot.addons: + data_addons.append({ + ATTR_SLUG: addon_data[ATTR_SLUG], + ATTR_NAME: addon_data[ATTR_NAME], + ATTR_VERSION: addon_data[ATTR_VERSION], + }) + return { ATTR_SLUG: snapshot.slug, ATTR_TYPE: snapshot.sys_type, @@ -74,7 +90,7 @@ async def info(self, request): ATTR_VERSION: snapshot.homeassistant_version, ATTR_DEVICES: snapshot.homeassistant_devices, }, - ATTR_ADDONS: self._addons_list(snapshot), + ATTR_ADDONS: data_addons, ATTR_REPOSITORIES: snapshot.repositories, ATTR_FOLDERS: snapshot.folders, } diff --git a/hassio/api/supervisor.py b/hassio/api/supervisor.py index 05d13f6cbbe..32cadd47962 100644 --- a/hassio/api/supervisor.py +++ b/hassio/api/supervisor.py @@ -10,7 +10,7 @@ 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, ATTR_ARCH, - ATTR_BUILD, ATTR_TIMEZONE, ATTR_DATE, ATTR_SNAPSHOTS) + ATTR_BUILD, ATTR_TIMEZONE) from ..tools import validate_timezone _LOGGER = logging.getLogger(__name__) @@ -77,18 +77,6 @@ def _repositories_list(self): return data - def _snapshots_list(self): - """Return a list of available snapshots.""" - data = [] - for snapshot in self.snapshots.list_snapshots: - data.append({ - ATTR_SLUG: snapshot.slug, - ATTR_NAME: snapshot.name, - ATTR_DATE: snapshot.date, - }) - - return data - @api_process async def ping(self, request): """Return ok for signal that the api is ready.""" diff --git a/hassio/snapshots/__init__.py b/hassio/snapshots/__init__.py index bc8603a9b75..c865becddd3 100644 --- a/hassio/snapshots/__init__.py +++ b/hassio/snapshots/__init__.py @@ -37,7 +37,7 @@ def get(self, slug): def _create_snapshot(self, name, sys_type): """Initialize a new snapshot object from name.""" - date_str = str(datetime.utcnow()) + date_str = datetime.utcnow().isoformat() slug = create_slug(name, date_str) tar_file = Path(self.config.path_backup, "{}.tar".format(slug)) From 3c5d4037f77d06c071a64bcb1aa5d6b5456e89b4 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 12 Jul 2017 02:19:01 +0200 Subject: [PATCH 17/18] fix lint --- hassio/api/addons.py | 4 ++-- hassio/api/supervisor.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/hassio/api/addons.py b/hassio/api/addons.py index 35f4ef423f2..2e805bf5201 100644 --- a/hassio/api/addons.py +++ b/hassio/api/addons.py @@ -10,8 +10,8 @@ ATTR_VERSION, ATTR_LAST_VERSION, ATTR_STATE, ATTR_BOOT, ATTR_OPTIONS, ATTR_URL, ATTR_DESCRIPTON, ATTR_DETACHED, ATTR_NAME, ATTR_REPOSITORY, ATTR_BUILD, ATTR_AUTO_UPDATE, ATTR_NETWORK, ATTR_HOST_NETWORK, ATTR_SLUG, - ATTR_SOURCE, ATTR_REPOSITORY, ATTR_REPOSITORIES, ATTR_ADDONS, - BOOT_AUTO, BOOT_MANUAL) + ATTR_SOURCE, ATTR_REPOSITORIES, ATTR_ADDONS, ATTR_ARCH, ATTR_MAINTAINER, + ATTR_INSTALLED, BOOT_AUTO, BOOT_MANUAL) from ..validate import DOCKER_PORTS _LOGGER = logging.getLogger(__name__) diff --git a/hassio/api/supervisor.py b/hassio/api/supervisor.py index 32cadd47962..2313ed1113f 100644 --- a/hassio/api/supervisor.py +++ b/hassio/api/supervisor.py @@ -93,7 +93,6 @@ async def info(self, request): ATTR_TIMEZONE: self.config.timezone, ATTR_ADDONS: self._addons_list(only_installed=True), ATTR_ADDONS_REPOSITORIES: self.config.addons_repositories, - ATTR_SNAPSHOTS: self._snapshots_list(), } @api_process From 253962df8775e1e328bc1c899d5c934f65c65aa5 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 12 Jul 2017 02:25:18 +0200 Subject: [PATCH 18/18] Update version.json --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index f92c8dc76ca..038cec26d23 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { - "hassio": "0.42", + "hassio": "0.43", "homeassistant": "0.48.1", "resinos": "0.8", "resinhup": "0.1",