Skip to content

Commit

Permalink
Merge pull request #108 from home-assistant/dev
Browse files Browse the repository at this point in the history
Release 0.46
  • Loading branch information
pvizeli authored Jul 22, 2017
2 parents 14bf834 + 58c5ed7 commit 803eb0f
Show file tree
Hide file tree
Showing 15 changed files with 121 additions and 93 deletions.
19 changes: 9 additions & 10 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,11 @@ The addons from `addons` are only installed one.
"name": "xy bla",
"slug": "xy",
"description": "description",
"arch": ["armhf", "aarch64", "i386", "amd64"],
"repository": "12345678|null",
"version": "LAST_VERSION",
"installed": "INSTALL_VERSION",
"detached": "bool",
"build": "bool",
"url": "null|url"
"logo": "bool",
"state": "started|stopped",
}
],
"addons_repositories": [
Expand All @@ -55,10 +53,6 @@ The addons from `addons` are only installed one.
}
```

- GET `/supervisor/addons`

Get all available addons. Will be delete soon. Look to `/addons`

- POST `/supervisor/update`
Optional:
```json
Expand Down Expand Up @@ -299,7 +293,8 @@ Get all available addons
"installed": "none|INSTALL_VERSION",
"detached": "bool",
"build": "bool",
"url": "null|url"
"url": "null|url",
"logo": "bool"
}
],
"repositories": [
Expand Down Expand Up @@ -332,10 +327,14 @@ Get all available addons
"build": "bool",
"options": "{}",
"network": "{}|null",
"host_network": "bool"
"host_network": "bool",
"logo": "bool",
"webui": "null|http(s)://[HOST]:port/xy/zx"
}
```

- GET `/addons/{addon}/logo`

- POST `/addons/{addon}/options`
```json
{
Expand Down
41 changes: 36 additions & 5 deletions hassio/addons/addon.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@
ATTR_URL, ATTR_ARCH, ATTR_LOCATON, ATTR_DEVICES, ATTR_ENVIRONMENT,
ATTR_HOST_NETWORK, ATTR_TMPFS, ATTR_PRIVILEGED, ATTR_STARTUP,
STATE_STARTED, STATE_STOPPED, STATE_NONE, ATTR_USER, ATTR_SYSTEM,
ATTR_STATE, ATTR_TIMEOUT, ATTR_AUTO_UPDATE, ATTR_NETWORK)
ATTR_STATE, ATTR_TIMEOUT, ATTR_AUTO_UPDATE, ATTR_NETWORK, ATTR_WEBUI)
from .util import check_installed
from ..dock.addon import DockerAddon
from ..tools import write_json_file, read_json_file

_LOGGER = logging.getLogger(__name__)

RE_VOLUME = re.compile(MAP_VOLUME)
RE_WEBUI = re.compile(r"^(.*\[HOST\]:)\[PORT:(\d+)\](.*)$")


class Addon(object):
Expand Down Expand Up @@ -130,7 +131,8 @@ def boot(self, value):
@property
def auto_update(self):
"""Return if auto update is enable."""
return self.data.user[self._id][ATTR_AUTO_UPDATE]
if ATTR_AUTO_UPDATE in self.data.user.get(self._id, {}):
return self.data.user[self._id][ATTR_AUTO_UPDATE]

@auto_update.setter
def auto_update(self, value):
Expand Down Expand Up @@ -196,6 +198,25 @@ def ports(self, value):

self.data.save()

@property
def webui(self):
"""Return URL to webui or None."""
if ATTR_WEBUI not in self._mesh:
return

webui = self._mesh[ATTR_WEBUI]
dock_port = RE_WEBUI.sub(r"\2", webui)
if self.ports is None:
real_port = dock_port
else:
real_port = self.ports.get("{}/tcp".format(dock_port), dock_port)

# for interface config or port lists
if isinstance(real_port, (tuple, list)):
real_port = real_port[-1]

return RE_WEBUI.sub(r"\g<1>{}\g<3>".format(real_port), webui)

@property
def network_mode(self):
"""Return network mode of addon."""
Expand Down Expand Up @@ -228,6 +249,11 @@ def url(self):
"""Return url of addon."""
return self._mesh.get(ATTR_URL)

@property
def with_logo(self):
"""Return True if a logo exists."""
return self.path_logo.exists()

@property
def supported_arch(self):
"""Return list of supported arch."""
Expand Down Expand Up @@ -273,23 +299,28 @@ def path_extern_data(self):
return PurePath(self.config.path_extern_addons_data, self._id)

@property
def path_addon_options(self):
def path_options(self):
"""Return path to addons options."""
return Path(self.path_data, "options.json")

@property
def path_addon_location(self):
def path_location(self):
"""Return path to this addon."""
return Path(self._mesh[ATTR_LOCATON])

@property
def path_logo(self):
"""Return path to addon logo."""
return Path(self.path_location, 'logo.png')

def write_options(self):
"""Return True if addon options is written to data."""
schema = self.schema
options = self.options

try:
schema(options)
return write_json_file(self.path_addon_options, options)
return write_json_file(self.path_options, options)
except vol.Invalid as ex:
_LOGGER.error("Addon %s have wrong options -> %s", self._id,
humanize_error(options, ex))
Expand Down
4 changes: 3 additions & 1 deletion hassio/addons/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
ARCH_AARCH64, ARCH_AMD64, ARCH_I386, ATTR_TMPFS, ATTR_PRIVILEGED,
ATTR_USER, ATTR_STATE, ATTR_SYSTEM, STATE_STARTED, STATE_STOPPED,
ATTR_LOCATON, ATTR_REPOSITORY, ATTR_TIMEOUT, ATTR_NETWORK,
ATTR_AUTO_UPDATE)
ATTR_AUTO_UPDATE, ATTR_WEBUI)
from ..validate import NETWORK_PORT, DOCKER_PORTS


Expand Down Expand Up @@ -65,6 +65,8 @@ def _migrate_startup(value):
vol.Required(ATTR_BOOT):
vol.In([BOOT_AUTO, BOOT_MANUAL]),
vol.Optional(ATTR_PORTS): DOCKER_PORTS,
vol.Optional(ATTR_WEBUI):
vol.Match(r"^(?:https?):\/\/\[HOST\]:\[PORT:\d+\].*$"),
vol.Optional(ATTR_HOST_NETWORK, default=False): vol.Boolean(),
vol.Optional(ATTR_DEVICES): [vol.Match(r"^(.*):(.*):([rwm]{1,3})$")],
vol.Optional(ATTR_TMPFS):
Expand Down
3 changes: 1 addition & 2 deletions hassio/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ def register_supervisor(self, supervisor, snapshots, addons, host_control,

self.webapp.router.add_get('/supervisor/ping', api_supervisor.ping)
self.webapp.router.add_get('/supervisor/info', api_supervisor.info)
self.webapp.router.add_get(
'/supervisor/addons', api_supervisor.available_addons)
self.webapp.router.add_post(
'/supervisor/update', api_supervisor.update)
self.webapp.router.add_post(
Expand Down Expand Up @@ -94,6 +92,7 @@ def register_addons(self, addons):
self.webapp.router.add_post(
'/addons/{addon}/options', api_addons.options)
self.webapp.router.add_get('/addons/{addon}/logs', api_addons.logs)
self.webapp.router.add_get('/addons/{addon}/logo', api_addons.logo)

def register_security(self):
"""Register security function."""
Expand Down
23 changes: 19 additions & 4 deletions hassio/api/addons.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
ATTR_URL, ATTR_DESCRIPTON, ATTR_DETACHED, ATTR_NAME, ATTR_REPOSITORY,
ATTR_BUILD, ATTR_AUTO_UPDATE, ATTR_NETWORK, ATTR_HOST_NETWORK, ATTR_SLUG,
ATTR_SOURCE, ATTR_REPOSITORIES, ATTR_ADDONS, ATTR_ARCH, ATTR_MAINTAINER,
ATTR_INSTALLED, BOOT_AUTO, BOOT_MANUAL)
ATTR_INSTALLED, ATTR_LOGO, ATTR_WEBUI, BOOT_AUTO, BOOT_MANUAL,
CONTENT_TYPE_PNG, CONTENT_TYPE_BINARY)
from ..validate import DOCKER_PORTS

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -64,6 +65,7 @@ async def list(self, request):
ATTR_REPOSITORY: addon.repository,
ATTR_BUILD: addon.need_build,
ATTR_URL: addon.url,
ATTR_LOGO: addon.with_logo,
})

data_repositories = []
Expand All @@ -82,9 +84,10 @@ async def list(self, request):
}

@api_process
def reload(self, request):
async def reload(self, request):
"""Reload all addons data."""
return self.addons.reload()
await asyncio.shield(self.addons.reload(), loop=self.loop)
return True

@api_process
async def info(self, request):
Expand All @@ -106,6 +109,8 @@ async def info(self, request):
ATTR_BUILD: addon.need_build,
ATTR_NETWORK: addon.ports,
ATTR_HOST_NETWORK: addon.network_mode == 'host',
ATTR_LOGO: addon.with_logo,
ATTR_WEBUI: addon.webui,
}

@api_process
Expand Down Expand Up @@ -182,8 +187,18 @@ async def restart(self, request):
addon = self._extract_addon(request)
return await asyncio.shield(addon.restart(), loop=self.loop)

@api_process_raw
@api_process_raw(CONTENT_TYPE_BINARY)
def logs(self, request):
"""Return logs from addon."""
addon = self._extract_addon(request)
return addon.logs()

@api_process_raw(CONTENT_TYPE_PNG)
async def logo(self, request):
"""Return logo from addon."""
addon = self._extract_addon(request, check_installed=False)
if not addon.with_logo:
raise RuntimeError("No image found!")

with addon.path_logo.open('rb') as png:
return png.read()
5 changes: 3 additions & 2 deletions hassio/api/homeassistant.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

from .util import api_process, api_process_raw, api_validate
from ..const import (
ATTR_VERSION, ATTR_LAST_VERSION, ATTR_DEVICES, ATTR_IMAGE, ATTR_CUSTOM)
ATTR_VERSION, ATTR_LAST_VERSION, ATTR_DEVICES, ATTR_IMAGE, ATTR_CUSTOM,
CONTENT_TYPE_BINARY)
from ..validate import HASS_DEVICES

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -79,7 +80,7 @@ async def restart(self, request):
return await asyncio.shield(
self.homeassistant.restart(), loop=self.loop)

@api_process_raw
@api_process_raw(CONTENT_TYPE_BINARY)
def logs(self, request):
"""Return homeassistant docker logs.
Expand Down
5 changes: 3 additions & 2 deletions hassio/api/snapshots.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,10 @@ async def list(self, request):
}

@api_process
def reload(self, request):
async def reload(self, request):
"""Reload snapshot list."""
return asyncio.shield(self.snapshots.reload(), loop=self.loop)
await asyncio.shield(self.snapshots.reload(), loop=self.loop)
return True

@api_process
async def info(self, request):
Expand Down
71 changes: 20 additions & 51 deletions hassio/api/supervisor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@

from .util import api_process, api_process_raw, api_validate
from ..const import (
ATTR_ADDONS, ATTR_VERSION, ATTR_LAST_VERSION, ATTR_BETA_CHANNEL,
HASSIO_VERSION, ATTR_ADDONS_REPOSITORIES, ATTR_REPOSITORIES,
ATTR_REPOSITORY, ATTR_DESCRIPTON, ATTR_NAME, ATTR_SLUG, ATTR_INSTALLED,
ATTR_DETACHED, ATTR_SOURCE, ATTR_MAINTAINER, ATTR_URL, ATTR_ARCH,
ATTR_BUILD, ATTR_TIMEZONE)
ATTR_ADDONS, ATTR_VERSION, ATTR_LAST_VERSION, ATTR_BETA_CHANNEL, ATTR_ARCH,
HASSIO_VERSION, ATTR_ADDONS_REPOSITORIES, ATTR_LOGO, ATTR_REPOSITORY,
ATTR_DESCRIPTON, ATTR_NAME, ATTR_SLUG, ATTR_INSTALLED, ATTR_TIMEZONE,
ATTR_STATE, CONTENT_TYPE_BINARY)
from ..tools import validate_timezone

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -41,42 +40,6 @@ def __init__(self, config, loop, supervisor, snapshots, addons,
self.host_control = host_control
self.websession = websession

def _addons_list(self, only_installed=False):
"""Return a list of addons."""
data = []
for addon in self.addons.list_addons:
if only_installed and not addon.is_installed:
continue

data.append({
ATTR_NAME: addon.name,
ATTR_SLUG: addon.slug,
ATTR_DESCRIPTON: addon.description,
ATTR_VERSION: addon.last_version,
ATTR_INSTALLED: addon.version_installed,
ATTR_ARCH: addon.supported_arch,
ATTR_DETACHED: addon.is_detached,
ATTR_REPOSITORY: addon.repository,
ATTR_BUILD: addon.need_build,
ATTR_URL: addon.url,
})

return data

def _repositories_list(self):
"""Return a list of addons repositories."""
data = []
for repository in self.addons.list_repositories:
data.append({
ATTR_SLUG: repository.slug,
ATTR_NAME: repository.name,
ATTR_SOURCE: repository.source,
ATTR_URL: repository.url,
ATTR_MAINTAINER: repository.maintainer,
})

return data

@api_process
async def ping(self, request):
"""Return ok for signal that the api is ready."""
Expand All @@ -85,24 +48,30 @@ async def ping(self, request):
@api_process
async def info(self, request):
"""Return host information."""
list_addons = []
for addon in self.addons.list_addons:
if addon.is_installed:
list_addons.append({
ATTR_NAME: addon.name,
ATTR_SLUG: addon.slug,
ATTR_DESCRIPTON: addon.description,
ATTR_STATE: await addon.state(),
ATTR_VERSION: addon.last_version,
ATTR_INSTALLED: addon.version_installed,
ATTR_REPOSITORY: addon.repository,
ATTR_LOGO: addon.with_logo,
})

return {
ATTR_VERSION: HASSIO_VERSION,
ATTR_LAST_VERSION: self.config.last_hassio,
ATTR_BETA_CHANNEL: self.config.upstream_beta,
ATTR_ARCH: self.config.arch,
ATTR_TIMEZONE: self.config.timezone,
ATTR_ADDONS: self._addons_list(only_installed=True),
ATTR_ADDONS: list_addons,
ATTR_ADDONS_REPOSITORIES: self.config.addons_repositories,
}

@api_process
async def available_addons(self, request):
"""Return information for all available addons."""
return {
ATTR_ADDONS: self._addons_list(),
ATTR_REPOSITORIES: self._repositories_list(),
}

@api_process
async def options(self, request):
"""Set supervisor options."""
Expand Down Expand Up @@ -150,7 +119,7 @@ async def reload(self, request):

return True

@api_process_raw
@api_process_raw(CONTENT_TYPE_BINARY)
def logs(self, request):
"""Return supervisor docker logs.
Expand Down
Loading

0 comments on commit 803eb0f

Please sign in to comment.