Expose new function to add-ons (#138)

* Expose new function to add-ons

* Rename `hassio` to `hassio_api`

* fix lint

* done
This commit is contained in:
Pascal Vizeli 2017-08-08 16:54:42 +02:00 committed by GitHub
parent cd3b382902
commit 8d468328f3
8 changed files with 99 additions and 7 deletions

14
API.md
View File

@ -336,7 +336,9 @@ Get all available addons.
"privileged": ["NET_ADMIN", "SYS_ADMIN"], "privileged": ["NET_ADMIN", "SYS_ADMIN"],
"devices": ["/dev/xy"], "devices": ["/dev/xy"],
"url": "null|url", "url": "null|url",
"logo": "bool" "logo": "bool",
"audio": "bool",
"hassio_api": "bool"
} }
], ],
"repositories": [ "repositories": [
@ -373,7 +375,11 @@ Get all available addons.
"privileged": ["NET_ADMIN", "SYS_ADMIN"], "privileged": ["NET_ADMIN", "SYS_ADMIN"],
"devices": ["/dev/xy"], "devices": ["/dev/xy"],
"logo": "bool", "logo": "bool",
"webui": "null|http(s)://[HOST]:port/xy/zx" "hassio_api": "bool",
"webui": "null|http(s)://[HOST]:port/xy/zx",
"audio": "bool",
"audio_input": "null|0,0",
"audio_output": "null|0,0"
} }
``` ```
@ -389,10 +395,12 @@ Get all available addons.
"CONTAINER": "port|[ip, port]" "CONTAINER": "port|[ip, port]"
}, },
"options": {}, "options": {},
"audio_output": "null|0,0",
"audio_input": "null|0,0"
} }
``` ```
For reset custom network settings, set it `null`. For reset custom network/audio settings, set it `null`.
- POST `/addons/{addon}/start` - POST `/addons/{addon}/start`

View File

@ -19,7 +19,8 @@ from ..const import (
ATTR_URL, ATTR_ARCH, ATTR_LOCATON, ATTR_DEVICES, ATTR_ENVIRONMENT, ATTR_URL, ATTR_ARCH, ATTR_LOCATON, ATTR_DEVICES, ATTR_ENVIRONMENT,
ATTR_HOST_NETWORK, ATTR_TMPFS, ATTR_PRIVILEGED, ATTR_STARTUP, ATTR_HOST_NETWORK, ATTR_TMPFS, ATTR_PRIVILEGED, ATTR_STARTUP,
STATE_STARTED, STATE_STOPPED, STATE_NONE, ATTR_USER, ATTR_SYSTEM, STATE_STARTED, STATE_STOPPED, STATE_NONE, ATTR_USER, ATTR_SYSTEM,
ATTR_STATE, ATTR_TIMEOUT, ATTR_AUTO_UPDATE, ATTR_NETWORK, ATTR_WEBUI) ATTR_STATE, ATTR_TIMEOUT, ATTR_AUTO_UPDATE, ATTR_NETWORK, ATTR_WEBUI,
ATTR_HASSIO_API, ATTR_AUDIO, ATTR_AUDIO_OUTPUT, ATTR_AUDIO_INPUT)
from .util import check_installed from .util import check_installed
from ..dock.addon import DockerAddon from ..dock.addon import DockerAddon
from ..tools import write_json_file, read_json_file from ..tools import write_json_file, read_json_file
@ -244,6 +245,56 @@ class Addon(object):
"""Return list of privilege.""" """Return list of privilege."""
return self._mesh.get(ATTR_PRIVILEGED) return self._mesh.get(ATTR_PRIVILEGED)
@property
def use_hassio_api(self):
"""Return True if the add-on access to hassio api."""
return self._mesh[ATTR_HASSIO_API]
@property
def with_audio(self):
"""Return True if the add-on access to audio."""
return self._mesh[ATTR_AUDIO]
@property
def audio_output(self):
"""Return ALSA config for output or None."""
if not self.with_audio:
return
setting = self.config.audio_output
if self.is_installed and ATTR_AUDIO_OUTPUT in self.data.user[self._id]:
setting = self.data.user[self._id][ATTR_AUDIO_OUTPUT]
return setting
@audio_output.setter
def audio_output(self, value):
"""Set/remove custom audio output settings."""
if value is None:
self.data.user[self._id].pop(ATTR_AUDIO_OUTPUT, None)
else:
self.data.user[self._id][ATTR_AUDIO_OUTPUT] = value
self.data.save()
@property
def audio_input(self):
"""Return ALSA config for input or None."""
if not self.with_audio:
return
setting = self.config.audio_input
if self.is_installed and ATTR_AUDIO_INPUT in self.data.user[self._id]:
setting = self.data.user[self._id][ATTR_AUDIO_INPUT]
return setting
@audio_input.setter
def audio_input(self, value):
"""Set/remove custom audio input settings."""
if value is None:
self.data.user[self._id].pop(ATTR_AUDIO_INPUT, None)
else:
self.data.user[self._id][ATTR_AUDIO_INPUT] = value
self.data.save()
@property @property
def url(self): def url(self):
"""Return url of addon.""" """Return url of addon."""

View File

@ -10,8 +10,9 @@ from ..const import (
ARCH_AARCH64, ARCH_AMD64, ARCH_I386, ATTR_TMPFS, ATTR_PRIVILEGED, ARCH_AARCH64, ARCH_AMD64, ARCH_I386, ATTR_TMPFS, ATTR_PRIVILEGED,
ATTR_USER, ATTR_STATE, ATTR_SYSTEM, STATE_STARTED, STATE_STOPPED, ATTR_USER, ATTR_STATE, ATTR_SYSTEM, STATE_STARTED, STATE_STOPPED,
ATTR_LOCATON, ATTR_REPOSITORY, ATTR_TIMEOUT, ATTR_NETWORK, ATTR_LOCATON, ATTR_REPOSITORY, ATTR_TIMEOUT, ATTR_NETWORK,
ATTR_AUTO_UPDATE, ATTR_WEBUI) ATTR_AUTO_UPDATE, ATTR_WEBUI, ATTR_AUDIO, ATTR_AUDIO_INPUT,
from ..validate import NETWORK_PORT, DOCKER_PORTS ATTR_AUDIO_OUTPUT, ATTR_HASSIO_API)
from ..validate import NETWORK_PORT, DOCKER_PORTS, ALSA_CHANNEL
MAP_VOLUME = r"^(config|ssl|addons|backup|share)(?::(rw|:ro))?$" MAP_VOLUME = r"^(config|ssl|addons|backup|share)(?::(rw|:ro))?$"
@ -73,6 +74,8 @@ SCHEMA_ADDON_CONFIG = vol.Schema({
vol.Optional(ATTR_MAP, default=[]): [vol.Match(MAP_VOLUME)], vol.Optional(ATTR_MAP, default=[]): [vol.Match(MAP_VOLUME)],
vol.Optional(ATTR_ENVIRONMENT): {vol.Match(r"\w*"): vol.Coerce(str)}, vol.Optional(ATTR_ENVIRONMENT): {vol.Match(r"\w*"): vol.Coerce(str)},
vol.Optional(ATTR_PRIVILEGED): [vol.In(PRIVILEGED_ALL)], vol.Optional(ATTR_PRIVILEGED): [vol.In(PRIVILEGED_ALL)],
vol.Optional(ATTR_AUDIO, default=False): vol.Boolean(),
vol.Optional(ATTR_HASSIO_API, default=False): vol.Boolean(),
vol.Required(ATTR_OPTIONS): dict, vol.Required(ATTR_OPTIONS): dict,
vol.Required(ATTR_SCHEMA): vol.Any(vol.Schema({ vol.Required(ATTR_SCHEMA): vol.Any(vol.Schema({
vol.Coerce(str): vol.Any(ADDON_ELEMENT, [ vol.Coerce(str): vol.Any(ADDON_ELEMENT, [
@ -101,6 +104,8 @@ SCHEMA_ADDON_USER = vol.Schema({
vol.Optional(ATTR_BOOT): vol.Optional(ATTR_BOOT):
vol.In([BOOT_AUTO, BOOT_MANUAL]), vol.In([BOOT_AUTO, BOOT_MANUAL]),
vol.Optional(ATTR_NETWORK): DOCKER_PORTS, vol.Optional(ATTR_NETWORK): DOCKER_PORTS,
vol.Optional(ATTR_AUDIO_OUTPUT): ALSA_CHANNEL,
vol.Optional(ATTR_AUDIO_INPUT): ALSA_CHANNEL,
}) })

View File

@ -12,6 +12,7 @@ from ..const import (
ATTR_BUILD, ATTR_AUTO_UPDATE, ATTR_NETWORK, ATTR_HOST_NETWORK, ATTR_SLUG, ATTR_BUILD, ATTR_AUTO_UPDATE, ATTR_NETWORK, ATTR_HOST_NETWORK, ATTR_SLUG,
ATTR_SOURCE, ATTR_REPOSITORIES, ATTR_ADDONS, ATTR_ARCH, ATTR_MAINTAINER, ATTR_SOURCE, ATTR_REPOSITORIES, ATTR_ADDONS, ATTR_ARCH, ATTR_MAINTAINER,
ATTR_INSTALLED, ATTR_LOGO, ATTR_WEBUI, ATTR_DEVICES, ATTR_PRIVILEGED, ATTR_INSTALLED, ATTR_LOGO, ATTR_WEBUI, ATTR_DEVICES, ATTR_PRIVILEGED,
ATTR_AUDIO, ATTR_AUDIO_INPUT, ATTR_AUDIO_OUTPUT, ATTR_HASSIO_API,
BOOT_AUTO, BOOT_MANUAL, CONTENT_TYPE_PNG, CONTENT_TYPE_BINARY) BOOT_AUTO, BOOT_MANUAL, CONTENT_TYPE_PNG, CONTENT_TYPE_BINARY)
from ..validate import DOCKER_PORTS from ..validate import DOCKER_PORTS
@ -76,6 +77,8 @@ class APIAddons(object):
ATTR_DEVICES: self._pretty_devices(addon), ATTR_DEVICES: self._pretty_devices(addon),
ATTR_URL: addon.url, ATTR_URL: addon.url,
ATTR_LOGO: addon.with_logo, ATTR_LOGO: addon.with_logo,
ATTR_HASSIO_API: addon.use_hassio_api,
ATTR_AUDIO: addon.with_audio,
}) })
data_repositories = [] data_repositories = []
@ -123,6 +126,10 @@ class APIAddons(object):
ATTR_DEVICES: self._pretty_devices(addon), ATTR_DEVICES: self._pretty_devices(addon),
ATTR_LOGO: addon.with_logo, ATTR_LOGO: addon.with_logo,
ATTR_WEBUI: addon.webui, ATTR_WEBUI: addon.webui,
ATTR_HASSIO_API: addon.use_hassio_api,
ATTR_AUDIO: addon.with_audio,
ATTR_AUDIO_INPUT: addon.audio_input,
ATTR_AUDIO_OUTPUT: addon.audio_output,
} }
@api_process @api_process
@ -144,6 +151,10 @@ class APIAddons(object):
addon.auto_update = body[ATTR_AUTO_UPDATE] addon.auto_update = body[ATTR_AUTO_UPDATE]
if ATTR_NETWORK in body: if ATTR_NETWORK in body:
addon.ports = body[ATTR_NETWORK] addon.ports = body[ATTR_NETWORK]
if ATTR_AUDIO_INPUT in body:
addon.audio_input = body[ATTR_AUDIO_INPUT]
if ATTR_AUDIO_OUTPUT in body:
addon.audio_output = body[ATTR_AUDIO_OUTPUT]
return True return True

View File

@ -52,7 +52,6 @@ class APIHost(object):
if ATTR_AUDIO_OUTPUT in body: if ATTR_AUDIO_OUTPUT in body:
self.config.audio_output = body[ATTR_AUDIO_OUTPUT] self.config.audio_output = body[ATTR_AUDIO_OUTPUT]
if ATTR_AUDIO_INPUT in body: if ATTR_AUDIO_INPUT in body:
self.config.audio_input = body[ATTR_AUDIO_INPUT] self.config.audio_input = body[ATTR_AUDIO_INPUT]

View File

@ -96,6 +96,7 @@ ATTR_SYSTEM = 'system'
ATTR_SNAPSHOTS = 'snapshots' ATTR_SNAPSHOTS = 'snapshots'
ATTR_HOMEASSISTANT = 'homeassistant' ATTR_HOMEASSISTANT = 'homeassistant'
ATTR_HASSIO = 'hassio' ATTR_HASSIO = 'hassio'
ATTR_HASSIO_API = 'hassio_api'
ATTR_FOLDERS = 'folders' ATTR_FOLDERS = 'folders'
ATTR_SIZE = 'size' ATTR_SIZE = 'size'
ATTR_TYPE = 'type' ATTR_TYPE = 'type'

View File

@ -32,6 +32,11 @@ class DockerAddon(DockerBase):
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 {}
if self.addon.with_audio:
addon_env.update({
'ALSA_OUTPUT': self.addon.audio_output,
'ALSA_INPUT': self.addon.audio_input,
})
return { return {
**addon_env, **addon_env,
@ -46,6 +51,16 @@ class DockerAddon(DockerBase):
return {"/tmpfs": "{}".format(options)} return {"/tmpfs": "{}".format(options)}
return None return None
@property
def mapping(self):
"""Return hosts mapping."""
if not self.addon.use_hassio_api:
return None
return {
'hassio': self.config.api_endpoint,
}
@property @property
def volumes(self): def volumes(self):
"""Generate volumes for mappings.""" """Generate volumes for mappings."""
@ -111,6 +126,7 @@ class DockerAddon(DockerBase):
detach=True, detach=True,
network_mode=self.addon.network_mode, network_mode=self.addon.network_mode,
ports=self.addon.ports, ports=self.addon.ports,
extra_hosts=self.mapping,
devices=self.addon.devices, devices=self.addon.devices,
cap_add=self.addon.privileged, cap_add=self.addon.privileged,
environment=self.environment, environment=self.environment,

View File

@ -62,6 +62,7 @@ class Updater(JsonConfig):
""" """
url = URL_HASSIO_VERSION.format(self.upstream) url = URL_HASSIO_VERSION.format(self.upstream)
try: try:
_LOGGER.info("Fetch update data from %s", url)
with async_timeout.timeout(10, loop=self.loop): with async_timeout.timeout(10, loop=self.loop):
async with self.websession.get(url) as request: async with self.websession.get(url) as request:
data = await request.json(content_type=None) data = await request.json(content_type=None)