diff --git a/API.md b/API.md index 58e5d136c..9e14112d4 100644 --- a/API.md +++ b/API.md @@ -217,25 +217,36 @@ return: ### Host - POST `/host/reload` + - POST `/host/shutdown` + - POST `/host/reboot` + - GET `/host/info` ```json { - "type": "", - "version": "", - "last_version": "", - "features": ["shutdown", "reboot", "update", "hostname", "network_info", "network_control"], - "hostname": "", - "os": "", - "audio": { - "input": "0,0", - "output": "0,0" - } + "hostname": "hostname|null", + "features": ["shutdown", "reboot", "update", "hostname"], + "operating_system": "Hass.io-OS XY|Ubuntu 16.4|null", + "kernel": "4.15.7|null", + "chassis": "specific|null", + "type": "Hass.io-OS Type|null", + "deployment": "stable|beta|dev|null", + "version": "xy|null", + "last_version": "xy|null", } ``` +- POST `/host/options` + +```json +{ + "hostname": "", +} +``` + + - POST `/host/update` Optional: @@ -284,24 +295,6 @@ Optional: } ``` -### Network - -- GET `/network/info` - -```json -{ - "hostname": "" -} -``` - -- POST `/network/options` - -```json -{ - "hostname": "", -} -``` - ### Home Assistant - GET `/homeassistant/info` @@ -310,6 +303,7 @@ Optional: { "version": "INSTALL_VERSION", "last_version": "LAST_VERSION", + "machine": "Image machine type", "image": "str", "custom": "bool -> if custom image", "boot": "bool", @@ -613,46 +607,3 @@ This service perform a auto discovery to Home-Assistant. ``` - DEL `/services/mqtt` - -## Host Control - -Communicate over UNIX socket with a host daemon. - -- commands - -``` -# info --> {'type', 'version', 'last_version', 'features', 'hostname'} -# reboot -# shutdown -# host-update [v] - -# hostname xy - -# network info --> {} -# network wlan ssd xy -# network wlan password xy -# network int ip xy -# network int netmask xy -# network int route xy -``` - -Features: - -- shutdown -- reboot -- update -- hostname -- network_info -- network_control - -Answer: -``` -{}|OK|ERROR|WRONG -``` - -- {}: json -- OK: call was successfully -- ERROR: error on call -- WRONG: not supported diff --git a/Dockerfile b/Dockerfile index df893aafb..2ed844f41 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,9 @@ RUN apk add --no-cache \ python3 \ git \ socat \ + glib \ libstdc++ \ + eudev-libs \ && apk add --no-cache --virtual .build-dependencies \ make \ python3-dev \ diff --git a/hassio/__main__.py b/hassio/__main__.py index 4d6f0d6b9..b64bad447 100644 --- a/hassio/__main__.py +++ b/hassio/__main__.py @@ -5,7 +5,6 @@ import logging import sys import hassio.bootstrap as bootstrap -import hassio.core as core _LOGGER = logging.getLogger(__name__) @@ -34,14 +33,13 @@ if __name__ == "__main__": _LOGGER.info("Initialize Hassio setup") coresys = bootstrap.initialize_coresys(loop) - hassio = core.HassIO(coresys) bootstrap.migrate_system_env(coresys) _LOGGER.info("Setup HassIO") - loop.run_until_complete(hassio.setup()) + loop.run_until_complete(coresys.core.setup()) - loop.call_soon_threadsafe(loop.create_task, hassio.start()) + loop.call_soon_threadsafe(loop.create_task, coresys.core.start()) loop.call_soon_threadsafe(bootstrap.reg_signal, loop) try: @@ -49,7 +47,7 @@ if __name__ == "__main__": loop.run_forever() finally: _LOGGER.info("Stopping HassIO") - loop.run_until_complete(hassio.stop()) + loop.run_until_complete(coresys.core.stop()) executor.shutdown(wait=False) loop.close() diff --git a/hassio/addons/__init__.py b/hassio/addons/__init__.py index d75dfab96..58c728af1 100644 --- a/hassio/addons/__init__.py +++ b/hassio/addons/__init__.py @@ -5,7 +5,7 @@ import logging from .addon import Addon from .repository import Repository from .data import AddonsData -from ..const import REPOSITORY_CORE, REPOSITORY_LOCAL, BOOT_AUTO +from ..const import REPOSITORY_CORE, REPOSITORY_LOCAL, BOOT_AUTO, STATE_STARTED from ..coresys import CoreSysAttributes _LOGGER = logging.getLogger(__name__) @@ -56,7 +56,7 @@ class AddonManager(CoreSysAttributes): # init hassio built-in repositories repositories = \ - set(self._config.addons_repositories) | BUILTIN_REPOSITORIES + set(self.sys_config.addons_repositories) | BUILTIN_REPOSITORIES # init custom repositories & load addons await self.load_repositories(repositories) @@ -66,7 +66,7 @@ class AddonManager(CoreSysAttributes): tasks = [repository.update() for repository in self.repositories_obj.values()] if tasks: - await asyncio.wait(tasks, loop=self._loop) + await asyncio.wait(tasks) # read data from repositories self.data.reload() @@ -90,16 +90,16 @@ class AddonManager(CoreSysAttributes): # don't add built-in repository to config if url not in BUILTIN_REPOSITORIES: - self._config.add_addon_repository(url) + self.sys_config.add_addon_repository(url) tasks = [_add_repository(url) for url in new_rep - old_rep] if tasks: - await asyncio.wait(tasks, loop=self._loop) + await asyncio.wait(tasks) # del new repository for url in old_rep - new_rep - BUILTIN_REPOSITORIES: self.repositories_obj.pop(url).remove() - self._config.drop_addon_repository(url) + self.sys_config.drop_addon_repository(url) # update data self.data.reload() @@ -125,13 +125,13 @@ class AddonManager(CoreSysAttributes): self.addons_obj[addon_slug] = addon if tasks: - await asyncio.wait(tasks, loop=self._loop) + await asyncio.wait(tasks) # remove for addon_slug in del_addons: self.addons_obj.pop(addon_slug) - async def auto_boot(self, stage): + async def boot(self, stage): """Boot addons with mode auto.""" tasks = [] for addon in self.addons_obj.values(): @@ -141,5 +141,18 @@ class AddonManager(CoreSysAttributes): _LOGGER.info("Startup %s run %d addons", stage, len(tasks)) if tasks: - await asyncio.wait(tasks, loop=self._loop) - await asyncio.sleep(self._config.wait_boot, loop=self._loop) + await asyncio.wait(tasks) + await asyncio.sleep(self.sys_config.wait_boot) + + async def shutdown(self, stage): + """Shutdown addons.""" + tasks = [] + for addon in self.addons_obj.values(): + if addon.is_installed and \ + await addon.state() == STATE_STARTED and \ + addon.startup == stage: + tasks.append(addon.stop()) + + _LOGGER.info("Shutdown %s stop %d addons", stage, len(tasks)) + if tasks: + await asyncio.wait(tasks) diff --git a/hassio/addons/addon.py b/hassio/addons/addon.py index e17f3f7a5..4bebda235 100644 --- a/hassio/addons/addon.py +++ b/hassio/addons/addon.py @@ -66,7 +66,7 @@ class Addon(CoreSysAttributes): @property def _data(self): """Return addons data storage.""" - return self._addons.data + return self.sys_addons.data @property def is_installed(self): @@ -376,7 +376,7 @@ class Addon(CoreSysAttributes): if self.is_installed and \ ATTR_AUDIO_OUTPUT in self._data.user[self._id]: return self._data.user[self._id][ATTR_AUDIO_OUTPUT] - return self._alsa.default.output + return self.sys_host.alsa.default.output @audio_output.setter def audio_output(self, value): @@ -394,7 +394,7 @@ class Addon(CoreSysAttributes): if self.is_installed and ATTR_AUDIO_INPUT in self._data.user[self._id]: return self._data.user[self._id][ATTR_AUDIO_INPUT] - return self._alsa.default.input + return self.sys_host.alsa.default.input @audio_input.setter def audio_input(self, value): @@ -436,11 +436,11 @@ class Addon(CoreSysAttributes): # Repository with dockerhub images if ATTR_IMAGE in addon_data: - return addon_data[ATTR_IMAGE].format(arch=self._arch) + return addon_data[ATTR_IMAGE].format(arch=self.sys_arch) # local build return "{}/{}-addon-{}".format( - addon_data[ATTR_REPOSITORY], self._arch, + addon_data[ATTR_REPOSITORY], self.sys_arch, addon_data[ATTR_SLUG]) @property @@ -461,12 +461,12 @@ class Addon(CoreSysAttributes): @property def path_data(self): """Return addon data path inside supervisor.""" - return Path(self._config.path_addons_data, self._id) + return Path(self.sys_config.path_addons_data, self._id) @property def path_extern_data(self): """Return addon data path external for docker.""" - return PurePath(self._config.path_extern_addons_data, self._id) + return PurePath(self.sys_config.path_extern_addons_data, self._id) @property def path_options(self): @@ -506,16 +506,16 @@ class Addon(CoreSysAttributes): @property def path_asound(self): """Return path to asound config.""" - return Path(self._config.path_tmp, f"{self.slug}_asound") + return Path(self.sys_config.path_tmp, f"{self.slug}_asound") @property def path_extern_asound(self): """Return path to asound config for docker.""" - return Path(self._config.path_extern_tmp, f"{self.slug}_asound") + return Path(self.sys_config.path_extern_tmp, f"{self.slug}_asound") def save_data(self): """Save data of addon.""" - self._addons.data.save_data() + self.sys_addons.data.save_data() def write_options(self): """Return True if addon options is written to data.""" @@ -537,7 +537,7 @@ class Addon(CoreSysAttributes): def write_asound(self): """Write asound config to file and return True on success.""" - asound_config = self._alsa.asound( + asound_config = self.sys_host.alsa.asound( alsa_input=self.audio_input, alsa_output=self.audio_output) try: @@ -590,9 +590,9 @@ class Addon(CoreSysAttributes): async def install(self): """Install a addon.""" - if self._arch not in self.supported_arch: + if self.sys_arch not in self.supported_arch: _LOGGER.error( - "Addon %s not supported on %s", self._id, self._arch) + "Addon %s not supported on %s", self._id, self.sys_arch) return False if self.is_installed: @@ -735,7 +735,7 @@ class Addon(CoreSysAttributes): @check_installed async def snapshot(self, tar_file): """Snapshot a state of a addon.""" - with TemporaryDirectory(dir=str(self._config.path_tmp)) as temp: + with TemporaryDirectory(dir=str(self.sys_config.path_tmp)) as temp: # store local image if self.need_build and not await \ self.instance.export_image(Path(temp, "image.tar")): @@ -764,7 +764,7 @@ class Addon(CoreSysAttributes): try: _LOGGER.info("Build snapshot for addon %s", self._id) - await self._loop.run_in_executor(None, _write_tarfile) + await self.sys_run_in_executor(_write_tarfile) except (tarfile.TarError, OSError) as err: _LOGGER.error("Can't write tarfile %s: %s", tar_file, err) return False @@ -774,7 +774,7 @@ class Addon(CoreSysAttributes): async def restore(self, tar_file): """Restore a state of a addon.""" - with TemporaryDirectory(dir=str(self._config.path_tmp)) as temp: + with TemporaryDirectory(dir=str(self.sys_config.path_tmp)) as temp: # extract snapshot def _extract_tarfile(): """Extract tar snapshot.""" @@ -782,7 +782,7 @@ class Addon(CoreSysAttributes): snapshot.extractall(path=Path(temp)) try: - await self._loop.run_in_executor(None, _extract_tarfile) + await self.sys_run_in_executor(_extract_tarfile) except tarfile.TarError as err: _LOGGER.error("Can't read tarfile %s: %s", tar_file, err) return False @@ -828,7 +828,7 @@ class Addon(CoreSysAttributes): try: _LOGGER.info("Restore data for addon %s", self._id) - await self._loop.run_in_executor(None, _restore_data) + await self.sys_run_in_executor(_restore_data) except shutil.Error as err: _LOGGER.error("Can't restore origin data: %s", err) return False diff --git a/hassio/addons/build.py b/hassio/addons/build.py index d98c9597a..4d9a37618 100644 --- a/hassio/addons/build.py +++ b/hassio/addons/build.py @@ -25,13 +25,13 @@ class AddonBuild(JsonConfig, CoreSysAttributes): @property def addon(self): """Return addon of build data.""" - return self._addons.get(self._id) + return self.sys_addons.get(self._id) @property def base_image(self): """Base images for this addon.""" return self._data[ATTR_BUILD_FROM].get( - self._arch, BASE_IMAGE[self._arch]) + self.sys_arch, BASE_IMAGE[self.sys_arch]) @property def squash(self): @@ -53,7 +53,7 @@ class AddonBuild(JsonConfig, CoreSysAttributes): 'squash': self.squash, 'labels': { 'io.hass.version': version, - 'io.hass.arch': self._arch, + 'io.hass.arch': self.sys_arch, 'io.hass.type': META_ADDON, 'io.hass.name': self._fix_label('name'), 'io.hass.description': self._fix_label('description'), @@ -61,7 +61,7 @@ class AddonBuild(JsonConfig, CoreSysAttributes): 'buildargs': { 'BUILD_FROM': self.base_image, 'BUILD_VERSION': version, - 'BUILD_ARCH': self._arch, + 'BUILD_ARCH': self.sys_arch, **self.additional_args, } } diff --git a/hassio/addons/data.py b/hassio/addons/data.py index 777d2b025..ade770c54 100644 --- a/hassio/addons/data.py +++ b/hassio/addons/data.py @@ -56,17 +56,17 @@ class AddonsData(JsonConfig, CoreSysAttributes): # read core repository self._read_addons_folder( - self._config.path_addons_core, REPOSITORY_CORE) + self.sys_config.path_addons_core, REPOSITORY_CORE) # read local repository self._read_addons_folder( - self._config.path_addons_local, REPOSITORY_LOCAL) + self.sys_config.path_addons_local, REPOSITORY_LOCAL) # add built-in repositories information self._set_builtin_repositories() # read custom git repositories - for repository_element in self._config.path_addons_git.iterdir(): + for repository_element in self.sys_config.path_addons_git.iterdir(): if repository_element.is_dir(): self._read_git_repository(repository_element) diff --git a/hassio/addons/git.py b/hassio/addons/git.py index a00521946..6f487d579 100644 --- a/hassio/addons/git.py +++ b/hassio/addons/git.py @@ -45,7 +45,7 @@ class GitRepo(CoreSysAttributes): async with self.lock: try: _LOGGER.info("Load addon %s repository", self.path) - self.repo = await self._loop.run_in_executor( + self.repo = await self.sys_loop.run_in_executor( None, git.Repo, str(self.path)) except (git.InvalidGitRepositoryError, git.NoSuchPathError, @@ -68,7 +68,7 @@ class GitRepo(CoreSysAttributes): try: _LOGGER.info("Clone addon %s repository", self.url) - self.repo = await self._loop.run_in_executor(None, ft.partial( + self.repo = await self.sys_run_in_executor(ft.partial( git.Repo.clone_from, self.url, str(self.path), **git_args )) @@ -89,7 +89,7 @@ class GitRepo(CoreSysAttributes): async with self.lock: try: _LOGGER.info("Pull addon %s repository", self.url) - await self._loop.run_in_executor( + await self.sys_loop.run_in_executor( None, self.repo.remotes.origin.pull) except (git.InvalidGitRepositoryError, git.NoSuchPathError, diff --git a/hassio/addons/repository.py b/hassio/addons/repository.py index 851e9e037..37d75ea75 100644 --- a/hassio/addons/repository.py +++ b/hassio/addons/repository.py @@ -30,7 +30,7 @@ class Repository(CoreSysAttributes): @property def _mesh(self): """Return data struct repository.""" - return self._addons.data.repositories.get(self._id, {}) + return self.sys_addons.data.repositories.get(self._id, {}) @property def slug(self): diff --git a/hassio/api/__init__.py b/hassio/api/__init__.py index b91d79485..20195d21c 100644 --- a/hassio/api/__init__.py +++ b/hassio/api/__init__.py @@ -9,7 +9,6 @@ from .discovery import APIDiscovery from .homeassistant import APIHomeAssistant from .hardware import APIHardware from .host import APIHost -from .network import APINetwork from .proxy import APIProxy from .supervisor import APISupervisor from .snapshots import APISnapshots @@ -28,7 +27,7 @@ class RestAPI(CoreSysAttributes): self.coresys = coresys self.security = SecurityMiddleware(coresys) self.webapp = web.Application( - middlewares=[self.security.token_validation], loop=self._loop) + middlewares=[self.security.token_validation], loop=coresys.loop) # service stuff self._handler = None @@ -44,7 +43,6 @@ class RestAPI(CoreSysAttributes): self._register_panel() self._register_addons() self._register_snapshots() - self._register_network() self._register_discovery() self._register_services() @@ -61,16 +59,6 @@ class RestAPI(CoreSysAttributes): web.post('/host/reload', api_host.reload), ]) - def _register_network(self): - """Register network function.""" - api_net = APINetwork() - api_net.coresys = self.coresys - - self.webapp.add_routes([ - web.get('/network/info', api_net.info), - web.post('/network/options', api_net.options), - ]) - def _register_hardware(self): """Register hardware function.""" api_hardware = APIHardware() @@ -221,10 +209,10 @@ class RestAPI(CoreSysAttributes): async def start(self): """Run rest api webserver.""" - self._handler = self.webapp.make_handler(loop=self._loop) + self._handler = self.webapp.make_handler() try: - self.server = await self._loop.create_server( + self.server = await self.sys_loop.create_server( self._handler, "0.0.0.0", "80") except OSError as err: _LOGGER.fatal( diff --git a/hassio/api/addons.py b/hassio/api/addons.py index a4d5b4593..f382c9116 100644 --- a/hassio/api/addons.py +++ b/hassio/api/addons.py @@ -43,7 +43,7 @@ class APIAddons(CoreSysAttributes): def _extract_addon(self, request, check_installed=True): """Return addon and if not exists trow a exception.""" - addon = self._addons.get(request.match_info.get('addon')) + addon = self.sys_addons.get(request.match_info.get('addon')) if not addon: raise RuntimeError("Addon not exists") @@ -64,7 +64,7 @@ class APIAddons(CoreSysAttributes): async def list(self, request): """Return all addons / repositories .""" data_addons = [] - for addon in self._addons.list_addons: + for addon in self.sys_addons.list_addons: data_addons.append({ ATTR_NAME: addon.name, ATTR_SLUG: addon.slug, @@ -81,7 +81,7 @@ class APIAddons(CoreSysAttributes): }) data_repositories = [] - for repository in self._addons.list_repositories: + for repository in self.sys_addons.list_repositories: data_repositories.append({ ATTR_SLUG: repository.slug, ATTR_NAME: repository.name, @@ -98,7 +98,7 @@ class APIAddons(CoreSysAttributes): @api_process async def reload(self, request): """Reload all addons data.""" - await asyncio.shield(self._addons.reload(), loop=self._loop) + await asyncio.shield(self.sys_addons.reload()) return True @api_process @@ -194,13 +194,13 @@ class APIAddons(CoreSysAttributes): def install(self, request): """Install addon.""" addon = self._extract_addon(request, check_installed=False) - return asyncio.shield(addon.install(), loop=self._loop) + return asyncio.shield(addon.install()) @api_process def uninstall(self, request): """Uninstall addon.""" addon = self._extract_addon(request) - return asyncio.shield(addon.uninstall(), loop=self._loop) + return asyncio.shield(addon.uninstall()) @api_process def start(self, request): @@ -214,13 +214,13 @@ class APIAddons(CoreSysAttributes): except vol.Invalid as ex: raise RuntimeError(humanize_error(options, ex)) from None - return asyncio.shield(addon.start(), loop=self._loop) + return asyncio.shield(addon.start()) @api_process def stop(self, request): """Stop addon.""" addon = self._extract_addon(request) - return asyncio.shield(addon.stop(), loop=self._loop) + return asyncio.shield(addon.stop()) @api_process def update(self, request): @@ -230,13 +230,13 @@ class APIAddons(CoreSysAttributes): if addon.last_version == addon.version_installed: raise RuntimeError("No update available!") - return asyncio.shield(addon.update(), loop=self._loop) + return asyncio.shield(addon.update()) @api_process def restart(self, request): """Restart addon.""" addon = self._extract_addon(request) - return asyncio.shield(addon.restart(), loop=self._loop) + return asyncio.shield(addon.restart()) @api_process def rebuild(self, request): @@ -245,7 +245,7 @@ class APIAddons(CoreSysAttributes): if not addon.need_build: raise RuntimeError("Only local build addons are supported") - return asyncio.shield(addon.rebuild(), loop=self._loop) + return asyncio.shield(addon.rebuild()) @api_process_raw(CONTENT_TYPE_BINARY) def logs(self, request): @@ -291,4 +291,4 @@ class APIAddons(CoreSysAttributes): raise RuntimeError("STDIN not supported by addon") data = await request.read() - return await asyncio.shield(addon.write_stdin(data), loop=self._loop) + return await asyncio.shield(addon.write_stdin(data)) diff --git a/hassio/api/discovery.py b/hassio/api/discovery.py index fb028fcd9..7ab5f8d2a 100644 --- a/hassio/api/discovery.py +++ b/hassio/api/discovery.py @@ -21,7 +21,7 @@ class APIDiscovery(CoreSysAttributes): def _extract_message(self, request): """Extract discovery message from URL.""" - message = self._services.discovery.get(request.match_info.get('uuid')) + message = self.sys_discovery.get(request.match_info.get('uuid')) if not message: raise RuntimeError("Discovery message not found") return message @@ -30,7 +30,7 @@ class APIDiscovery(CoreSysAttributes): async def list(self, request): """Show register services.""" discovery = [] - for message in self._services.discovery.list_messages: + for message in self.sys_discovery.list_messages: discovery.append({ ATTR_PROVIDER: message.provider, ATTR_UUID: message.uuid, @@ -45,7 +45,7 @@ class APIDiscovery(CoreSysAttributes): async def set_discovery(self, request): """Write data into a discovery pipeline.""" body = await api_validate(SCHEMA_DISCOVERY, request) - message = self._services.discovery.send( + message = self.sys_discovery.send( provider=request[REQUEST_FROM], **body) return {ATTR_UUID: message.uuid} @@ -68,5 +68,5 @@ class APIDiscovery(CoreSysAttributes): """Delete data into a discovery message.""" message = self._extract_message(request) - self._services.discovery.remove(message) + self.sys_discovery.remove(message) return True diff --git a/hassio/api/hardware.py b/hassio/api/hardware.py index e5e22a35f..7830b9675 100644 --- a/hassio/api/hardware.py +++ b/hassio/api/hardware.py @@ -16,11 +16,11 @@ class APIHardware(CoreSysAttributes): async def info(self, request): """Show hardware info.""" return { - ATTR_SERIAL: list(self._hardware.serial_devices), - ATTR_INPUT: list(self._hardware.input_devices), - ATTR_DISK: list(self._hardware.disk_devices), - ATTR_GPIO: list(self._hardware.gpio_devices), - ATTR_AUDIO: self._hardware.audio_devices, + ATTR_SERIAL: list(self.sys_hardware.serial_devices), + ATTR_INPUT: list(self.sys_hardware.input_devices), + ATTR_DISK: list(self.sys_hardware.disk_devices), + ATTR_GPIO: list(self.sys_hardware.gpio_devices), + ATTR_AUDIO: self.sys_hardware.audio_devices, } @api_process @@ -28,7 +28,7 @@ class APIHardware(CoreSysAttributes): """Show ALSA audio devices.""" return { ATTR_AUDIO: { - ATTR_INPUT: self._alsa.input_devices, - ATTR_OUTPUT: self._alsa.output_devices, + ATTR_INPUT: self.sys_host.alsa.input_devices, + ATTR_OUTPUT: self.sys_host.alsa.output_devices, } } diff --git a/hassio/api/homeassistant.py b/hassio/api/homeassistant.py index 86bfaf296..dd5e9698b 100644 --- a/hassio/api/homeassistant.py +++ b/hassio/api/homeassistant.py @@ -9,7 +9,8 @@ from ..const import ( ATTR_VERSION, ATTR_LAST_VERSION, ATTR_IMAGE, ATTR_CUSTOM, ATTR_BOOT, ATTR_PORT, ATTR_PASSWORD, ATTR_SSL, ATTR_WATCHDOG, ATTR_CPU_PERCENT, ATTR_MEMORY_USAGE, ATTR_MEMORY_LIMIT, ATTR_NETWORK_RX, ATTR_NETWORK_TX, - ATTR_BLK_READ, ATTR_BLK_WRITE, ATTR_WAIT_BOOT, CONTENT_TYPE_BINARY) + ATTR_BLK_READ, ATTR_BLK_WRITE, ATTR_WAIT_BOOT, ATTR_MACHINE, + CONTENT_TYPE_BINARY) from ..coresys import CoreSysAttributes from ..validate import NETWORK_PORT, DOCKER_IMAGE @@ -43,15 +44,16 @@ class APIHomeAssistant(CoreSysAttributes): async def info(self, request): """Return host information.""" return { - ATTR_VERSION: self._homeassistant.version, - ATTR_LAST_VERSION: self._homeassistant.last_version, - ATTR_IMAGE: self._homeassistant.image, - ATTR_CUSTOM: self._homeassistant.is_custom_image, - ATTR_BOOT: self._homeassistant.boot, - ATTR_PORT: self._homeassistant.api_port, - ATTR_SSL: self._homeassistant.api_ssl, - ATTR_WATCHDOG: self._homeassistant.watchdog, - ATTR_WAIT_BOOT: self._homeassistant.wait_boot, + ATTR_VERSION: self.sys_homeassistant.version, + ATTR_LAST_VERSION: self.sys_homeassistant.last_version, + ATTR_MACHINE: self.sys_homeassistant.machine, + ATTR_IMAGE: self.sys_homeassistant.image, + ATTR_CUSTOM: self.sys_homeassistant.is_custom_image, + ATTR_BOOT: self.sys_homeassistant.boot, + ATTR_PORT: self.sys_homeassistant.api_port, + ATTR_SSL: self.sys_homeassistant.api_ssl, + ATTR_WATCHDOG: self.sys_homeassistant.watchdog, + ATTR_WAIT_BOOT: self.sys_homeassistant.wait_boot, } @api_process @@ -60,34 +62,34 @@ class APIHomeAssistant(CoreSysAttributes): body = await api_validate(SCHEMA_OPTIONS, request) if ATTR_IMAGE in body and ATTR_LAST_VERSION in body: - self._homeassistant.image = body[ATTR_IMAGE] - self._homeassistant.last_version = body[ATTR_LAST_VERSION] + self.sys_homeassistant.image = body[ATTR_IMAGE] + self.sys_homeassistant.last_version = body[ATTR_LAST_VERSION] if ATTR_BOOT in body: - self._homeassistant.boot = body[ATTR_BOOT] + self.sys_homeassistant.boot = body[ATTR_BOOT] if ATTR_PORT in body: - self._homeassistant.api_port = body[ATTR_PORT] + self.sys_homeassistant.api_port = body[ATTR_PORT] if ATTR_PASSWORD in body: - self._homeassistant.api_password = body[ATTR_PASSWORD] + self.sys_homeassistant.api_password = body[ATTR_PASSWORD] if ATTR_SSL in body: - self._homeassistant.api_ssl = body[ATTR_SSL] + self.sys_homeassistant.api_ssl = body[ATTR_SSL] if ATTR_WATCHDOG in body: - self._homeassistant.watchdog = body[ATTR_WATCHDOG] + self.sys_homeassistant.watchdog = body[ATTR_WATCHDOG] if ATTR_WAIT_BOOT in body: - self._homeassistant.wait_boot = body[ATTR_WAIT_BOOT] + self.sys_homeassistant.wait_boot = body[ATTR_WAIT_BOOT] - self._homeassistant.save_data() + self.sys_homeassistant.save_data() return True @api_process async def stats(self, request): """Return resource information.""" - stats = await self._homeassistant.stats() + stats = await self.sys_homeassistant.stats() if not stats: raise RuntimeError("No stats available") @@ -105,38 +107,38 @@ class APIHomeAssistant(CoreSysAttributes): async def update(self, request): """Update homeassistant.""" body = await api_validate(SCHEMA_VERSION, request) - version = body.get(ATTR_VERSION, self._homeassistant.last_version) + version = body.get(ATTR_VERSION, self.sys_homeassistant.last_version) - if version == self._homeassistant.version: + if version == self.sys_homeassistant.version: raise RuntimeError("Version {} is already in use".format(version)) return await asyncio.shield( - self._homeassistant.update(version), loop=self._loop) + self.sys_homeassistant.update(version)) @api_process def stop(self, request): """Stop homeassistant.""" - return asyncio.shield(self._homeassistant.stop(), loop=self._loop) + return asyncio.shield(self.sys_homeassistant.stop()) @api_process def start(self, request): """Start homeassistant.""" - return asyncio.shield(self._homeassistant.start(), loop=self._loop) + return asyncio.shield(self.sys_homeassistant.start()) @api_process def restart(self, request): """Restart homeassistant.""" - return asyncio.shield(self._homeassistant.restart(), loop=self._loop) + return asyncio.shield(self.sys_homeassistant.restart()) @api_process_raw(CONTENT_TYPE_BINARY) def logs(self, request): """Return homeassistant docker logs.""" - return self._homeassistant.logs() + return self.sys_homeassistant.logs() @api_process async def check(self, request): """Check config of homeassistant.""" - result = await self._homeassistant.check_config() + result = await self.sys_homeassistant.check_config() if not result.valid: raise RuntimeError(result.log) diff --git a/hassio/api/host.py b/hassio/api/host.py index a0b63f2c9..94991a46b 100644 --- a/hassio/api/host.py +++ b/hassio/api/host.py @@ -4,10 +4,10 @@ import logging import voluptuous as vol -from .utils import api_process_hostcontrol, api_process, api_validate +from .utils import api_process, api_validate from ..const import ( - ATTR_VERSION, ATTR_LAST_VERSION, ATTR_TYPE, ATTR_HOSTNAME, ATTR_FEATURES, - ATTR_OS) + ATTR_VERSION, ATTR_LAST_VERSION, ATTR_HOSTNAME, ATTR_FEATURES, ATTR_KERNEL, + ATTR_TYPE, ATTR_OPERATING_SYSTEM, ATTR_CHASSIS, ATTR_DEPLOYMENT) from ..coresys import CoreSysAttributes _LOGGER = logging.getLogger(__name__) @@ -16,6 +16,10 @@ SCHEMA_VERSION = vol.Schema({ vol.Optional(ATTR_VERSION): vol.Coerce(str), }) +SCHEMA_OPTIONS = vol.Schema({ + vol.Optional(ATTR_HOSTNAME): vol.Coerce(str), +}) + class APIHost(CoreSysAttributes): """Handle rest api for host functions.""" @@ -24,38 +28,45 @@ class APIHost(CoreSysAttributes): async def info(self, request): """Return host information.""" return { - ATTR_TYPE: self._host_control.type, - ATTR_VERSION: self._host_control.version, - ATTR_LAST_VERSION: self._host_control.last_version, - ATTR_FEATURES: self._host_control.features, - ATTR_HOSTNAME: self._host_control.hostname, - ATTR_OS: self._host_control.os_info, + ATTR_CHASSIS: self.sys_host.info.chassis, + ATTR_VERSION: None, + ATTR_LAST_VERSION: None, + ATTR_TYPE: None, + ATTR_FEATURES: self.sys_host.supperted_features, + ATTR_HOSTNAME: self.sys_host.info.hostname, + ATTR_OPERATING_SYSTEM: self.sys_host.info.operating_system, + ATTR_DEPLOYMENT: self.sys_host.info.deployment, + ATTR_KERNEL: self.sys_host.info.kernel, } - @api_process_hostcontrol + @api_process + async def options(self, request): + """Edit host settings.""" + body = await api_validate(SCHEMA_OPTIONS, request) + + # hostname + if ATTR_HOSTNAME in body: + await asyncio.shield( + self.sys_host.control.set_hostname(body[ATTR_HOSTNAME])) + + @api_process def reboot(self, request): """Reboot host.""" - return self._host_control.reboot() + return asyncio.shield(self.sys_host.control.reboot()) - @api_process_hostcontrol + @api_process def shutdown(self, request): """Poweroff host.""" - return self._host_control.shutdown() + return asyncio.shield(self.sys_host.control.shutdown()) - @api_process_hostcontrol - async def reload(self, request): + @api_process + def reload(self, request): """Reload host data.""" - await self._host_control.load() - return True + return asyncio.shield(self.sys_host.reload()) - @api_process_hostcontrol + @api_process async def update(self, request): """Update host OS.""" - body = await api_validate(SCHEMA_VERSION, request) - version = body.get(ATTR_VERSION, self._host_control.last_version) - - if version == self._host_control.version: - raise RuntimeError(f"Version {version} is already in use") - - return await asyncio.shield( - self._host_control.update(version=version), loop=self._loop) + pass + # body = await api_validate(SCHEMA_VERSION, request) + # version = body.get(ATTR_VERSION, self.sys_host.last_version) diff --git a/hassio/api/network.py b/hassio/api/network.py deleted file mode 100644 index c5c647066..000000000 --- a/hassio/api/network.py +++ /dev/null @@ -1,38 +0,0 @@ -"""Init file for HassIO network rest api.""" -import logging - -import voluptuous as vol - -from .utils import api_process, api_process_hostcontrol, api_validate -from ..const import ATTR_HOSTNAME -from ..coresys import CoreSysAttributes - -_LOGGER = logging.getLogger(__name__) - - -SCHEMA_OPTIONS = vol.Schema({ - vol.Optional(ATTR_HOSTNAME): vol.Coerce(str), -}) - - -class APINetwork(CoreSysAttributes): - """Handle rest api for network functions.""" - - @api_process - async def info(self, request): - """Show network settings.""" - return { - ATTR_HOSTNAME: self._host_control.hostname, - } - - @api_process_hostcontrol - async def options(self, request): - """Edit network settings.""" - body = await api_validate(SCHEMA_OPTIONS, request) - - # hostname - if ATTR_HOSTNAME in body: - if self._host_control.hostname != body[ATTR_HOSTNAME]: - await self._host_control.set_hostname(body[ATTR_HOSTNAME]) - - return True diff --git a/hassio/api/panel/hassio-app.html b/hassio/api/panel/hassio-app.html index f1d3192ae..09f945df5 100644 --- a/hassio/api/panel/hassio-app.html +++ b/hassio/api/panel/hassio-app.html @@ -18,8 +18,8 @@ .wave{opacity:0;}#waves, .wave{overflow:hidden;}.wave-container, .wave{border-radius:50%;}:host(.circle) #background, - :host(.circle) #waves{border-radius:50%;}:host(.circle) .wave-container{overflow:hidden;}