From 02c8baef68252abbb5a57cbfffc6ef2dc88ba13b Mon Sep 17 00:00:00 2001 From: pvizeli Date: Tue, 11 Jul 2017 12:26:58 +0200 Subject: [PATCH] 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 35c1cddce..51c652cb1 100644 --- a/hassio/addons/data.py +++ b/hassio/addons/data.py @@ -15,54 +15,22 @@ from .validate import ( 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 02fe0e3a4..bc57e9865 100644 --- a/hassio/config.py +++ b/hassio/config.py @@ -6,11 +6,9 @@ import os 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 @@ DATETIME_FORMAT = "%Y%m%d %H:%M:%S" HOMEASSISTANT_CONFIG = PurePath("homeassistant") HOMEASSISTANT_LAST = 'homeassistant_last' -HOMEASSISTANT_DEVICES = 'homeassistant_devices' HASSIO_SSL = PurePath("ssl") HASSIO_LAST = 'hassio_last' @@ -50,7 +47,6 @@ SCHEMA_CONFIG = vol.Schema({ 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 @@ SCHEMA_CONFIG = vol.Schema({ }, 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 @@ class CoreConfig(object): 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 f63515f14..c8ee16ad9 100644 --- a/hassio/const.py +++ b/hassio/const.py @@ -24,6 +24,7 @@ RESTART_EXIT_CODE = 100 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 7b361bf5b..d86202b66 100644 --- a/hassio/dock/homeassistant.py +++ b/hassio/dock/homeassistant.py @@ -13,9 +13,10 @@ HASS_DOCKER_NAME = 'homeassistant' 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 @@ class DockerHomeAssistant(DockerBase): @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 000000000..82525e5f5 --- /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 7147fe62d..975cfc303 100644 --- a/hassio/tools.py +++ b/hassio/tools.py @@ -9,6 +9,7 @@ import aiohttp 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 4a97a54b7..f339a9348 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 @@ DOCKER_PORTS = vol.Schema({ 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) +})