Update docstrings, comments and log messages (#707)

This commit is contained in:
Fabian Affolter 2018-09-18 23:47:47 +02:00 committed by GitHub
parent 67dcf1563b
commit 267791833e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 370 additions and 372 deletions

View File

@ -1,4 +1,4 @@
"""Init file for HassIO addons.""" """Init file for Hass.io add-ons."""
import asyncio import asyncio
import logging import logging
@ -14,10 +14,10 @@ BUILTIN_REPOSITORIES = set((REPOSITORY_CORE, REPOSITORY_LOCAL))
class AddonManager(CoreSysAttributes): class AddonManager(CoreSysAttributes):
"""Manage addons inside HassIO.""" """Manage add-ons inside Hass.io."""
def __init__(self, coresys): def __init__(self, coresys):
"""Initialize docker base wrapper.""" """Initialize Docker base wrapper."""
self.coresys = coresys self.coresys = coresys
self.data = AddonsData(coresys) self.data = AddonsData(coresys)
self.addons_obj = {} self.addons_obj = {}
@ -25,18 +25,18 @@ class AddonManager(CoreSysAttributes):
@property @property
def list_addons(self): def list_addons(self):
"""Return a list of all addons.""" """Return a list of all add-ons."""
return list(self.addons_obj.values()) return list(self.addons_obj.values())
@property @property
def list_installed(self): def list_installed(self):
"""Return a list of installed addons.""" """Return a list of installed add-ons."""
return [addon for addon in self.addons_obj.values() return [addon for addon in self.addons_obj.values()
if addon.is_installed] if addon.is_installed]
@property @property
def list_repositories(self): def list_repositories(self):
"""Return list of addon repositories.""" """Return list of add-on repositories."""
return list(self.repositories_obj.values()) return list(self.repositories_obj.values())
def get(self, addon_slug): def get(self, addon_slug):
@ -44,32 +44,32 @@ class AddonManager(CoreSysAttributes):
return self.addons_obj.get(addon_slug) return self.addons_obj.get(addon_slug)
def from_uuid(self, uuid): def from_uuid(self, uuid):
"""Return an add-on from uuid.""" """Return an add-on from UUID."""
for addon in self.list_addons: for addon in self.list_addons:
if addon.is_installed and uuid == addon.uuid: if addon.is_installed and uuid == addon.uuid:
return addon return addon
return None return None
def from_token(self, token): def from_token(self, token):
"""Return an add-on from hassio token.""" """Return an add-on from Hass.io token."""
for addon in self.list_addons: for addon in self.list_addons:
if addon.is_installed and token == addon.hassio_token: if addon.is_installed and token == addon.hassio_token:
return addon return addon
return None return None
async def load(self): async def load(self):
"""Startup addon management.""" """Start up add-on management."""
self.data.reload() self.data.reload()
# init hassio built-in repositories # Init Hass.io built-in repositories
repositories = \ repositories = \
set(self.sys_config.addons_repositories) | BUILTIN_REPOSITORIES set(self.sys_config.addons_repositories) | BUILTIN_REPOSITORIES
# init custom repositories & load addons # Init custom repositories and load add-ons
await self.load_repositories(repositories) await self.load_repositories(repositories)
async def reload(self): async def reload(self):
"""Update addons from repo and reload list.""" """Update add-ons from repository and reload list."""
tasks = [repository.update() for repository in tasks = [repository.update() for repository in
self.repositories_obj.values()] self.repositories_obj.values()]
if tasks: if tasks:
@ -113,14 +113,14 @@ class AddonManager(CoreSysAttributes):
await self.load_addons() await self.load_addons()
async def load_addons(self): async def load_addons(self):
"""Update/add internal addon store.""" """Update/add internal add-on store."""
all_addons = set(self.data.system) | set(self.data.cache) all_addons = set(self.data.system) | set(self.data.cache)
# calc diff # calc diff
add_addons = all_addons - set(self.addons_obj) add_addons = all_addons - set(self.addons_obj)
del_addons = set(self.addons_obj) - all_addons del_addons = set(self.addons_obj) - all_addons
_LOGGER.info("Load addons: %d all - %d new - %d remove", _LOGGER.info("Load add-ons: %d all - %d new - %d remove",
len(all_addons), len(add_addons), len(del_addons)) len(all_addons), len(add_addons), len(del_addons))
# new addons # new addons
@ -139,14 +139,14 @@ class AddonManager(CoreSysAttributes):
self.addons_obj.pop(addon_slug) self.addons_obj.pop(addon_slug)
async def boot(self, stage): async def boot(self, stage):
"""Boot addons with mode auto.""" """Boot add-ons with mode auto."""
tasks = [] tasks = []
for addon in self.addons_obj.values(): for addon in self.addons_obj.values():
if addon.is_installed and addon.boot == BOOT_AUTO and \ if addon.is_installed and addon.boot == BOOT_AUTO and \
addon.startup == stage: addon.startup == stage:
tasks.append(addon.start()) tasks.append(addon.start())
_LOGGER.info("Startup %s run %d addons", stage, len(tasks)) _LOGGER.info("Startup %s run %d add-ons", stage, len(tasks))
if tasks: if tasks:
await asyncio.wait(tasks) await asyncio.wait(tasks)
await asyncio.sleep(self.sys_config.wait_boot) await asyncio.sleep(self.sys_config.wait_boot)
@ -160,6 +160,6 @@ class AddonManager(CoreSysAttributes):
addon.startup == stage: addon.startup == stage:
tasks.append(addon.stop()) tasks.append(addon.stop())
_LOGGER.info("Shutdown %s stop %d addons", stage, len(tasks)) _LOGGER.info("Shutdown %s stop %d add-ons", stage, len(tasks))
if tasks: if tasks:
await asyncio.wait(tasks) await asyncio.wait(tasks)

View File

@ -1,4 +1,4 @@
"""Init file for HassIO addons.""" """Init file for Hass.io add-ons."""
from contextlib import suppress from contextlib import suppress
from copy import deepcopy from copy import deepcopy
import logging import logging
@ -43,7 +43,7 @@ RE_WEBUI = re.compile(
class Addon(CoreSysAttributes): class Addon(CoreSysAttributes):
"""Hold data for addon inside HassIO.""" """Hold data for add-on inside Hass.io."""
def __init__(self, coresys, slug): def __init__(self, coresys, slug):
"""Initialize data holder.""" """Initialize data holder."""
@ -59,27 +59,27 @@ class Addon(CoreSysAttributes):
@property @property
def slug(self): def slug(self):
"""Return slug/id of addon.""" """Return slug/id of add-on."""
return self._id return self._id
@property @property
def _mesh(self): def _mesh(self):
"""Return addon data from system or cache.""" """Return add-on data from system or cache."""
return self._data.system.get(self._id, self._data.cache.get(self._id)) return self._data.system.get(self._id, self._data.cache.get(self._id))
@property @property
def _data(self): def _data(self):
"""Return addons data storage.""" """Return add-ons data storage."""
return self.sys_addons.data return self.sys_addons.data
@property @property
def is_installed(self): def is_installed(self):
"""Return True if an addon is installed.""" """Return True if an add-on is installed."""
return self._id in self._data.system return self._id in self._data.system
@property @property
def is_detached(self): def is_detached(self):
"""Return True if addon is detached.""" """Return True if add-on is detached."""
return self._id not in self._data.cache return self._id not in self._data.cache
@property @property
@ -97,19 +97,19 @@ class Addon(CoreSysAttributes):
self._data.save_data() self._data.save_data()
def _set_uninstall(self): def _set_uninstall(self):
"""Set addon as uninstalled.""" """Set add-on as uninstalled."""
self._data.system.pop(self._id, None) self._data.system.pop(self._id, None)
self._data.user.pop(self._id, None) self._data.user.pop(self._id, None)
self._data.save_data() self._data.save_data()
def _set_update(self, version): def _set_update(self, version):
"""Update version of addon.""" """Update version of add-on."""
self._data.system[self._id] = deepcopy(self._data.cache[self._id]) self._data.system[self._id] = deepcopy(self._data.cache[self._id])
self._data.user[self._id][ATTR_VERSION] = version self._data.user[self._id][ATTR_VERSION] = version
self._data.save_data() self._data.save_data()
def _restore_data(self, user, system): def _restore_data(self, user, system):
"""Restore data to addon.""" """Restore data to add-on."""
self._data.user[self._id] = deepcopy(user) self._data.user[self._id] = deepcopy(user)
self._data.system[self._id] = deepcopy(system) self._data.system[self._id] = deepcopy(system)
self._data.save_data() self._data.save_data()
@ -126,7 +126,7 @@ class Addon(CoreSysAttributes):
@options.setter @options.setter
def options(self, value): def options(self, value):
"""Store user addon options.""" """Store user add-on options."""
if value is None: if value is None:
self._data.user[self._id][ATTR_OPTIONS] = {} self._data.user[self._id][ATTR_OPTIONS] = {}
else: else:
@ -158,7 +158,7 @@ class Addon(CoreSysAttributes):
@property @property
def name(self): def name(self):
"""Return name of addon.""" """Return name of add-on."""
return self._mesh[ATTR_NAME] return self._mesh[ATTR_NAME]
@property @property
@ -175,14 +175,14 @@ class Addon(CoreSysAttributes):
@property @property
def hassio_token(self): def hassio_token(self):
"""Return access token for hass.io API.""" """Return access token for Hass.io API."""
if self.is_installed: if self.is_installed:
return self._data.user[self._id].get(ATTR_ACCESS_TOKEN) return self._data.user[self._id].get(ATTR_ACCESS_TOKEN)
return None return None
@property @property
def description(self): def description(self):
"""Return description of addon.""" """Return description of add-on."""
return self._mesh[ATTR_DESCRIPTON] return self._mesh[ATTR_DESCRIPTON]
@property @property
@ -200,31 +200,31 @@ class Addon(CoreSysAttributes):
@property @property
def repository(self): def repository(self):
"""Return repository of addon.""" """Return repository of add-on."""
return self._mesh[ATTR_REPOSITORY] return self._mesh[ATTR_REPOSITORY]
@property @property
def last_version(self): def last_version(self):
"""Return version of addon.""" """Return version of add-on."""
if self._id in self._data.cache: if self._id in self._data.cache:
return self._data.cache[self._id][ATTR_VERSION] return self._data.cache[self._id][ATTR_VERSION]
return self.version_installed return self.version_installed
@property @property
def protected(self): def protected(self):
"""Return if addon is in protected mode.""" """Return if add-on is in protected mode."""
if self.is_installed: if self.is_installed:
return self._data.user[self._id][ATTR_PROTECTED] return self._data.user[self._id][ATTR_PROTECTED]
return True return True
@protected.setter @protected.setter
def protected(self, value): def protected(self, value):
"""Set addon in protected mode.""" """Set add-on in protected mode."""
self._data.user[self._id][ATTR_PROTECTED] = value self._data.user[self._id][ATTR_PROTECTED] = value
@property @property
def startup(self): def startup(self):
"""Return startup type of addon.""" """Return startup type of add-on."""
return self._mesh.get(ATTR_STARTUP) return self._mesh.get(ATTR_STARTUP)
@property @property
@ -249,7 +249,7 @@ class Addon(CoreSysAttributes):
@property @property
def ports(self): def ports(self):
"""Return ports of addon.""" """Return ports of add-on."""
if self.host_network or ATTR_PORTS not in self._mesh: if self.host_network or ATTR_PORTS not in self._mesh:
return None return None
@ -260,7 +260,7 @@ class Addon(CoreSysAttributes):
@ports.setter @ports.setter
def ports(self, value): def ports(self, value):
"""Set custom ports of addon.""" """Set custom ports of add-on."""
if value is None: if value is None:
self._data.user[self._id].pop(ATTR_NETWORK, None) self._data.user[self._id].pop(ATTR_NETWORK, None)
else: else:
@ -304,42 +304,42 @@ class Addon(CoreSysAttributes):
@property @property
def host_network(self): def host_network(self):
"""Return True if addon run on host network.""" """Return True if add-on run on host network."""
return self._mesh[ATTR_HOST_NETWORK] return self._mesh[ATTR_HOST_NETWORK]
@property @property
def host_pid(self): def host_pid(self):
"""Return True if addon run on host PID namespace.""" """Return True if add-on run on host PID namespace."""
return self._mesh[ATTR_HOST_PID] return self._mesh[ATTR_HOST_PID]
@property @property
def host_ipc(self): def host_ipc(self):
"""Return True if addon run on host IPC namespace.""" """Return True if add-on run on host IPC namespace."""
return self._mesh[ATTR_HOST_IPC] return self._mesh[ATTR_HOST_IPC]
@property @property
def host_dbus(self): def host_dbus(self):
"""Return True if addon run on host DBUS.""" """Return True if add-on run on host D-BUS."""
return self._mesh[ATTR_HOST_DBUS] return self._mesh[ATTR_HOST_DBUS]
@property @property
def devices(self): def devices(self):
"""Return devices of addon.""" """Return devices of add-on."""
return self._mesh.get(ATTR_DEVICES) return self._mesh.get(ATTR_DEVICES)
@property @property
def auto_uart(self): def auto_uart(self):
"""Return True if we should map all uart device.""" """Return True if we should map all UART device."""
return self._mesh.get(ATTR_AUTO_UART) return self._mesh.get(ATTR_AUTO_UART)
@property @property
def tmpfs(self): def tmpfs(self):
"""Return tmpfs of addon.""" """Return tmpfs of add-on."""
return self._mesh.get(ATTR_TMPFS) return self._mesh.get(ATTR_TMPFS)
@property @property
def environment(self): def environment(self):
"""Return environment of addon.""" """Return environment of add-on."""
return self._mesh.get(ATTR_ENVIRONMENT) return self._mesh.get(ATTR_ENVIRONMENT)
@property @property
@ -349,7 +349,7 @@ class Addon(CoreSysAttributes):
@property @property
def apparmor(self): def apparmor(self):
"""Return True if apparmor is enabled.""" """Return True if AppArmor is enabled."""
if not self._mesh.get(ATTR_APPARMOR): if not self._mesh.get(ATTR_APPARMOR):
return SECURITY_DISABLE return SECURITY_DISABLE
elif self.sys_host.apparmor.exists(self.slug): elif self.sys_host.apparmor.exists(self.slug):
@ -358,22 +358,22 @@ class Addon(CoreSysAttributes):
@property @property
def legacy(self): def legacy(self):
"""Return if the add-on don't support hass labels.""" """Return if the add-on don't support Home Assistant labels."""
return self._mesh.get(ATTR_LEGACY) return self._mesh.get(ATTR_LEGACY)
@property @property
def access_docker_api(self): def access_docker_api(self):
"""Return if the add-on need read-only docker API access.""" """Return if the add-on need read-only Docker API access."""
return self._mesh.get(ATTR_DOCKER_API) return self._mesh.get(ATTR_DOCKER_API)
@property @property
def access_hassio_api(self): def access_hassio_api(self):
"""Return True if the add-on access to hassio api.""" """Return True if the add-on access to Hass.io REASTful API."""
return self._mesh[ATTR_HASSIO_API] return self._mesh[ATTR_HASSIO_API]
@property @property
def access_homeassistant_api(self): def access_homeassistant_api(self):
"""Return True if the add-on access to Home-Assistant api proxy.""" """Return True if the add-on access to Home Assistant API proxy."""
return self._mesh[ATTR_HOMEASSISTANT_API] return self._mesh[ATTR_HOMEASSISTANT_API]
@property @property
@ -388,7 +388,7 @@ class Addon(CoreSysAttributes):
@property @property
def with_gpio(self): def with_gpio(self):
"""Return True if the add-on access to gpio interface.""" """Return True if the add-on access to GPIO interface."""
return self._mesh[ATTR_GPIO] return self._mesh[ATTR_GPIO]
@property @property
@ -445,7 +445,7 @@ class Addon(CoreSysAttributes):
@property @property
def url(self): def url(self):
"""Return url of addon.""" """Return URL of add-on."""
return self._mesh.get(ATTR_URL) return self._mesh.get(ATTR_URL)
@property @property
@ -470,10 +470,10 @@ class Addon(CoreSysAttributes):
@property @property
def image(self): def image(self):
"""Return image name of addon.""" """Return image name of add-on."""
addon_data = self._mesh addon_data = self._mesh
# Repository with dockerhub images # Repository with Dockerhub images
if ATTR_IMAGE in addon_data: if ATTR_IMAGE in addon_data:
return addon_data[ATTR_IMAGE].format(arch=self.sys_arch) return addon_data[ATTR_IMAGE].format(arch=self.sys_arch)
@ -484,12 +484,12 @@ class Addon(CoreSysAttributes):
@property @property
def need_build(self): def need_build(self):
"""Return True if this addon need a local build.""" """Return True if this add-on need a local build."""
return ATTR_IMAGE not in self._mesh return ATTR_IMAGE not in self._mesh
@property @property
def map_volumes(self): def map_volumes(self):
"""Return a dict of {volume: policy} from addon.""" """Return a dict of {volume: policy} from add-on."""
volumes = {} volumes = {}
for volume in self._mesh[ATTR_MAP]: for volume in self._mesh[ATTR_MAP]:
result = RE_VOLUME.match(volume) result = RE_VOLUME.match(volume)
@ -499,37 +499,37 @@ class Addon(CoreSysAttributes):
@property @property
def path_data(self): def path_data(self):
"""Return addon data path inside supervisor.""" """Return add-on data path inside Supervisor."""
return Path(self.sys_config.path_addons_data, self._id) return Path(self.sys_config.path_addons_data, self._id)
@property @property
def path_extern_data(self): def path_extern_data(self):
"""Return addon data path external for docker.""" """Return add-on data path external for Docker."""
return PurePath(self.sys_config.path_extern_addons_data, self._id) return PurePath(self.sys_config.path_extern_addons_data, self._id)
@property @property
def path_options(self): def path_options(self):
"""Return path to addons options.""" """Return path to add-on options."""
return Path(self.path_data, "options.json") return Path(self.path_data, "options.json")
@property @property
def path_location(self): def path_location(self):
"""Return path to this addon.""" """Return path to this add-on."""
return Path(self._mesh[ATTR_LOCATON]) return Path(self._mesh[ATTR_LOCATON])
@property @property
def path_icon(self): def path_icon(self):
"""Return path to addon icon.""" """Return path to add-on icon."""
return Path(self.path_location, 'icon.png') return Path(self.path_location, 'icon.png')
@property @property
def path_logo(self): def path_logo(self):
"""Return path to addon logo.""" """Return path to add-on logo."""
return Path(self.path_location, 'logo.png') return Path(self.path_location, 'logo.png')
@property @property
def path_changelog(self): def path_changelog(self):
"""Return path to addon changelog.""" """Return path to add-on changelog."""
return Path(self.path_location, 'CHANGELOG.md') return Path(self.path_location, 'CHANGELOG.md')
@property @property
@ -544,15 +544,15 @@ class Addon(CoreSysAttributes):
@property @property
def path_extern_asound(self): def path_extern_asound(self):
"""Return path to asound config for docker.""" """Return path to asound config for Docker."""
return Path(self.sys_config.path_extern_tmp, f"{self.slug}_asound") return Path(self.sys_config.path_extern_tmp, f"{self.slug}_asound")
def save_data(self): def save_data(self):
"""Save data of addon.""" """Save data of add-on."""
self.sys_addons.data.save_data() self.sys_addons.data.save_data()
def write_options(self): def write_options(self):
"""Return True if addon options is written to data.""" """Return True if add-on options is written to data."""
schema = self.schema schema = self.schema
options = self.options options = self.options
@ -560,10 +560,10 @@ class Addon(CoreSysAttributes):
schema(options) schema(options)
write_json_file(self.path_options, options) write_json_file(self.path_options, options)
except vol.Invalid as ex: except vol.Invalid as ex:
_LOGGER.error("Addon %s have wrong options: %s", self._id, _LOGGER.error("Add-on %s have wrong options: %s", self._id,
humanize_error(options, ex)) humanize_error(options, ex))
except (OSError, json.JSONDecodeError) as err: except (OSError, json.JSONDecodeError) as err:
_LOGGER.error("Addon %s can't write options: %s", self._id, err) _LOGGER.error("Add-on %s can't write options: %s", self._id, err)
else: else:
return True return True
@ -578,7 +578,7 @@ class Addon(CoreSysAttributes):
with self.path_asound.open('w') as config_file: with self.path_asound.open('w') as config_file:
config_file.write(asound_config) config_file.write(asound_config)
except OSError as err: except OSError as err:
_LOGGER.error("Addon %s can't write asound: %s", self._id, err) _LOGGER.error("Add-on %s can't write asound: %s", self._id, err)
return False return False
return True return True
@ -606,7 +606,7 @@ class Addon(CoreSysAttributes):
@property @property
def schema(self): def schema(self):
"""Create a schema for addon options.""" """Create a schema for add-on options."""
raw_schema = self._mesh[ATTR_SCHEMA] raw_schema = self._mesh[ATTR_SCHEMA]
if isinstance(raw_schema, bool): if isinstance(raw_schema, bool):
@ -614,7 +614,7 @@ class Addon(CoreSysAttributes):
return vol.Schema(vol.All(dict, validate_options(raw_schema))) return vol.Schema(vol.All(dict, validate_options(raw_schema)))
def test_update_schema(self): def test_update_schema(self):
"""Check if the exists config valid after update.""" """Check if the existing configuration is valid after update."""
if not self.is_installed or self.is_detached: if not self.is_installed or self.is_detached:
return True return True
@ -644,19 +644,19 @@ class Addon(CoreSysAttributes):
return True return True
async def install(self): async def install(self):
"""Install an addon.""" """Install an add-on."""
if self.sys_arch not in self.supported_arch: if self.sys_arch not in self.supported_arch:
_LOGGER.error( _LOGGER.error(
"Addon %s not supported on %s", self._id, self.sys_arch) "Add-on %s not supported on %s", self._id, self.sys_arch)
return False return False
if self.is_installed: if self.is_installed:
_LOGGER.error("Addon %s is already installed", self._id) _LOGGER.error("Add-on %s is already installed", self._id)
return False return False
if not self.path_data.is_dir(): if not self.path_data.is_dir():
_LOGGER.info( _LOGGER.info(
"Create Home-Assistant addon data folder %s", self.path_data) "Create Home Assistant add-on data folder %s", self.path_data)
self.path_data.mkdir() self.path_data.mkdir()
# Setup/Fix AppArmor profile # Setup/Fix AppArmor profile
@ -670,13 +670,13 @@ class Addon(CoreSysAttributes):
@check_installed @check_installed
async def uninstall(self): async def uninstall(self):
"""Remove an addon.""" """Remove an add-on."""
if not await self.instance.remove(): if not await self.instance.remove():
return False return False
if self.path_data.is_dir(): if self.path_data.is_dir():
_LOGGER.info( _LOGGER.info(
"Remove Home-Assistant addon data folder %s", self.path_data) "Remove Home Assistant add-on data folder %s", self.path_data)
await remove_data(self.path_data) await remove_data(self.path_data)
# Cleanup audio settings # Cleanup audio settings
@ -684,7 +684,7 @@ class Addon(CoreSysAttributes):
with suppress(OSError): with suppress(OSError):
self.path_asound.unlink() self.path_asound.unlink()
# Cleanup apparmor profile # Cleanup AppArmor profile
if self.sys_host.apparmor.exists(self.slug): if self.sys_host.apparmor.exists(self.slug):
with suppress(HostAppArmorError): with suppress(HostAppArmorError):
await self.sys_host.apparmor.remove_profile(self.slug) await self.sys_host.apparmor.remove_profile(self.slug)
@ -693,7 +693,7 @@ class Addon(CoreSysAttributes):
return True return True
async def state(self): async def state(self):
"""Return running state of addon.""" """Return running state of add-on."""
if not self.is_installed: if not self.is_installed:
return STATE_NONE return STATE_NONE
@ -703,9 +703,9 @@ class Addon(CoreSysAttributes):
@check_installed @check_installed
async def start(self): async def start(self):
"""Set options and start addon.""" """Set options and start add-on."""
if await self.instance.is_running(): if await self.instance.is_running():
_LOGGER.warning("%s allready running!", self.slug) _LOGGER.warning("%s already running!", self.slug)
return return
# Access Token # Access Token
@ -724,7 +724,7 @@ class Addon(CoreSysAttributes):
@check_installed @check_installed
def stop(self): def stop(self):
"""Stop addon. """Stop add-on.
Return a coroutine. Return a coroutine.
""" """
@ -732,11 +732,11 @@ class Addon(CoreSysAttributes):
@check_installed @check_installed
async def update(self): async def update(self):
"""Update addon.""" """Update add-on."""
last_state = await self.state() last_state = await self.state()
if self.last_version == self.version_installed: if self.last_version == self.version_installed:
_LOGGER.warning("No update available for Addon %s", self._id) _LOGGER.warning("No update available for add-on %s", self._id)
return False return False
if not await self.instance.update(self.last_version): if not await self.instance.update(self.last_version):
@ -753,13 +753,13 @@ class Addon(CoreSysAttributes):
@check_installed @check_installed
async def restart(self): async def restart(self):
"""Restart addon.""" """Restart add-on."""
await self.stop() await self.stop()
return await self.start() return await self.start()
@check_installed @check_installed
def logs(self): def logs(self):
"""Return addons log output. """Return add-ons log output.
Return a coroutine. Return a coroutine.
""" """
@ -775,11 +775,11 @@ class Addon(CoreSysAttributes):
@check_installed @check_installed
async def rebuild(self): async def rebuild(self):
"""Performe a rebuild of local build addon.""" """Perform a rebuild of local build add-on."""
last_state = await self.state() last_state = await self.state()
if not self.need_build: if not self.need_build:
_LOGGER.error("Can't rebuild a none local build addon!") _LOGGER.error("Can't rebuild a none local build add-on!")
return False return False
# remove docker container but not addon config # remove docker container but not addon config
@ -808,7 +808,7 @@ class Addon(CoreSysAttributes):
@check_installed @check_installed
async def snapshot(self, tar_file): async def snapshot(self, tar_file):
"""Snapshot state of an addon.""" """Snapshot state of an add-on."""
with TemporaryDirectory(dir=str(self.sys_config.path_tmp)) as temp: with TemporaryDirectory(dir=str(self.sys_config.path_tmp)) as temp:
# store local image # store local image
if self.need_build and not await \ if self.need_build and not await \
@ -822,7 +822,7 @@ class Addon(CoreSysAttributes):
ATTR_STATE: await self.state(), ATTR_STATE: await self.state(),
} }
# store local configs/state # Store local configs/state
try: try:
write_json_file(Path(temp, 'addon.json'), data) write_json_file(Path(temp, 'addon.json'), data)
except (OSError, json.JSONDecodeError) as err: except (OSError, json.JSONDecodeError) as err:
@ -846,7 +846,7 @@ class Addon(CoreSysAttributes):
snapshot.add(self.path_data, arcname="data") snapshot.add(self.path_data, arcname="data")
try: try:
_LOGGER.info("Build snapshot for addon %s", self._id) _LOGGER.info("Build snapshot for add-on %s", self._id)
await self.sys_run_in_executor(_write_tarfile) await self.sys_run_in_executor(_write_tarfile)
except (tarfile.TarError, OSError) as err: except (tarfile.TarError, OSError) as err:
_LOGGER.error("Can't write tarfile %s: %s", tar_file, err) _LOGGER.error("Can't write tarfile %s: %s", tar_file, err)
@ -856,7 +856,7 @@ class Addon(CoreSysAttributes):
return True return True
async def restore(self, tar_file): async def restore(self, tar_file):
"""Restore state of an addon.""" """Restore state of an add-on."""
with TemporaryDirectory(dir=str(self.sys_config.path_tmp)) as temp: with TemporaryDirectory(dir=str(self.sys_config.path_tmp)) as temp:
# extract snapshot # extract snapshot
def _extract_tarfile(): def _extract_tarfile():
@ -870,13 +870,13 @@ class Addon(CoreSysAttributes):
_LOGGER.error("Can't read tarfile %s: %s", tar_file, err) _LOGGER.error("Can't read tarfile %s: %s", tar_file, err)
return False return False
# read snapshot data # Read snapshot data
try: try:
data = read_json_file(Path(temp, 'addon.json')) data = read_json_file(Path(temp, 'addon.json'))
except (OSError, json.JSONDecodeError) as err: except (OSError, json.JSONDecodeError) as err:
_LOGGER.error("Can't read addon.json: %s", err) _LOGGER.error("Can't read addon.json: %s", err)
# validate # Validate
try: try:
data = SCHEMA_ADDON_SNAPSHOT(data) data = SCHEMA_ADDON_SNAPSHOT(data)
except vol.Invalid as err: except vol.Invalid as err:
@ -884,11 +884,11 @@ class Addon(CoreSysAttributes):
self._id, humanize_error(data, err)) self._id, humanize_error(data, err))
return False return False
# restore data / reload addon # Restore data or reload add-on
_LOGGER.info("Restore config for addon %s", self._id) _LOGGER.info("Restore config for addon %s", self._id)
self._restore_data(data[ATTR_USER], data[ATTR_SYSTEM]) self._restore_data(data[ATTR_USER], data[ATTR_SYSTEM])
# check version / restore image # Check version / restore image
version = data[ATTR_VERSION] version = data[ATTR_VERSION]
if not await self.instance.exists(): if not await self.instance.exists():
_LOGGER.info("Restore image for addon %s", self._id) _LOGGER.info("Restore image for addon %s", self._id)
@ -902,7 +902,7 @@ class Addon(CoreSysAttributes):
else: else:
await self.instance.stop() await self.instance.stop()
# restore data # Restore data
def _restore_data(): def _restore_data():
"""Restore data.""" """Restore data."""
shutil.copytree(str(Path(temp, "data")), str(self.path_data)) shutil.copytree(str(Path(temp, "data")), str(self.path_data))
@ -926,9 +926,9 @@ class Addon(CoreSysAttributes):
_LOGGER.error("Can't restore AppArmor profile") _LOGGER.error("Can't restore AppArmor profile")
return False return False
# run addon # Run add-on
if data[ATTR_STATE] == STATE_STARTED: if data[ATTR_STATE] == STATE_STARTED:
return await self.start() return await self.start()
_LOGGER.info("Finish restore for addon %s", self._id) _LOGGER.info("Finish restore for add-on %s", self._id)
return True return True

View File

@ -1,4 +1,4 @@
"""HassIO addons build environment.""" """Hass.io add-on build environment."""
from pathlib import Path from pathlib import Path
from .validate import SCHEMA_BUILD_CONFIG, BASE_IMAGE from .validate import SCHEMA_BUILD_CONFIG, BASE_IMAGE
@ -8,10 +8,10 @@ from ..utils.json import JsonConfig
class AddonBuild(JsonConfig, CoreSysAttributes): class AddonBuild(JsonConfig, CoreSysAttributes):
"""Handle build options for addons.""" """Handle build options for add-ons."""
def __init__(self, coresys, slug): def __init__(self, coresys, slug):
"""Initialize addon builder.""" """Initialize Hass.io add-on builder."""
self.coresys = coresys self.coresys = coresys
self._id = slug self._id = slug
@ -24,12 +24,12 @@ class AddonBuild(JsonConfig, CoreSysAttributes):
@property @property
def addon(self): def addon(self):
"""Return addon of build data.""" """Return add-on of build data."""
return self.sys_addons.get(self._id) return self.sys_addons.get(self._id)
@property @property
def base_image(self): def base_image(self):
"""Base images for this addon.""" """Base images for this add-on."""
return self._data[ATTR_BUILD_FROM].get( return self._data[ATTR_BUILD_FROM].get(
self.sys_arch, BASE_IMAGE[self.sys_arch]) self.sys_arch, BASE_IMAGE[self.sys_arch])
@ -40,11 +40,11 @@ class AddonBuild(JsonConfig, CoreSysAttributes):
@property @property
def additional_args(self): def additional_args(self):
"""Return additional docker build arguments.""" """Return additional Docker build arguments."""
return self._data[ATTR_ARGS] return self._data[ATTR_ARGS]
def get_docker_args(self, version): def get_docker_args(self, version):
"""Create a dict with docker build arguments.""" """Create a dict with Docker build arguments."""
args = { args = {
'path': str(self.addon.path_location), 'path': str(self.addon.path_location),
'tag': f"{self.addon.image}:{version}", 'tag': f"{self.addon.image}:{version}",

View File

@ -1,4 +1,4 @@
"""Init file for HassIO addons.""" """Init file for Hass.io add-on data."""
import logging import logging
import json import json
from pathlib import Path from pathlib import Path
@ -19,7 +19,7 @@ _LOGGER = logging.getLogger(__name__)
class AddonsData(JsonConfig, CoreSysAttributes): class AddonsData(JsonConfig, CoreSysAttributes):
"""Hold data for addons inside HassIO.""" """Hold data for Add-ons inside Hass.io."""
def __init__(self, coresys): def __init__(self, coresys):
"""Initialize data holder.""" """Initialize data holder."""
@ -30,26 +30,26 @@ class AddonsData(JsonConfig, CoreSysAttributes):
@property @property
def user(self): def user(self):
"""Return local addon user data.""" """Return local add-on user data."""
return self._data[ATTR_USER] return self._data[ATTR_USER]
@property @property
def system(self): def system(self):
"""Return local addon data.""" """Return local add-on data."""
return self._data[ATTR_SYSTEM] return self._data[ATTR_SYSTEM]
@property @property
def cache(self): def cache(self):
"""Return addon data from cache/repositories.""" """Return add-on data from cache/repositories."""
return self._cache return self._cache
@property @property
def repositories(self): def repositories(self):
"""Return addon data from repositories.""" """Return add-on data from repositories."""
return self._repositories return self._repositories
def reload(self): def reload(self):
"""Read data from addons repository.""" """Read data from add-on repository."""
self._cache = {} self._cache = {}
self._repositories = {} self._repositories = {}
@ -94,7 +94,7 @@ class AddonsData(JsonConfig, CoreSysAttributes):
self._read_addons_folder(path, slug) self._read_addons_folder(path, slug)
def _read_addons_folder(self, path, repository): def _read_addons_folder(self, path, repository):
"""Read data from addons folder.""" """Read data from add-ons folder."""
for addon in path.glob("**/config.json"): for addon in path.glob("**/config.json"):
try: try:
addon_config = read_json_file(addon) addon_config = read_json_file(addon)

View File

@ -1,4 +1,4 @@
"""Init file for HassIO addons git.""" """Init file for Hass.io add-on Git."""
import asyncio import asyncio
import logging import logging
import functools as ft import functools as ft
@ -16,10 +16,10 @@ _LOGGER = logging.getLogger(__name__)
class GitRepo(CoreSysAttributes): class GitRepo(CoreSysAttributes):
"""Manage addons git repo.""" """Manage Add-on Git repository."""
def __init__(self, coresys, path, url): def __init__(self, coresys, path, url):
"""Initialize git base wrapper.""" """Initialize Git base wrapper."""
self.coresys = coresys self.coresys = coresys
self.repo = None self.repo = None
self.path = path self.path = path
@ -38,13 +38,13 @@ class GitRepo(CoreSysAttributes):
return self._data[ATTR_BRANCH] return self._data[ATTR_BRANCH]
async def load(self): async def load(self):
"""Init git addon repo.""" """Init Git add-on repository."""
if not self.path.is_dir(): if not self.path.is_dir():
return await self.clone() return await self.clone()
async with self.lock: async with self.lock:
try: try:
_LOGGER.info("Load addon %s repository", self.path) _LOGGER.info("Load add-on %s repository", self.path)
self.repo = await self.sys_run_in_executor( self.repo = await self.sys_run_in_executor(
git.Repo, str(self.path)) git.Repo, str(self.path))
@ -57,7 +57,7 @@ class GitRepo(CoreSysAttributes):
return True return True
async def clone(self): async def clone(self):
"""Clone git addon repo.""" """Clone git add-on repository."""
async with self.lock: async with self.lock:
git_args = { git_args = {
attribute: value attribute: value
@ -70,7 +70,7 @@ class GitRepo(CoreSysAttributes):
} }
try: try:
_LOGGER.info("Clone addon %s repository", self.url) _LOGGER.info("Clone add-on %s repository", self.url)
self.repo = await self.sys_run_in_executor(ft.partial( self.repo = await self.sys_run_in_executor(ft.partial(
git.Repo.clone_from, self.url, str(self.path), git.Repo.clone_from, self.url, str(self.path),
**git_args **git_args
@ -78,20 +78,20 @@ class GitRepo(CoreSysAttributes):
except (git.InvalidGitRepositoryError, git.NoSuchPathError, except (git.InvalidGitRepositoryError, git.NoSuchPathError,
git.GitCommandError) as err: git.GitCommandError) as err:
_LOGGER.error("Can't clone %s repo: %s.", self.url, err) _LOGGER.error("Can't clone %s repository: %s.", self.url, err)
self._remove() self._remove()
return False return False
return True return True
async def pull(self): async def pull(self):
"""Pull git addon repo.""" """Pull Git add-on repo."""
if self.lock.locked(): if self.lock.locked():
_LOGGER.warning("It is already a task in progress.") _LOGGER.warning("It is already a task in progress")
return False return False
async with self.lock: async with self.lock:
_LOGGER.info("Update addon %s repository", self.url) _LOGGER.info("Update add-on %s repository", self.url)
branch = self.repo.active_branch.name branch = self.repo.active_branch.name
try: try:
@ -130,19 +130,19 @@ class GitRepo(CoreSysAttributes):
class GitRepoHassIO(GitRepo): class GitRepoHassIO(GitRepo):
"""HassIO addons repository.""" """Hass.io add-ons repository."""
def __init__(self, coresys): def __init__(self, coresys):
"""Initialize git hassio addon repository.""" """Initialize Git Hass.io add-on repository."""
super().__init__( super().__init__(
coresys, coresys.config.path_addons_core, URL_HASSIO_ADDONS) coresys, coresys.config.path_addons_core, URL_HASSIO_ADDONS)
class GitRepoCustom(GitRepo): class GitRepoCustom(GitRepo):
"""Custom addons repository.""" """Custom add-ons repository."""
def __init__(self, coresys, url): def __init__(self, coresys, url):
"""Initialize git hassio addon repository.""" """Initialize custom Git Hass.io addo-n repository."""
path = Path( path = Path(
coresys.config.path_addons_git, coresys.config.path_addons_git,
get_hash_from_repository(url)) get_hash_from_repository(url))
@ -151,5 +151,5 @@ class GitRepoCustom(GitRepo):
def remove(self): def remove(self):
"""Remove a custom repository.""" """Remove a custom repository."""
_LOGGER.info("Remove custom addon repository %s", self.url) _LOGGER.info("Remove custom add-on repository %s", self.url)
self._remove() self._remove()

View File

@ -1,4 +1,4 @@
"""Represent a HassIO repository.""" """Represent a Hass.io repository."""
from .git import GitRepoHassIO, GitRepoCustom from .git import GitRepoHassIO, GitRepoCustom
from .utils import get_hash_from_repository from .utils import get_hash_from_repository
from ..const import ( from ..const import (
@ -9,7 +9,7 @@ UNKNOWN = 'unknown'
class Repository(CoreSysAttributes): class Repository(CoreSysAttributes):
"""Repository in HassIO.""" """Repository in Hass.io."""
def __init__(self, coresys, repository): def __init__(self, coresys, repository):
"""Initialize repository object.""" """Initialize repository object."""
@ -44,7 +44,7 @@ class Repository(CoreSysAttributes):
@property @property
def url(self): def url(self):
"""Return url of repository.""" """Return URL of repository."""
return self._mesh.get(ATTR_URL, self.source) return self._mesh.get(ATTR_URL, self.source)
@property @property
@ -59,13 +59,13 @@ class Repository(CoreSysAttributes):
return True return True
async def update(self): async def update(self):
"""Update addon repository.""" """Update add-on repository."""
if self.git: if self.git:
return await self.git.pull() return await self.git.pull()
return True return True
def remove(self): def remove(self):
"""Remove addon repository.""" """Remove add-on repository."""
if self._id in (REPOSITORY_CORE, REPOSITORY_LOCAL): if self._id in (REPOSITORY_CORE, REPOSITORY_LOCAL):
raise RuntimeError("Can't remove built-in repositories!") raise RuntimeError("Can't remove built-in repositories!")

View File

@ -1,4 +1,4 @@
"""Util addons functions.""" """Util add-ons functions."""
import asyncio import asyncio
import hashlib import hashlib
import logging import logging
@ -78,7 +78,7 @@ def extract_hash_from_path(path):
def check_installed(method): def check_installed(method):
"""Wrap function with check if addon is installed.""" """Wrap function with check if add-on is installed."""
async def wrap_check(addon, *args, **kwargs): async def wrap_check(addon, *args, **kwargs):
"""Return False if not installed or the function.""" """Return False if not installed or the function."""
if not addon.is_installed: if not addon.is_installed:

View File

@ -1,4 +1,4 @@
"""Validate addons options schema.""" """Validate add-ons options schema."""
import logging import logging
import re import re
import uuid import uuid
@ -218,7 +218,7 @@ SCHEMA_ADDON_SNAPSHOT = vol.Schema({
def validate_options(raw_schema): def validate_options(raw_schema):
"""Validate schema.""" """Validate schema."""
def validate(struct): def validate(struct):
"""Create schema validator for addons options.""" """Create schema validator for add-ons options."""
options = {} options = {}
# read options # read options

View File

@ -1,4 +1,4 @@
"""Init file for HassIO rest api.""" """Init file for Hass.io RESTful API."""
import logging import logging
from pathlib import Path from pathlib import Path
@ -21,10 +21,10 @@ _LOGGER = logging.getLogger(__name__)
class RestAPI(CoreSysAttributes): class RestAPI(CoreSysAttributes):
"""Handle rest api for hassio.""" """Handle RESTful API for Hass.io."""
def __init__(self, coresys): def __init__(self, coresys):
"""Initialize docker base wrapper.""" """Initialize Docker base wrapper."""
self.coresys = coresys self.coresys = coresys
self.security = SecurityMiddleware(coresys) self.security = SecurityMiddleware(coresys)
self.webapp = web.Application( self.webapp = web.Application(
@ -49,7 +49,7 @@ class RestAPI(CoreSysAttributes):
self._register_services() self._register_services()
def _register_host(self): def _register_host(self):
"""Register hostcontrol function.""" """Register hostcontrol functions."""
api_host = APIHost() api_host = APIHost()
api_host.coresys = self.coresys api_host.coresys = self.coresys
@ -69,7 +69,7 @@ class RestAPI(CoreSysAttributes):
]) ])
def _register_hassos(self): def _register_hassos(self):
"""Register hassos function.""" """Register HassOS functions."""
api_hassos = APIHassOS() api_hassos = APIHassOS()
api_hassos.coresys = self.coresys api_hassos.coresys = self.coresys
@ -81,7 +81,7 @@ class RestAPI(CoreSysAttributes):
]) ])
def _register_hardware(self): def _register_hardware(self):
"""Register hardware function.""" """Register hardware functions."""
api_hardware = APIHardware() api_hardware = APIHardware()
api_hardware.coresys = self.coresys api_hardware.coresys = self.coresys
@ -91,7 +91,7 @@ class RestAPI(CoreSysAttributes):
]) ])
def _register_supervisor(self): def _register_supervisor(self):
"""Register supervisor function.""" """Register Supervisor functions."""
api_supervisor = APISupervisor() api_supervisor = APISupervisor()
api_supervisor.coresys = self.coresys api_supervisor.coresys = self.coresys
@ -106,7 +106,7 @@ class RestAPI(CoreSysAttributes):
]) ])
def _register_homeassistant(self): def _register_homeassistant(self):
"""Register homeassistant function.""" """Register Home Assistant functions."""
api_hass = APIHomeAssistant() api_hass = APIHomeAssistant()
api_hass.coresys = self.coresys api_hass.coresys = self.coresys
@ -123,7 +123,7 @@ class RestAPI(CoreSysAttributes):
]) ])
def _register_proxy(self): def _register_proxy(self):
"""Register HomeAssistant API Proxy.""" """Register Home Assistant API Proxy."""
api_proxy = APIProxy() api_proxy = APIProxy()
api_proxy.coresys = self.coresys api_proxy.coresys = self.coresys
@ -137,7 +137,7 @@ class RestAPI(CoreSysAttributes):
]) ])
def _register_addons(self): def _register_addons(self):
"""Register homeassistant function.""" """Register Add-on functions."""
api_addons = APIAddons() api_addons = APIAddons()
api_addons.coresys = self.coresys api_addons.coresys = self.coresys
@ -163,7 +163,7 @@ class RestAPI(CoreSysAttributes):
]) ])
def _register_snapshots(self): def _register_snapshots(self):
"""Register snapshots function.""" """Register snapshots functions."""
api_snapshots = APISnapshots() api_snapshots = APISnapshots()
api_snapshots.coresys = self.coresys api_snapshots.coresys = self.coresys
@ -183,6 +183,7 @@ class RestAPI(CoreSysAttributes):
]) ])
def _register_services(self): def _register_services(self):
"""Register services functions."""
api_services = APIServices() api_services = APIServices()
api_services.coresys = self.coresys api_services.coresys = self.coresys
@ -194,6 +195,7 @@ class RestAPI(CoreSysAttributes):
]) ])
def _register_discovery(self): def _register_discovery(self):
"""Register discovery functions."""
api_discovery = APIDiscovery() api_discovery = APIDiscovery()
api_discovery.coresys = self.coresys api_discovery.coresys = self.coresys
@ -206,7 +208,7 @@ class RestAPI(CoreSysAttributes):
]) ])
def _register_panel(self): def _register_panel(self):
"""Register panel for homeassistant.""" """Register panel for Home Assistant."""
panel_dir = Path(__file__).parent.joinpath("panel") panel_dir = Path(__file__).parent.joinpath("panel")
def create_response(panel_file): def create_response(panel_file):
@ -234,7 +236,7 @@ class RestAPI(CoreSysAttributes):
self.webapp.add_routes([web.static('/app', panel_dir)]) self.webapp.add_routes([web.static('/app', panel_dir)])
async def start(self): async def start(self):
"""Run rest api webserver.""" """Run RESTful API webserver."""
await self._runner.setup() await self._runner.setup()
self._site = web.TCPSite( self._site = web.TCPSite(
self._runner, host="0.0.0.0", port=80, shutdown_timeout=5) self._runner, host="0.0.0.0", port=80, shutdown_timeout=5)
@ -248,7 +250,7 @@ class RestAPI(CoreSysAttributes):
_LOGGER.info("Start API on %s", self.sys_docker.network.supervisor) _LOGGER.info("Start API on %s", self.sys_docker.network.supervisor)
async def stop(self): async def stop(self):
"""Stop rest api webserver.""" """Stop RESTful API webserver."""
if not self._site: if not self._site:
return return

View File

@ -1,4 +1,4 @@
"""Init file for HassIO homeassistant rest api.""" """Init file for Hass.io Home Assistant RESTful API."""
import asyncio import asyncio
import logging import logging
@ -48,7 +48,7 @@ SCHEMA_SECURITY = vol.Schema({
class APIAddons(CoreSysAttributes): class APIAddons(CoreSysAttributes):
"""Handle rest api for addons functions.""" """Handle RESTful API for add-on functions."""
def _extract_addon(self, request, check_installed=True): def _extract_addon(self, request, check_installed=True):
"""Return addon, throw an exception it it doesn't exist.""" """Return addon, throw an exception it it doesn't exist."""
@ -77,7 +77,7 @@ class APIAddons(CoreSysAttributes):
@api_process @api_process
async def list(self, request): async def list(self, request):
"""Return all addons / repositories .""" """Return all add-ons or repositories."""
data_addons = [] data_addons = []
for addon in self.sys_addons.list_addons: for addon in self.sys_addons.list_addons:
data_addons.append({ data_addons.append({
@ -112,13 +112,13 @@ class APIAddons(CoreSysAttributes):
@api_process @api_process
async def reload(self, request): async def reload(self, request):
"""Reload all addons data.""" """Reload all add-on data."""
await asyncio.shield(self.sys_addons.reload()) await asyncio.shield(self.sys_addons.reload())
return True return True
@api_process @api_process
async def info(self, request): async def info(self, request):
"""Return addon information.""" """Return add-on information."""
addon = self._extract_addon(request, check_installed=False) addon = self._extract_addon(request, check_installed=False)
return { return {
@ -167,7 +167,7 @@ class APIAddons(CoreSysAttributes):
@api_process @api_process
async def options(self, request): async def options(self, request):
"""Store user options for addon.""" """Store user options for add-on."""
addon = self._extract_addon(request) addon = self._extract_addon(request)
addon_schema = SCHEMA_OPTIONS.extend({ addon_schema = SCHEMA_OPTIONS.extend({
@ -194,7 +194,7 @@ class APIAddons(CoreSysAttributes):
@api_process @api_process
async def security(self, request): async def security(self, request):
"""Store security options for addon.""" """Store security options for add-on."""
addon = self._extract_addon(request) addon = self._extract_addon(request)
# Have Access # Have Access
@ -233,19 +233,19 @@ class APIAddons(CoreSysAttributes):
@api_process @api_process
def install(self, request): def install(self, request):
"""Install addon.""" """Install add-on."""
addon = self._extract_addon(request, check_installed=False) addon = self._extract_addon(request, check_installed=False)
return asyncio.shield(addon.install()) return asyncio.shield(addon.install())
@api_process @api_process
def uninstall(self, request): def uninstall(self, request):
"""Uninstall addon.""" """Uninstall add-on."""
addon = self._extract_addon(request) addon = self._extract_addon(request)
return asyncio.shield(addon.uninstall()) return asyncio.shield(addon.uninstall())
@api_process @api_process
def start(self, request): def start(self, request):
"""Start addon.""" """Start add-on."""
addon = self._extract_addon(request) addon = self._extract_addon(request)
# check options # check options
@ -259,13 +259,13 @@ class APIAddons(CoreSysAttributes):
@api_process @api_process
def stop(self, request): def stop(self, request):
"""Stop addon.""" """Stop add-on."""
addon = self._extract_addon(request) addon = self._extract_addon(request)
return asyncio.shield(addon.stop()) return asyncio.shield(addon.stop())
@api_process @api_process
def update(self, request): def update(self, request):
"""Update addon.""" """Update add-on."""
addon = self._extract_addon(request) addon = self._extract_addon(request)
if addon.last_version == addon.version_installed: if addon.last_version == addon.version_installed:
@ -275,13 +275,13 @@ class APIAddons(CoreSysAttributes):
@api_process @api_process
def restart(self, request): def restart(self, request):
"""Restart addon.""" """Restart add-on."""
addon = self._extract_addon(request) addon = self._extract_addon(request)
return asyncio.shield(addon.restart()) return asyncio.shield(addon.restart())
@api_process @api_process
def rebuild(self, request): def rebuild(self, request):
"""Rebuild local build addon.""" """Rebuild local build add-on."""
addon = self._extract_addon(request) addon = self._extract_addon(request)
if not addon.need_build: if not addon.need_build:
raise RuntimeError("Only local build addons are supported") raise RuntimeError("Only local build addons are supported")
@ -290,13 +290,13 @@ class APIAddons(CoreSysAttributes):
@api_process_raw(CONTENT_TYPE_BINARY) @api_process_raw(CONTENT_TYPE_BINARY)
def logs(self, request): def logs(self, request):
"""Return logs from addon.""" """Return logs from add-on."""
addon = self._extract_addon(request) addon = self._extract_addon(request)
return addon.logs() return addon.logs()
@api_process_raw(CONTENT_TYPE_PNG) @api_process_raw(CONTENT_TYPE_PNG)
async def icon(self, request): async def icon(self, request):
"""Return icon from addon.""" """Return icon from add-on."""
addon = self._extract_addon(request, check_installed=False) addon = self._extract_addon(request, check_installed=False)
if not addon.with_icon: if not addon.with_icon:
raise RuntimeError("No icon found!") raise RuntimeError("No icon found!")
@ -306,7 +306,7 @@ class APIAddons(CoreSysAttributes):
@api_process_raw(CONTENT_TYPE_PNG) @api_process_raw(CONTENT_TYPE_PNG)
async def logo(self, request): async def logo(self, request):
"""Return logo from addon.""" """Return logo from add-on."""
addon = self._extract_addon(request, check_installed=False) addon = self._extract_addon(request, check_installed=False)
if not addon.with_logo: if not addon.with_logo:
raise RuntimeError("No logo found!") raise RuntimeError("No logo found!")
@ -316,7 +316,7 @@ class APIAddons(CoreSysAttributes):
@api_process_raw(CONTENT_TYPE_TEXT) @api_process_raw(CONTENT_TYPE_TEXT)
async def changelog(self, request): async def changelog(self, request):
"""Return changelog from addon.""" """Return changelog from add-on."""
addon = self._extract_addon(request, check_installed=False) addon = self._extract_addon(request, check_installed=False)
if not addon.with_changelog: if not addon.with_changelog:
raise RuntimeError("No changelog found!") raise RuntimeError("No changelog found!")
@ -326,10 +326,10 @@ class APIAddons(CoreSysAttributes):
@api_process @api_process
async def stdin(self, request): async def stdin(self, request):
"""Write to stdin of addon.""" """Write to stdin of add-on."""
addon = self._extract_addon(request) addon = self._extract_addon(request)
if not addon.with_stdin: if not addon.with_stdin:
raise RuntimeError("STDIN not supported by addon") raise RuntimeError("STDIN not supported by add-on")
data = await request.read() data = await request.read()
return await asyncio.shield(addon.write_stdin(data)) return await asyncio.shield(addon.write_stdin(data))

View File

@ -1,5 +1,4 @@
"""Init file for HassIO network rest api.""" """Init file for Hass.io network RESTful API."""
import voluptuous as vol import voluptuous as vol
from .utils import api_process, api_validate from .utils import api_process, api_validate
@ -17,7 +16,7 @@ SCHEMA_DISCOVERY = vol.Schema({
class APIDiscovery(CoreSysAttributes): class APIDiscovery(CoreSysAttributes):
"""Handle rest api for discovery functions.""" """Handle RESTful API for discovery functions."""
def _extract_message(self, request): def _extract_message(self, request):
"""Extract discovery message from URL.""" """Extract discovery message from URL."""

View File

@ -1,4 +1,4 @@
"""Init file for HassIO hardware rest api.""" """Init file for Hass.io hardware RESTful API."""
import logging import logging
from .utils import api_process from .utils import api_process
@ -10,7 +10,7 @@ _LOGGER = logging.getLogger(__name__)
class APIHardware(CoreSysAttributes): class APIHardware(CoreSysAttributes):
"""Handle rest api for hardware functions.""" """Handle RESTful API for hardware functions."""
@api_process @api_process
async def info(self, request): async def info(self, request):

View File

@ -1,4 +1,4 @@
"""Init file for Hass.io hassos rest api.""" """Init file for Hass.io HassOS RESTful API."""
import asyncio import asyncio
import logging import logging
@ -18,11 +18,11 @@ SCHEMA_VERSION = vol.Schema({
class APIHassOS(CoreSysAttributes): class APIHassOS(CoreSysAttributes):
"""Handle rest api for hassos functions.""" """Handle RESTful API for HassOS functions."""
@api_process @api_process
async def info(self, request): async def info(self, request):
"""Return hassos information.""" """Return HassOS information."""
return { return {
ATTR_VERSION: self.sys_hassos.version, ATTR_VERSION: self.sys_hassos.version,
ATTR_VERSION_CLI: self.sys_hassos.version_cli, ATTR_VERSION_CLI: self.sys_hassos.version_cli,

View File

@ -1,4 +1,4 @@
"""Init file for HassIO homeassistant rest api.""" """Init file for Hass.io Home Assistant RESTful API."""
import asyncio import asyncio
import logging import logging
@ -39,7 +39,7 @@ SCHEMA_VERSION = vol.Schema({
class APIHomeAssistant(CoreSysAttributes): class APIHomeAssistant(CoreSysAttributes):
"""Handle rest api for homeassistant functions.""" """Handle RESTful API for Home Assistant functions."""
@api_process @api_process
async def info(self, request): async def info(self, request):
@ -59,7 +59,7 @@ class APIHomeAssistant(CoreSysAttributes):
@api_process @api_process
async def options(self, request): async def options(self, request):
"""Set homeassistant options.""" """Set Home Assistant options."""
body = await api_validate(SCHEMA_OPTIONS, request) body = await api_validate(SCHEMA_OPTIONS, request)
if ATTR_IMAGE in body and ATTR_LAST_VERSION in body: if ATTR_IMAGE in body and ATTR_LAST_VERSION in body:
@ -108,7 +108,7 @@ class APIHomeAssistant(CoreSysAttributes):
@api_process @api_process
async def update(self, request): async def update(self, request):
"""Update homeassistant.""" """Update Home Assistant."""
body = await api_validate(SCHEMA_VERSION, request) body = await api_validate(SCHEMA_VERSION, request)
version = body.get(ATTR_VERSION, self.sys_homeassistant.last_version) version = body.get(ATTR_VERSION, self.sys_homeassistant.last_version)
@ -116,27 +116,27 @@ class APIHomeAssistant(CoreSysAttributes):
@api_process @api_process
def stop(self, request): def stop(self, request):
"""Stop homeassistant.""" """Stop Home Assistant."""
return asyncio.shield(self.sys_homeassistant.stop()) return asyncio.shield(self.sys_homeassistant.stop())
@api_process @api_process
def start(self, request): def start(self, request):
"""Start homeassistant.""" """Start Home Assistant."""
return asyncio.shield(self.sys_homeassistant.start()) return asyncio.shield(self.sys_homeassistant.start())
@api_process @api_process
def restart(self, request): def restart(self, request):
"""Restart homeassistant.""" """Restart Home Assistant."""
return asyncio.shield(self.sys_homeassistant.restart()) return asyncio.shield(self.sys_homeassistant.restart())
@api_process_raw(CONTENT_TYPE_BINARY) @api_process_raw(CONTENT_TYPE_BINARY)
def logs(self, request): def logs(self, request):
"""Return homeassistant docker logs.""" """Return Home Assistant Docker logs."""
return self.sys_homeassistant.logs() return self.sys_homeassistant.logs()
@api_process @api_process
async def check(self, request): async def check(self, request):
"""Check config of homeassistant.""" """Check configuration of Home Assistant."""
result = await self.sys_homeassistant.check_config() result = await self.sys_homeassistant.check_config()
if not result.valid: if not result.valid:
raise RuntimeError(result.log) raise RuntimeError(result.log)

View File

@ -1,4 +1,4 @@
"""Init file for HassIO host rest api.""" """Init file for Hass.io host RESTful API."""
import asyncio import asyncio
import logging import logging
@ -21,7 +21,7 @@ SCHEMA_OPTIONS = vol.Schema({
class APIHost(CoreSysAttributes): class APIHost(CoreSysAttributes):
"""Handle rest api for host functions.""" """Handle RESTful API for host functions."""
@api_process @api_process
async def info(self, request): async def info(self, request):

View File

@ -1,4 +1,4 @@
"""Utils for HomeAssistant Proxy.""" """Utils for Home Assistant Proxy."""
import asyncio import asyncio
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
import logging import logging
@ -18,7 +18,7 @@ _LOGGER = logging.getLogger(__name__)
class APIProxy(CoreSysAttributes): class APIProxy(CoreSysAttributes):
"""API Proxy for Home-Assistant.""" """API Proxy for Home Assistant."""
def _check_access(self, request): def _check_access(self, request):
"""Check the Hass.io token.""" """Check the Hass.io token."""
@ -30,7 +30,7 @@ class APIProxy(CoreSysAttributes):
addon = self.sys_addons.from_uuid(hassio_token) addon = self.sys_addons.from_uuid(hassio_token)
if not addon: if not addon:
_LOGGER.warning("Unknown HomeAssistant API access!") _LOGGER.warning("Unknown Home Assistant API access!")
elif not addon.access_homeassistant_api: elif not addon.access_homeassistant_api:
_LOGGER.warning("Not permitted API access: %s", addon.slug) _LOGGER.warning("Not permitted API access: %s", addon.slug)
else: else:
@ -41,7 +41,7 @@ class APIProxy(CoreSysAttributes):
@asynccontextmanager @asynccontextmanager
async def _api_client(self, request, path, timeout=300): async def _api_client(self, request, path, timeout=300):
"""Return a client request with proxy origin for Home-Assistant.""" """Return a client request with proxy origin for Home Assistant."""
try: try:
# read data # read data
with async_timeout.timeout(30): with async_timeout.timeout(30):
@ -76,7 +76,7 @@ class APIProxy(CoreSysAttributes):
"""Proxy HomeAssistant EventStream Requests.""" """Proxy HomeAssistant EventStream Requests."""
self._check_access(request) self._check_access(request)
_LOGGER.info("Home-Assistant EventStream start") _LOGGER.info("Home Assistant EventStream start")
async with self._api_client(request, 'stream', timeout=None) as client: async with self._api_client(request, 'stream', timeout=None) as client:
response = web.StreamResponse() response = web.StreamResponse()
response.content_type = request.headers.get(CONTENT_TYPE) response.content_type = request.headers.get(CONTENT_TYPE)
@ -93,12 +93,12 @@ class APIProxy(CoreSysAttributes):
finally: finally:
client.close() client.close()
_LOGGER.info("Home-Assistant EventStream close") _LOGGER.info("Home Assistant EventStream close")
return response return response
async def api(self, request): async def api(self, request):
"""Proxy HomeAssistant API Requests.""" """Proxy Home Assistant API Requests."""
self._check_access(request) self._check_access(request)
# Normal request # Normal request
@ -112,14 +112,14 @@ class APIProxy(CoreSysAttributes):
) )
async def _websocket_client(self): async def _websocket_client(self):
"""Initialize a websocket api connection.""" """Initialize a WebSocket API connection."""
url = f"{self.sys_homeassistant.api_url}/api/websocket" url = f"{self.sys_homeassistant.api_url}/api/websocket"
try: try:
client = await self.sys_websession_ssl.ws_connect( client = await self.sys_websession_ssl.ws_connect(
url, heartbeat=60, verify_ssl=False) url, heartbeat=60, verify_ssl=False)
# handle authentication # Handle authentication
data = await client.receive_json() data = await client.receive_json()
if data.get('type') == 'auth_ok': if data.get('type') == 'auth_ok':
@ -128,7 +128,7 @@ class APIProxy(CoreSysAttributes):
if data.get('type') != 'auth_required': if data.get('type') != 'auth_required':
# Invalid protocol # Invalid protocol
_LOGGER.error( _LOGGER.error(
'Got unexpected response from HA websocket: %s', data) "Got unexpected response from HA WebSocket: %s", data)
raise HTTPBadGateway() raise HTTPBadGateway()
if self.sys_homeassistant.refresh_token: if self.sys_homeassistant.refresh_token:
@ -157,15 +157,15 @@ class APIProxy(CoreSysAttributes):
raise HomeAssistantAuthError() raise HomeAssistantAuthError()
except (RuntimeError, ValueError) as err: except (RuntimeError, ValueError) as err:
_LOGGER.error("Client error on websocket API %s.", err) _LOGGER.error("Client error on WebSocket API %s.", err)
except HomeAssistantAuthError as err: except HomeAssistantAuthError as err:
_LOGGER.error("Failed authentication to HomeAssistant websocket") _LOGGER.error("Failed authentication to Home Assistant WebSocket")
raise HTTPBadGateway() raise HTTPBadGateway()
async def websocket(self, request): async def websocket(self, request):
"""Initialize a websocket api connection.""" """Initialize a WebSocket API connection."""
_LOGGER.info("Home-Assistant Websocket API request initialze") _LOGGER.info("Home Assistant WebSocket API request initialize")
# init server # init server
server = web.WebSocketResponse(heartbeat=60) server = web.WebSocketResponse(heartbeat=60)
@ -189,14 +189,14 @@ class APIProxy(CoreSysAttributes):
addon = self.sys_addons.from_uuid(hassio_token) addon = self.sys_addons.from_uuid(hassio_token)
if not addon or not addon.access_homeassistant_api: if not addon or not addon.access_homeassistant_api:
_LOGGER.warning("Unauthorized websocket access!") _LOGGER.warning("Unauthorized WebSocket access!")
await server.send_json({ await server.send_json({
'type': 'auth_invalid', 'type': 'auth_invalid',
'message': 'Invalid access', 'message': 'Invalid access',
}) })
return server return server
_LOGGER.info("Websocket access from %s", addon.slug) _LOGGER.info("WebSocket access from %s", addon.slug)
await server.send_json({ await server.send_json({
'type': 'auth_ok', 'type': 'auth_ok',
@ -209,7 +209,7 @@ class APIProxy(CoreSysAttributes):
# init connection to hass # init connection to hass
client = await self._websocket_client() client = await self._websocket_client()
_LOGGER.info("Home-Assistant Websocket API request running") _LOGGER.info("Home Assistant WebSocket API request running")
try: try:
client_read = None client_read = None
server_read = None server_read = None
@ -243,7 +243,7 @@ class APIProxy(CoreSysAttributes):
pass pass
except RuntimeError as err: except RuntimeError as err:
_LOGGER.info("Home-Assistant Websocket API error: %s", err) _LOGGER.info("Home Assistant WebSocket API error: %s", err)
finally: finally:
if client_read: if client_read:
@ -255,5 +255,5 @@ class APIProxy(CoreSysAttributes):
await client.close() await client.close()
await server.close() await server.close()
_LOGGER.info("Home-Assistant Websocket API connection is closed") _LOGGER.info("Home Assistant WebSocket API connection is closed")
return server return server

View File

@ -88,7 +88,7 @@ class SecurityMiddleware(CoreSysAttributes):
# UUID check need removed with 131 # UUID check need removed with 131
if hassio_token in (self.sys_homeassistant.uuid, if hassio_token in (self.sys_homeassistant.uuid,
self.sys_homeassistant.hassio_token): self.sys_homeassistant.hassio_token):
_LOGGER.debug("%s access from Home-Assistant", request.path) _LOGGER.debug("%s access from Home Assistant", request.path)
request_from = 'homeassistant' request_from = 'homeassistant'
# Host # Host

View File

@ -1,4 +1,4 @@
"""Init file for HassIO network rest api.""" """Init file for Hass.io network RESTful API."""
from .utils import api_process, api_validate from .utils import api_process, api_validate
from ..const import ( from ..const import (
@ -7,7 +7,7 @@ from ..coresys import CoreSysAttributes
class APIServices(CoreSysAttributes): class APIServices(CoreSysAttributes):
"""Handle rest api for services functions.""" """Handle RESTful API for services functions."""
def _extract_service(self, request): def _extract_service(self, request):
"""Return service, throw an exception if it doesn't exist.""" """Return service, throw an exception if it doesn't exist."""

View File

@ -1,4 +1,4 @@
"""Init file for HassIO snapshot rest api.""" """Init file for Hass.io snapshot RESTful API."""
import asyncio import asyncio
import logging import logging
from pathlib import Path from pathlib import Path
@ -46,7 +46,7 @@ SCHEMA_SNAPSHOT_PARTIAL = SCHEMA_SNAPSHOT_FULL.extend({
class APISnapshots(CoreSysAttributes): class APISnapshots(CoreSysAttributes):
"""Handle rest api for snapshot functions.""" """Handle RESTful API for snapshot functions."""
def _extract_snapshot(self, request): def _extract_snapshot(self, request):
"""Return snapshot, throw an exception if it doesn't exist.""" """Return snapshot, throw an exception if it doesn't exist."""

View File

@ -1,4 +1,4 @@
"""Init file for HassIO supervisor rest api.""" """Init file for Hass.io Supervisor RESTful API."""
import asyncio import asyncio
import logging import logging
@ -30,11 +30,11 @@ SCHEMA_VERSION = vol.Schema({
class APISupervisor(CoreSysAttributes): class APISupervisor(CoreSysAttributes):
"""Handle rest api for supervisor functions.""" """Handle RESTful API for Supervisor functions."""
@api_process @api_process
async def ping(self, request): async def ping(self, request):
"""Return ok for signal that the api is ready.""" """Return ok for signal that the API is ready."""
return True return True
@api_process @api_process
@ -68,7 +68,7 @@ class APISupervisor(CoreSysAttributes):
@api_process @api_process
async def options(self, request): async def options(self, request):
"""Set supervisor options.""" """Set Supervisor options."""
body = await api_validate(SCHEMA_OPTIONS, request) body = await api_validate(SCHEMA_OPTIONS, request)
if ATTR_CHANNEL in body: if ATTR_CHANNEL in body:
@ -107,7 +107,7 @@ class APISupervisor(CoreSysAttributes):
@api_process @api_process
async def update(self, request): async def update(self, request):
"""Update supervisor OS.""" """Update Supervisor OS."""
body = await api_validate(SCHEMA_VERSION, request) body = await api_validate(SCHEMA_VERSION, request)
version = body.get(ATTR_VERSION, self.sys_updater.version_hassio) version = body.get(ATTR_VERSION, self.sys_updater.version_hassio)
@ -119,7 +119,7 @@ class APISupervisor(CoreSysAttributes):
@api_process @api_process
async def reload(self, request): async def reload(self, request):
"""Reload addons, config etc.""" """Reload add-ons, configuration, etc."""
tasks = [ tasks = [
self.sys_updater.reload(), self.sys_updater.reload(),
] ]
@ -134,5 +134,5 @@ class APISupervisor(CoreSysAttributes):
@api_process_raw(CONTENT_TYPE_BINARY) @api_process_raw(CONTENT_TYPE_BINARY)
def logs(self, request): def logs(self, request):
"""Return supervisor docker logs.""" """Return supervisor Docker logs."""
return self.sys_supervisor.logs() return self.sys_supervisor.logs()

View File

@ -1,4 +1,4 @@
"""Init file for HassIO util for rest api.""" """Init file for Hass.io util for RESTful API."""
import json import json
import logging import logging
@ -27,7 +27,7 @@ def json_loads(data):
def api_process(method): def api_process(method):
"""Wrap function with true/false calls to rest api.""" """Wrap function with true/false calls to rest api."""
async def wrap_api(api, *args, **kwargs): async def wrap_api(api, *args, **kwargs):
"""Return api information.""" """Return API information."""
try: try:
answer = await method(api, *args, **kwargs) answer = await method(api, *args, **kwargs)
except HassioError: except HassioError:

View File

@ -16,7 +16,7 @@ class Systemd(DBusInterface):
"""Systemd function handler.""" """Systemd function handler."""
async def connect(self): async def connect(self):
"""Connect do bus.""" """Connect to D-Bus."""
try: try:
self.dbus = await DBus.connect(DBUS_NAME, DBUS_OBJECT) self.dbus = await DBus.connect(DBUS_NAME, DBUS_OBJECT)
except DBusError: except DBusError:

View File

@ -1,4 +1,4 @@
"""Init file for HassIO docker object.""" """Init file for Hass.io Docker object."""
from contextlib import suppress from contextlib import suppress
import logging import logging
@ -15,13 +15,13 @@ CommandReturn = attr.make_class('CommandReturn', ['exit_code', 'output'])
class DockerAPI: class DockerAPI:
"""Docker hassio wrapper. """Docker Hass.io wrapper.
This class is not AsyncIO safe! This class is not AsyncIO safe!
""" """
def __init__(self): def __init__(self):
"""Initialize docker base wrapper.""" """Initialize Docker base wrapper."""
self.docker = docker.DockerClient( self.docker = docker.DockerClient(
base_url="unix:/{}".format(str(SOCKET_DOCKER)), base_url="unix:/{}".format(str(SOCKET_DOCKER)),
version='auto', timeout=900) version='auto', timeout=900)
@ -29,21 +29,21 @@ class DockerAPI:
@property @property
def images(self): def images(self):
"""Return api images.""" """Return API images."""
return self.docker.images return self.docker.images
@property @property
def containers(self): def containers(self):
"""Return api containers.""" """Return API containers."""
return self.docker.containers return self.docker.containers
@property @property
def api(self): def api(self):
"""Return api containers.""" """Return API containers."""
return self.docker.api return self.docker.api
def run(self, image, **kwargs): def run(self, image, **kwargs):
""""Create a docker and run it. """"Create a Docker container and run it.
Need run inside executor. Need run inside executor.
""" """
@ -51,7 +51,7 @@ class DockerAPI:
network_mode = kwargs.get('network_mode') network_mode = kwargs.get('network_mode')
hostname = kwargs.get('hostname') hostname = kwargs.get('hostname')
# setup network # Setup network
kwargs['dns_search'] = ["."] kwargs['dns_search'] = ["."]
if network_mode: if network_mode:
kwargs['dns'] = [str(self.network.supervisor)] kwargs['dns'] = [str(self.network.supervisor)]
@ -59,7 +59,7 @@ class DockerAPI:
else: else:
kwargs['network'] = None kwargs['network'] = None
# create container # Create container
try: try:
container = self.docker.containers.create(image, **kwargs) container = self.docker.containers.create(image, **kwargs)
except docker.errors.DockerException as err: except docker.errors.DockerException as err:

View File

@ -1,4 +1,4 @@
"""Init file for HassIO addon docker object.""" """Init file for Hass.io add-on Docker object."""
import logging import logging
import os import os
from pathlib import Path from pathlib import Path
@ -19,45 +19,45 @@ AUDIO_DEVICE = "/dev/snd:/dev/snd:rwm"
class DockerAddon(DockerInterface): class DockerAddon(DockerInterface):
"""Docker hassio wrapper for HomeAssistant.""" """Docker Hass.io wrapper for Home Assistant."""
def __init__(self, coresys, slug): def __init__(self, coresys, slug):
"""Initialize docker homeassistant wrapper.""" """Initialize Docker Home Assistant wrapper."""
super().__init__(coresys) super().__init__(coresys)
self._id = slug self._id = slug
@property @property
def addon(self): def addon(self):
"""Return addon of docker image.""" """Return add-on of Docker image."""
return self.sys_addons.get(self._id) return self.sys_addons.get(self._id)
@property @property
def image(self): def image(self):
"""Return name of docker image.""" """Return name of Docker image."""
return self.addon.image return self.addon.image
@property @property
def timeout(self): def timeout(self):
"""Return timeout for docker actions.""" """Return timeout for Docker actions."""
return self.addon.timeout return self.addon.timeout
@property @property
def version(self): def version(self):
"""Return version of docker image.""" """Return version of Docker image."""
if not self.addon.legacy: if not self.addon.legacy:
return super().version return super().version
return self.addon.version_installed return self.addon.version_installed
@property @property
def arch(self): def arch(self):
"""Return arch of docker image.""" """Return arch of Docker image."""
if not self.addon.legacy: if not self.addon.legacy:
return super().arch return super().arch
return self.sys_arch return self.sys_arch
@property @property
def name(self): def name(self):
"""Return name of docker container.""" """Return name of Docker container."""
return "addon_{}".format(self.addon.slug) return "addon_{}".format(self.addon.slug)
@property @property
@ -74,12 +74,12 @@ class DockerAddon(DockerInterface):
@property @property
def hostname(self): def hostname(self):
"""Return slug/id of addon.""" """Return slug/id of add-on."""
return self.addon.slug.replace('_', '-') return self.addon.slug.replace('_', '-')
@property @property
def environment(self): def environment(self):
"""Return environment for docker add-on.""" """Return environment for Docker add-on."""
addon_env = self.addon.environment or {} addon_env = self.addon.environment or {}
# Need audio settings # Need audio settings
@ -114,7 +114,7 @@ class DockerAddon(DockerInterface):
@property @property
def ports(self): def ports(self):
"""Filter None from addon ports.""" """Filter None from add-on ports."""
if not self.addon.ports: if not self.addon.ports:
return None return None
@ -126,7 +126,7 @@ class DockerAddon(DockerInterface):
@property @property
def security_opt(self): def security_opt(self):
"""Controlling security opt.""" """Controlling security options."""
security = [] security = []
# AppArmor # AppArmor
@ -144,7 +144,7 @@ class DockerAddon(DockerInterface):
@property @property
def tmpfs(self): def tmpfs(self):
"""Return tmpfs for docker add-on.""" """Return tmpfs for Docker add-on."""
options = self.addon.tmpfs options = self.addon.tmpfs
if options: if options:
return {"/tmpfs": f"{options}"} return {"/tmpfs": f"{options}"}
@ -160,14 +160,14 @@ class DockerAddon(DockerInterface):
@property @property
def network_mode(self): def network_mode(self):
"""Return network mode for addon.""" """Return network mode for add-on."""
if self.addon.host_network: if self.addon.host_network:
return 'host' return 'host'
return None return None
@property @property
def pid_mode(self): def pid_mode(self):
"""Return PID mode for addon.""" """Return PID mode for add-on."""
if not self.addon.protected and self.addon.host_pid: if not self.addon.protected and self.addon.host_pid:
return 'host' return 'host'
return None return None
@ -242,7 +242,7 @@ class DockerAddon(DockerInterface):
}, },
}) })
# Host dbus system # Host D-Bus system
if self.addon.host_dbus: if self.addon.host_dbus:
volumes.update({ volumes.update({
"/var/run/dbus": { "/var/run/dbus": {
@ -259,7 +259,7 @@ class DockerAddon(DockerInterface):
return volumes return volumes
def _run(self): def _run(self):
"""Run docker image. """Run Docker image.
Need run inside executor. Need run inside executor.
""" """
@ -269,7 +269,7 @@ class DockerAddon(DockerInterface):
# Security check # Security check
if not self.addon.protected: if not self.addon.protected:
_LOGGER.warning( _LOGGER.warning(
"%s run with disabled proteced mode!", self.addon.name) "%s run with disabled protected mode!", self.addon.name)
# cleanup # cleanup
self._stop() self._stop()
@ -296,13 +296,13 @@ class DockerAddon(DockerInterface):
) )
if ret: if ret:
_LOGGER.info("Start docker addon %s with version %s", _LOGGER.info("Start Docker add-on %s with version %s",
self.image, self.version) self.image, self.version)
return ret return ret
def _install(self, tag): def _install(self, tag):
"""Pull docker image or build it. """Pull Docker image or build it.
Need run inside executor. Need run inside executor.
""" """
@ -312,7 +312,7 @@ class DockerAddon(DockerInterface):
return super()._install(tag) return super()._install(tag)
def _build(self, tag): def _build(self, tag):
"""Build a docker container. """Build a Docker container.
Need run inside executor. Need run inside executor.
""" """
@ -329,7 +329,7 @@ class DockerAddon(DockerInterface):
# Update meta data # Update meta data
self._meta = image.attrs self._meta = image.attrs
except (docker.errors.DockerException) as err: except docker.errors.DockerException as err:
_LOGGER.error("Can't build %s:%s: %s", self.image, tag, err) _LOGGER.error("Can't build %s:%s: %s", self.image, tag, err)
return False return False
@ -403,7 +403,7 @@ class DockerAddon(DockerInterface):
return False return False
try: try:
# load needed docker objects # Load needed docker objects
container = self.sys_docker.containers.get(self.name) container = self.sys_docker.containers.get(self.name)
socket = container.attach_socket(params={'stdin': 1, 'stream': 1}) socket = container.attach_socket(params={'stdin': 1, 'stream': 1})
except docker.errors.DockerException as err: except docker.errors.DockerException as err:
@ -411,7 +411,7 @@ class DockerAddon(DockerInterface):
return False return False
try: try:
# write to stdin # Write to stdin
data += b"\n" data += b"\n"
os.write(socket.fileno(), data) os.write(socket.fileno(), data)
socket.close() socket.close()

View File

@ -10,11 +10,11 @@ _LOGGER = logging.getLogger(__name__)
class DockerHassOSCli(DockerInterface, CoreSysAttributes): class DockerHassOSCli(DockerInterface, CoreSysAttributes):
"""Docker hassio wrapper for HassOS Cli.""" """Docker Hass.io wrapper for HassOS Cli."""
@property @property
def image(self): def image(self):
"""Return name of HassOS cli image.""" """Return name of HassOS CLI image."""
return f"homeassistant/{self.sys_arch}-hassio-cli" return f"homeassistant/{self.sys_arch}-hassio-cli"
def _stop(self): def _stop(self):
@ -22,16 +22,16 @@ class DockerHassOSCli(DockerInterface, CoreSysAttributes):
return True return True
def _attach(self): def _attach(self):
"""Attach to running docker container. """Attach to running Docker container.
Need run inside executor. Need run inside executor.
""" """
try: try:
image = self.sys_docker.images.get(self.image) image = self.sys_docker.images.get(self.image)
except docker.errors.DockerException: except docker.errors.DockerException:
_LOGGER.warning("Can't find a HassOS cli %s", self.image) _LOGGER.warning("Can't find a HassOS CLI %s", self.image)
else: else:
self._meta = image.attrs self._meta = image.attrs
_LOGGER.info("Found HassOS cli %s with version %s", _LOGGER.info("Found HassOS CLI %s with version %s",
self.image, self.version) self.image, self.version)

View File

@ -1,4 +1,4 @@
"""Init file for HassIO docker object.""" """Init file for Hass.io Docker object."""
import logging import logging
import docker import docker
@ -12,35 +12,35 @@ HASS_DOCKER_NAME = 'homeassistant'
class DockerHomeAssistant(DockerInterface): class DockerHomeAssistant(DockerInterface):
"""Docker hassio wrapper for HomeAssistant.""" """Docker Hass.io wrapper for Home Assistant."""
@property @property
def machine(self): def machine(self):
"""Return machine of Home-Assistant docker image.""" """Return machine of Home Assistant Docker image."""
if self._meta and LABEL_MACHINE in self._meta['Config']['Labels']: if self._meta and LABEL_MACHINE in self._meta['Config']['Labels']:
return self._meta['Config']['Labels'][LABEL_MACHINE] return self._meta['Config']['Labels'][LABEL_MACHINE]
return None return None
@property @property
def image(self): def image(self):
"""Return name of docker image.""" """Return name of Docker image."""
return self.sys_homeassistant.image return self.sys_homeassistant.image
@property @property
def name(self): def name(self):
"""Return name of docker container.""" """Return name of Docker container."""
return HASS_DOCKER_NAME return HASS_DOCKER_NAME
@property @property
def devices(self): def devices(self):
"""Create list of special device to map into docker.""" """Create list of special device to map into Docker."""
devices = [] devices = []
for device in self.sys_hardware.serial_devices: for device in self.sys_hardware.serial_devices:
devices.append(f"{device}:{device}:rwm") devices.append(f"{device}:{device}:rwm")
return devices or None return devices or None
def _run(self): def _run(self):
"""Run docker image. """Run Docker image.
Need run inside executor. Need run inside executor.
""" """
@ -108,7 +108,7 @@ class DockerHomeAssistant(DockerInterface):
) )
def is_initialize(self): def is_initialize(self):
"""Return True if docker container exists.""" """Return True if Docker container exists."""
return self.sys_run_in_executor(self._is_initialize) return self.sys_run_in_executor(self._is_initialize)
def _is_initialize(self): def _is_initialize(self):

View File

@ -1,4 +1,4 @@
"""Interface class for HassIO docker object.""" """Interface class for Hass.io Docker object."""
import asyncio import asyncio
from contextlib import suppress from contextlib import suppress
import logging import logging
@ -14,27 +14,27 @@ _LOGGER = logging.getLogger(__name__)
class DockerInterface(CoreSysAttributes): class DockerInterface(CoreSysAttributes):
"""Docker hassio interface.""" """Docker Hass.io interface."""
def __init__(self, coresys): def __init__(self, coresys):
"""Initialize docker base wrapper.""" """Initialize Docker base wrapper."""
self.coresys = coresys self.coresys = coresys
self._meta = None self._meta = None
self.lock = asyncio.Lock(loop=coresys.loop) self.lock = asyncio.Lock(loop=coresys.loop)
@property @property
def timeout(self): def timeout(self):
"""Return timeout for docker actions.""" """Return timeout for Docker actions."""
return 30 return 30
@property @property
def name(self): def name(self):
"""Return name of docker container.""" """Return name of Docker container."""
return None return None
@property @property
def meta_config(self): def meta_config(self):
"""Return meta data of config for container/image.""" """Return meta data of configuration for container/image."""
if not self._meta: if not self._meta:
return {} return {}
return self._meta.get('Config', {}) return self._meta.get('Config', {})
@ -46,17 +46,17 @@ class DockerInterface(CoreSysAttributes):
@property @property
def image(self): def image(self):
"""Return name of docker image.""" """Return name of Docker image."""
return self.meta_config.get('Image') return self.meta_config.get('Image')
@property @property
def version(self): def version(self):
"""Return version of docker image.""" """Return version of Docker image."""
return self.meta_labels.get(LABEL_VERSION) return self.meta_labels.get(LABEL_VERSION)
@property @property
def arch(self): def arch(self):
"""Return arch of docker image.""" """Return arch of Docker image."""
return self.meta_labels.get(LABEL_ARCH) return self.meta_labels.get(LABEL_ARCH)
@property @property
@ -70,7 +70,7 @@ class DockerInterface(CoreSysAttributes):
return self.sys_run_in_executor(self._install, tag) return self.sys_run_in_executor(self._install, tag)
def _install(self, tag): def _install(self, tag):
"""Pull docker image. """Pull Docker image.
Need run inside executor. Need run inside executor.
""" """
@ -88,11 +88,11 @@ class DockerInterface(CoreSysAttributes):
return True return True
def exists(self): def exists(self):
"""Return True if docker image exists in local repo.""" """Return True if Docker image exists in local repository."""
return self.sys_run_in_executor(self._exists) return self.sys_run_in_executor(self._exists)
def _exists(self): def _exists(self):
"""Return True if docker image exists in local repo. """Return True if Docker image exists in local repository.
Need run inside executor. Need run inside executor.
""" """
@ -105,14 +105,14 @@ class DockerInterface(CoreSysAttributes):
return True return True
def is_running(self): def is_running(self):
"""Return True if docker is Running. """Return True if Docker is running.
Return a Future. Return a Future.
""" """
return self.sys_run_in_executor(self._is_running) return self.sys_run_in_executor(self._is_running)
def _is_running(self): def _is_running(self):
"""Return True if docker is Running. """Return True if Docker is running.
Need run inside executor. Need run inside executor.
""" """
@ -134,7 +134,7 @@ class DockerInterface(CoreSysAttributes):
@process_lock @process_lock
def attach(self): def attach(self):
"""Attach to running docker container.""" """Attach to running Docker container."""
return self.sys_run_in_executor(self._attach) return self.sys_run_in_executor(self._attach)
def _attach(self): def _attach(self):
@ -157,11 +157,11 @@ class DockerInterface(CoreSysAttributes):
@process_lock @process_lock
def run(self): def run(self):
"""Run docker image.""" """Run Docker image."""
return self.sys_run_in_executor(self._run) return self.sys_run_in_executor(self._run)
def _run(self): def _run(self):
"""Run docker image. """Run Docker image.
Need run inside executor. Need run inside executor.
""" """
@ -169,7 +169,7 @@ class DockerInterface(CoreSysAttributes):
@process_lock @process_lock
def stop(self): def stop(self):
"""Stop/remove docker container.""" """Stop/remove Docker container."""
return self.sys_run_in_executor(self._stop) return self.sys_run_in_executor(self._stop)
def _stop(self): def _stop(self):
@ -183,19 +183,19 @@ class DockerInterface(CoreSysAttributes):
return False return False
if container.status == 'running': if container.status == 'running':
_LOGGER.info("Stop %s docker application", self.image) _LOGGER.info("Stop %s Docker application", self.image)
with suppress(docker.errors.DockerException): with suppress(docker.errors.DockerException):
container.stop(timeout=self.timeout) container.stop(timeout=self.timeout)
with suppress(docker.errors.DockerException): with suppress(docker.errors.DockerException):
_LOGGER.info("Clean %s docker application", self.image) _LOGGER.info("Clean %s Docker application", self.image)
container.remove(force=True) container.remove(force=True)
return True return True
@process_lock @process_lock
def remove(self): def remove(self):
"""Remove docker images.""" """Remove Docker images."""
return self.sys_run_in_executor(self._remove) return self.sys_run_in_executor(self._remove)
def _remove(self): def _remove(self):
@ -203,11 +203,11 @@ class DockerInterface(CoreSysAttributes):
Need run inside executor. Need run inside executor.
""" """
# cleanup container # Cleanup container
self._stop() self._stop()
_LOGGER.info( _LOGGER.info(
"Remove docker %s with latest and %s", self.image, self.version) "Remove Docker %s with latest and %s", self.image, self.version)
try: try:
with suppress(docker.errors.ImageNotFound): with suppress(docker.errors.ImageNotFound):
@ -227,7 +227,7 @@ class DockerInterface(CoreSysAttributes):
@process_lock @process_lock
def update(self, tag): def update(self, tag):
"""Update a docker image.""" """Update a Docker image."""
return self.sys_run_in_executor(self._update, tag) return self.sys_run_in_executor(self._update, tag)
def _update(self, tag): def _update(self, tag):
@ -236,27 +236,27 @@ class DockerInterface(CoreSysAttributes):
Need run inside executor. Need run inside executor.
""" """
_LOGGER.info( _LOGGER.info(
"Update docker %s with %s:%s", self.version, self.image, tag) "Update Docker %s with %s:%s", self.version, self.image, tag)
# update docker image # Update docker image
if not self._install(tag): if not self._install(tag):
return False return False
# stop container & cleanup # Stop container & cleanup
self._stop() self._stop()
self._cleanup() self._cleanup()
return True return True
def logs(self): def logs(self):
"""Return docker logs of container. """Return Docker logs of container.
Return a Future. Return a Future.
""" """
return self.sys_run_in_executor(self._logs) return self.sys_run_in_executor(self._logs)
def _logs(self): def _logs(self):
"""Return docker logs of container. """Return Docker logs of container.
Need run inside executor. Need run inside executor.
""" """
@ -268,7 +268,7 @@ class DockerInterface(CoreSysAttributes):
try: try:
return container.logs(tail=100, stdout=True, stderr=True) return container.logs(tail=100, stdout=True, stderr=True)
except docker.errors.DockerException as err: except docker.errors.DockerException as err:
_LOGGER.warning("Can't grap logs from %s: %s", self.image, err) _LOGGER.warning("Can't grep logs from %s: %s", self.image, err)
@process_lock @process_lock
def cleanup(self): def cleanup(self):
@ -291,7 +291,7 @@ class DockerInterface(CoreSysAttributes):
continue continue
with suppress(docker.errors.DockerException): with suppress(docker.errors.DockerException):
_LOGGER.info("Cleanup docker images: %s", image.tags) _LOGGER.info("Cleanup Docker images: %s", image.tags)
self.sys_docker.images.remove(image.id, force=True) self.sys_docker.images.remove(image.id, force=True)
return True return True

View File

@ -1,4 +1,4 @@
"""Internal network manager for HassIO.""" """Internal network manager for Hass.io."""
import logging import logging
import docker import docker
@ -9,13 +9,13 @@ _LOGGER = logging.getLogger(__name__)
class DockerNetwork: class DockerNetwork:
"""Internal HassIO Network. """Internal Hass.io Network.
This class is not AsyncIO safe! This class is not AsyncIO safe!
""" """
def __init__(self, dock): def __init__(self, dock):
"""Initialize internal hassio network.""" """Initialize internal Hass.io network."""
self.docker = dock self.docker = dock
self.network = self._get_network() self.network = self._get_network()
@ -44,7 +44,7 @@ class DockerNetwork:
try: try:
return self.docker.networks.get(DOCKER_NETWORK) return self.docker.networks.get(DOCKER_NETWORK)
except docker.errors.NotFound: except docker.errors.NotFound:
_LOGGER.info("Can't find HassIO network, create new network") _LOGGER.info("Can't find Hass.io network, create new network")
ipam_pool = docker.types.IPAMPool( ipam_pool = docker.types.IPAMPool(
subnet=str(DOCKER_NETWORK_MASK), subnet=str(DOCKER_NETWORK_MASK),
@ -61,7 +61,7 @@ class DockerNetwork:
}) })
def attach_container(self, container, alias=None, ipv4=None): def attach_container(self, container, alias=None, ipv4=None):
"""Attach container to hassio network. """Attach container to Hass.io network.
Need run inside executor. Need run inside executor.
""" """
@ -77,7 +77,7 @@ class DockerNetwork:
return True return True
def detach_default_bridge(self, container): def detach_default_bridge(self, container):
"""Detach default docker bridge. """Detach default Docker bridge.
Need run inside executor. Need run inside executor.
""" """

View File

@ -1,4 +1,4 @@
"""Calc & represent docker stats data.""" """Calc and represent docker stats data."""
from contextlib import suppress from contextlib import suppress
@ -6,7 +6,7 @@ class DockerStats:
"""Hold stats data from container inside.""" """Hold stats data from container inside."""
def __init__(self, stats): def __init__(self, stats):
"""Initialize docker stats.""" """Initialize Docker stats."""
self._cpu = 0.0 self._cpu = 0.0
self._network_rx = 0 self._network_rx = 0
self._network_tx = 0 self._network_tx = 0

View File

@ -1,4 +1,4 @@
"""Init file for HassIO docker object.""" """Init file for Hass.io Docker object."""
import logging import logging
import os import os
@ -11,11 +11,11 @@ _LOGGER = logging.getLogger(__name__)
class DockerSupervisor(DockerInterface, CoreSysAttributes): class DockerSupervisor(DockerInterface, CoreSysAttributes):
"""Docker hassio wrapper for Supervisor.""" """Docker Hass.io wrapper for Supervisor."""
@property @property
def name(self): def name(self):
"""Return name of docker container.""" """Return name of Docker container."""
return os.environ['SUPERVISOR_NAME'] return os.environ['SUPERVISOR_NAME']
def _attach(self): def _attach(self):
@ -29,14 +29,14 @@ class DockerSupervisor(DockerInterface, CoreSysAttributes):
return False return False
self._meta = container.attrs self._meta = container.attrs
_LOGGER.info("Attach to supervisor %s with version %s", _LOGGER.info("Attach to Supervisor %s with version %s",
self.image, self.version) self.image, self.version)
# if already attach # If already attach
if container in self.sys_docker.network.containers: if container in self.sys_docker.network.containers:
return True return True
# attach to network # Attach to network
return self.sys_docker.network.attach_container( return self.sys_docker.network.attach_container(
container, alias=['hassio'], container, alias=['hassio'],
ipv4=self.sys_docker.network.supervisor) ipv4=self.sys_docker.network.supervisor)

View File

@ -1,4 +1,4 @@
"""Host function like audio/dbus/systemd.""" """Host function like audio, D-Bus or systemd."""
from contextlib import suppress from contextlib import suppress
import logging import logging
@ -35,7 +35,7 @@ class HostManager(CoreSysAttributes):
@property @property
def apparmor(self): def apparmor(self):
"""Return host apparmor handler.""" """Return host AppArmor handler."""
return self._apparmor return self._apparmor
@property @property

View File

@ -1,4 +1,4 @@
"""Host Audio-support.""" """Host Audio support."""
import logging import logging
import json import json
from pathlib import Path from pathlib import Path
@ -19,7 +19,7 @@ class AlsaAudio(CoreSysAttributes):
"""Handle Audio ALSA host data.""" """Handle Audio ALSA host data."""
def __init__(self, coresys): def __init__(self, coresys):
"""Initialize Alsa audio system.""" """Initialize ALSA audio system."""
self.coresys = coresys self.coresys = coresys
self._data = { self._data = {
ATTR_INPUT: {}, ATTR_INPUT: {},

View File

@ -13,7 +13,7 @@ SYSTEMD_SERVICES = {'hassos-apparmor.service', 'hassio-apparmor.service'}
class AppArmorControl(CoreSysAttributes): class AppArmorControl(CoreSysAttributes):
"""Handle host apparmor controls.""" """Handle host AppArmor controls."""
def __init__(self, coresys): def __init__(self, coresys):
"""Initialize host power handling.""" """Initialize host power handling."""
@ -23,7 +23,7 @@ class AppArmorControl(CoreSysAttributes):
@property @property
def available(self): def available(self):
"""Return True if AppArmor is availabe on host.""" """Return True if AppArmor is available on host."""
return self._service is not None return self._service is not None
def exists(self, profile): def exists(self, profile):
@ -62,12 +62,12 @@ class AppArmorControl(CoreSysAttributes):
if self.available: if self.available:
await self._reload_service() await self._reload_service()
else: else:
_LOGGER.info("AppArmor is not enabled on Host") _LOGGER.info("AppArmor is not enabled on host")
async def load_profile(self, profile_name, profile_file): async def load_profile(self, profile_name, profile_file):
"""Load/Update a new/exists profile into AppArmor.""" """Load/Update a new/exists profile into AppArmor."""
if not validate_profile(profile_name, profile_file): if not validate_profile(profile_name, profile_file):
_LOGGER.error("profile is not valid with name %s", profile_name) _LOGGER.error("Profile is not valid with name %s", profile_name)
raise HostAppArmorError() raise HostAppArmorError()
# Copy to AppArmor folder # Copy to AppArmor folder

View File

@ -24,7 +24,7 @@ class SystemControl(CoreSysAttributes):
if flag == HOSTNAME and self.sys_dbus.hostname.is_connected: if flag == HOSTNAME and self.sys_dbus.hostname.is_connected:
return return
_LOGGER.error("No %s dbus connection available", flag) _LOGGER.error("No %s D-Bus connection available", flag)
raise HostNotSupportedError() raise HostNotSupportedError()
async def reboot(self): async def reboot(self):
@ -51,6 +51,6 @@ class SystemControl(CoreSysAttributes):
"""Set local a new Hostname.""" """Set local a new Hostname."""
self._check_dbus(HOSTNAME) self._check_dbus(HOSTNAME)
_LOGGER.info("Set Hostname %s", hostname) _LOGGER.info("Set hostname %s", hostname)
await self.sys_dbus.hostname.set_static_hostname(hostname) await self.sys_dbus.hostname.set_static_hostname(hostname)
await self.sys_host.info.update() await self.sys_host.info.update()

View File

@ -48,7 +48,7 @@ class InfoCenter(CoreSysAttributes):
async def update(self): async def update(self):
"""Update properties over dbus.""" """Update properties over dbus."""
if not self.sys_dbus.hostname.is_connected: if not self.sys_dbus.hostname.is_connected:
_LOGGER.error("No hostname dbus connection available") _LOGGER.error("No hostname D-Bus connection available")
raise HostNotSupportedError() raise HostNotSupportedError()
_LOGGER.info("Update local host information") _LOGGER.info("Update local host information")

View File

@ -95,5 +95,5 @@ class ServiceInfo:
@staticmethod @staticmethod
def read_from(unit): def read_from(unit):
"""Parse data from dbus into this object.""" """Parse data from D-Bus into this object."""
return ServiceInfo(unit[0], unit[1], unit[3]) return ServiceInfo(unit[0], unit[1], unit[3])

View File

@ -24,7 +24,7 @@ RE_TTY = re.compile(r"tty[A-Z]+")
class Hardware: class Hardware:
"""Represent an interface to procfs, sysfs and udev.""" """Representation of an interface to procfs, sysfs and udev."""
def __init__(self): def __init__(self):
"""Init hardware object.""" """Init hardware object."""

View File

@ -1,4 +1,4 @@
"""Schedule for HassIO.""" """Schedule for Hass.io."""
import logging import logging
from datetime import date, datetime, time, timedelta from datetime import date, datetime, time, timedelta
@ -11,7 +11,7 @@ TASK = 'task'
class Scheduler: class Scheduler:
"""Schedule task inside HassIO.""" """Schedule task inside Hass.io."""
def __init__(self, loop): def __init__(self, loop):
"""Initialize task schedule.""" """Initialize task schedule."""
@ -22,18 +22,18 @@ class Scheduler:
def register_task(self, coro_callback, interval, repeat=True): def register_task(self, coro_callback, interval, repeat=True):
"""Schedule a coroutine. """Schedule a coroutine.
The coroutien need to be a callback without arguments. The coroutine need to be a callback without arguments.
""" """
task_id = hash(coro_callback) task_id = hash(coro_callback)
# generate data # Generate data
opts = { opts = {
CALL: coro_callback, CALL: coro_callback,
INTERVAL: interval, INTERVAL: interval,
REPEAT: repeat, REPEAT: repeat,
} }
# schedule task # Schedule task
self._data[task_id] = opts self._data[task_id] = opts
self._schedule_task(interval, task_id) self._schedule_task(interval, task_id)
@ -60,7 +60,7 @@ class Scheduler:
tomorrow = datetime.combine( tomorrow = datetime.combine(
date.today() + timedelta(days=1), interval) date.today() + timedelta(days=1), interval)
# check if we run it today or next day # Check if we run it today or next day
if today > datetime.today(): if today > datetime.today():
calc = today calc = today
else: else:
@ -68,7 +68,7 @@ class Scheduler:
job = self.loop.call_at(calc.timestamp(), self._run_task, task_id) job = self.loop.call_at(calc.timestamp(), self._run_task, task_id)
else: else:
_LOGGER.fatal("Unknow interval %s (type: %s) for scheduler %s", _LOGGER.fatal("Unknown interval %s (type: %s) for scheduler %s",
interval, type(interval), task_id) interval, type(interval), task_id)
# Store job # Store job

View File

@ -1,5 +1,4 @@
"""Handle internal services discovery.""" """Handle internal services discovery."""
from .discovery import Discovery # noqa from .discovery import Discovery # noqa
from .mqtt import MQTTService from .mqtt import MQTTService
from .data import ServicesData from .data import ServicesData

View File

@ -14,10 +14,10 @@ class ServicesData(JsonConfig):
@property @property
def discovery(self): def discovery(self):
"""Return discovery data for home-assistant.""" """Return discovery data for Home Assistant."""
return self._data[ATTR_DISCOVERY] return self._data[ATTR_DISCOVERY]
@property @property
def mqtt(self): def mqtt(self):
"""Return settings for mqtt service.""" """Return settings for MQTT service."""
return self._data[SERVICE_MQTT] return self._data[SERVICE_MQTT]

View File

@ -1,4 +1,4 @@
"""Handle discover message for Home-Assistant.""" """Handle discover message for Home Assistant."""
import logging import logging
from uuid import uuid4 from uuid import uuid4
@ -12,7 +12,7 @@ EVENT_DISCOVERY_DEL = 'hassio_discovery_del'
class Discovery(CoreSysAttributes): class Discovery(CoreSysAttributes):
"""Home-Assistant Discovery handler.""" """Home Assistant Discovery handler."""
def __init__(self, coresys): def __init__(self, coresys):
"""Initialize discovery handler.""" """Initialize discovery handler."""
@ -53,29 +53,29 @@ class Discovery(CoreSysAttributes):
return self.message_obj.values() return self.message_obj.values()
def send(self, provider, component, platform=None, config=None): def send(self, provider, component, platform=None, config=None):
"""Send a discovery message to Home-Assistant.""" """Send a discovery message to Home Assistant."""
message = Message(provider, component, platform, config) message = Message(provider, component, platform, config)
# Already exists? # Already exists?
for exists_message in self.message_obj: for exists_message in self.message_obj:
if exists_message == message: if exists_message == message:
_LOGGER.warning("Found douplicate discovery message from %s", _LOGGER.warning("Found duplicate discovery message from %s",
provider) provider)
return exists_message return exists_message
_LOGGER.info("Send discovery to Home-Assistant %s/%s from %s", _LOGGER.info("Send discovery to Home Assistant %s/%s from %s",
component, platform, provider) component, platform, provider)
self.message_obj[message.uuid] = message self.message_obj[message.uuid] = message
self.save() self.save()
# send event to Home-Assistant # Send event to Home Assistant
self.sys_create_task(self.sys_homeassistant.send_event( self.sys_create_task(self.sys_homeassistant.send_event(
EVENT_DISCOVERY_ADD, {ATTR_UUID: message.uuid})) EVENT_DISCOVERY_ADD, {ATTR_UUID: message.uuid}))
return message return message
def remove(self, message): def remove(self, message):
"""Remove a discovery message from Home-Assistant.""" """Remove a discovery message from Home Assistant."""
self.message_obj.pop(message.uuid, None) self.message_obj.pop(message.uuid, None)
self.save() self.save()

View File

@ -1,4 +1,4 @@
"""Provide MQTT Service.""" """Provide the MQTT Service."""
import logging import logging
from .interface import ServiceInterface from .interface import ServiceInterface
@ -11,7 +11,7 @@ _LOGGER = logging.getLogger(__name__)
class MQTTService(ServiceInterface): class MQTTService(ServiceInterface):
"""Provide mqtt services.""" """Provide MQTT services."""
@property @property
def slug(self): def slug(self):
@ -35,7 +35,7 @@ class MQTTService(ServiceInterface):
@property @property
def hass_config(self): def hass_config(self):
"""Return Home-Assistant mqtt config.""" """Return Home Assistant MQTT config."""
if not self.enabled: if not self.enabled:
return None return None
@ -54,18 +54,18 @@ class MQTTService(ServiceInterface):
def set_service_data(self, provider, data): def set_service_data(self, provider, data):
"""Write the data into service object.""" """Write the data into service object."""
if self.enabled: if self.enabled:
_LOGGER.error("It is already a mqtt in use from %s", self.provider) _LOGGER.error("It is already a MQTT in use from %s", self.provider)
return False return False
self._data.update(data) self._data.update(data)
self._data[ATTR_PROVIDER] = provider self._data[ATTR_PROVIDER] = provider
if provider == 'homeassistant': if provider == 'homeassistant':
_LOGGER.info("Use mqtt settings from Home-Assistant") _LOGGER.info("Use MQTT settings from Home Assistant")
self.save() self.save()
return True return True
# discover mqtt to homeassistant # Discover MQTT to Home Assistant
message = self.sys_discovery.send( message = self.sys_discovery.send(
provider, SERVICE_MQTT, None, self.hass_config) provider, SERVICE_MQTT, None, self.hass_config)
@ -76,7 +76,7 @@ class MQTTService(ServiceInterface):
def del_service_data(self, provider): def del_service_data(self, provider):
"""Remove the data from service object.""" """Remove the data from service object."""
if not self.enabled: if not self.enabled:
_LOGGER.warning("Can't remove not exists services.") _LOGGER.warning("Can't remove not exists services")
return False return False
discovery_id = self._data.get(ATTR_DISCOVERY_ID) discovery_id = self._data.get(ATTR_DISCOVERY_ID)

View File

@ -1,5 +1,4 @@
"""Validate services schema.""" """Validate services schema."""
import voluptuous as vol import voluptuous as vol
from ..const import ( from ..const import (

View File

@ -1,4 +1,4 @@
"""Represent a snapshot file.""" """Representation of a snapshot file."""
import asyncio import asyncio
from base64 import b64decode, b64encode from base64 import b64decode, b64encode
import json import json
@ -29,7 +29,7 @@ _LOGGER = logging.getLogger(__name__)
class Snapshot(CoreSysAttributes): class Snapshot(CoreSysAttributes):
"""A signle hassio snapshot.""" """A single Hass.io snapshot."""
def __init__(self, coresys, tar_file): def __init__(self, coresys, tar_file):
"""Initialize a snapshot.""" """Initialize a snapshot."""
@ -72,7 +72,7 @@ class Snapshot(CoreSysAttributes):
@property @property
def addon_list(self): def addon_list(self):
"""Return a list of addons slugs.""" """Return a list of add-ons slugs."""
return [addon_data[ATTR_SLUG] for addon_data in self.addons] return [addon_data[ATTR_SLUG] for addon_data in self.addons]
@property @property
@ -92,12 +92,12 @@ class Snapshot(CoreSysAttributes):
@property @property
def homeassistant_version(self): def homeassistant_version(self):
"""Return snapshot homeassistant version.""" """Return snapshot Home Assistant version."""
return self._data[ATTR_HOMEASSISTANT].get(ATTR_VERSION) return self._data[ATTR_HOMEASSISTANT].get(ATTR_VERSION)
@property @property
def homeassistant(self): def homeassistant(self):
"""Return snapshot homeassistant data.""" """Return snapshot Home Assistant data."""
return self._data[ATTR_HOMEASSISTANT] return self._data[ATTR_HOMEASSISTANT]
@property @property
@ -119,7 +119,7 @@ class Snapshot(CoreSysAttributes):
def new(self, slug, name, date, sys_type, password=None): def new(self, slug, name, date, sys_type, password=None):
"""Initialize a new snapshot.""" """Initialize a new snapshot."""
# init metadata # Init metadata
self._data[ATTR_SLUG] = slug self._data[ATTR_SLUG] = slug
self._data[ATTR_NAME] = name self._data[ATTR_NAME] = name
self._data[ATTR_DATE] = date self._data[ATTR_DATE] = date
@ -306,16 +306,16 @@ class Snapshot(CoreSysAttributes):
await asyncio.wait(tasks) await asyncio.wait(tasks)
async def store_folders(self, folder_list=None): async def store_folders(self, folder_list=None):
"""Backup hassio data into snapshot.""" """Backup Hass.io data into snapshot."""
folder_list = set(folder_list or ALL_FOLDERS) folder_list = set(folder_list or ALL_FOLDERS)
def _folder_save(name): def _folder_save(name):
"""Intenal function to snapshot a folder.""" """Internal function to snapshot a folder."""
slug_name = name.replace("/", "_") slug_name = name.replace("/", "_")
tar_name = Path(self._tmp.name, f"{slug_name}.tar.gz") tar_name = Path(self._tmp.name, f"{slug_name}.tar.gz")
origin_dir = Path(self.sys_config.path_hassio, name) origin_dir = Path(self.sys_config.path_hassio, name)
# Check if exsits # Check if exists
if not origin_dir.is_dir(): if not origin_dir.is_dir():
_LOGGER.warning("Can't find snapshot folder %s", name) _LOGGER.warning("Can't find snapshot folder %s", name)
return return
@ -338,7 +338,7 @@ class Snapshot(CoreSysAttributes):
await asyncio.wait(tasks) await asyncio.wait(tasks)
async def restore_folders(self, folder_list=None): async def restore_folders(self, folder_list=None):
"""Backup hassio data into snapshot.""" """Backup Hass.io data into snapshot."""
folder_list = set(folder_list or self.folders) folder_list = set(folder_list or self.folders)
def _folder_restore(name): def _folder_restore(name):
@ -356,7 +356,7 @@ class Snapshot(CoreSysAttributes):
if origin_dir.is_dir(): if origin_dir.is_dir():
remove_folder(origin_dir) remove_folder(origin_dir)
# Performe a restore # Perform a restore
try: try:
_LOGGER.info("Restore folder %s", name) _LOGGER.info("Restore folder %s", name)
with SecureTarFile(tar_name, 'r', key=self._key) as tar_file: with SecureTarFile(tar_name, 'r', key=self._key) as tar_file:
@ -372,7 +372,7 @@ class Snapshot(CoreSysAttributes):
await asyncio.wait(tasks) await asyncio.wait(tasks)
def store_homeassistant(self): def store_homeassistant(self):
"""Read all data from homeassistant object.""" """Read all data from Home Assistant object."""
self.homeassistant[ATTR_VERSION] = self.sys_homeassistant.version self.homeassistant[ATTR_VERSION] = self.sys_homeassistant.version
self.homeassistant[ATTR_WATCHDOG] = self.sys_homeassistant.watchdog self.homeassistant[ATTR_WATCHDOG] = self.sys_homeassistant.watchdog
self.homeassistant[ATTR_BOOT] = self.sys_homeassistant.boot self.homeassistant[ATTR_BOOT] = self.sys_homeassistant.boot
@ -393,7 +393,7 @@ class Snapshot(CoreSysAttributes):
self._encrypt_data(self.sys_homeassistant.api_password) self._encrypt_data(self.sys_homeassistant.api_password)
def restore_homeassistant(self): def restore_homeassistant(self):
"""Write all data to homeassistant object.""" """Write all data to the Home Assistant object."""
self.sys_homeassistant.watchdog = self.homeassistant[ATTR_WATCHDOG] self.sys_homeassistant.watchdog = self.homeassistant[ATTR_WATCHDOG]
self.sys_homeassistant.boot = self.homeassistant[ATTR_BOOT] self.sys_homeassistant.boot = self.homeassistant[ATTR_BOOT]
self.sys_homeassistant.wait_boot = self.homeassistant[ATTR_WAIT_BOOT] self.sys_homeassistant.wait_boot = self.homeassistant[ATTR_WAIT_BOOT]

View File

@ -1,4 +1,4 @@
"""Util addons functions.""" """Util add-on functions."""
import hashlib import hashlib
import shutil import shutil
import re import re

View File

@ -1,5 +1,4 @@
"""Validate some things around restore.""" """Validate some things around restore."""
import voluptuous as vol import voluptuous as vol
from ..const import ( from ..const import (