mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-09 02:06:30 +00:00
Support new API for add-on STDIN support (#207)
* Add function to write data to add-on stdin with API * Update API Doc * Add to api * Update addon.py
This commit is contained in:
parent
f167197640
commit
bd1c8be1e1
6
API.md
6
API.md
@ -355,6 +355,7 @@ Get all available addons.
|
|||||||
"logo": "bool",
|
"logo": "bool",
|
||||||
"audio": "bool",
|
"audio": "bool",
|
||||||
"gpio": "bool",
|
"gpio": "bool",
|
||||||
|
"stdin": "bool",
|
||||||
"hassio_api": "bool",
|
"hassio_api": "bool",
|
||||||
"homeassistant_api": "bool"
|
"homeassistant_api": "bool"
|
||||||
}
|
}
|
||||||
@ -395,6 +396,7 @@ Get all available addons.
|
|||||||
"logo": "bool",
|
"logo": "bool",
|
||||||
"hassio_api": "bool",
|
"hassio_api": "bool",
|
||||||
"homeassistant_api": "bool",
|
"homeassistant_api": "bool",
|
||||||
|
"stdin": "bool",
|
||||||
"webui": "null|http(s)://[HOST]:port/xy/zx",
|
"webui": "null|http(s)://[HOST]:port/xy/zx",
|
||||||
"gpio": "bool",
|
"gpio": "bool",
|
||||||
"audio": "bool",
|
"audio": "bool",
|
||||||
@ -458,6 +460,10 @@ Output is the raw Docker log.
|
|||||||
|
|
||||||
Only supported for local build addons
|
Only supported for local build addons
|
||||||
|
|
||||||
|
- POST `/addons/{addon}/stdin`
|
||||||
|
|
||||||
|
Write data to add-on stdin
|
||||||
|
|
||||||
## Host Control
|
## Host Control
|
||||||
|
|
||||||
Communicate over UNIX socket with a host daemon.
|
Communicate over UNIX socket with a host daemon.
|
||||||
|
@ -22,7 +22,7 @@ from ..const import (
|
|||||||
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,
|
ATTR_HASSIO_API, ATTR_AUDIO, ATTR_AUDIO_OUTPUT, ATTR_AUDIO_INPUT,
|
||||||
ATTR_GPIO, ATTR_HOMEASSISTANT_API)
|
ATTR_GPIO, ATTR_HOMEASSISTANT_API, ATTR_STDIN)
|
||||||
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
|
||||||
@ -270,6 +270,11 @@ class Addon(object):
|
|||||||
"""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
|
||||||
|
def with_stdin(self):
|
||||||
|
"""Return True if the add-on access use stdin input."""
|
||||||
|
return self._mesh[ATTR_STDIN]
|
||||||
|
|
||||||
@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."""
|
||||||
@ -561,6 +566,18 @@ class Addon(object):
|
|||||||
await self.docker.run()
|
await self.docker.run()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@check_installed
|
||||||
|
async def write_stdin(self, data):
|
||||||
|
"""Write data to add-on stdin.
|
||||||
|
|
||||||
|
Return a coroutine.
|
||||||
|
"""
|
||||||
|
if not self.with_stdin:
|
||||||
|
_LOGGER.error("Add-on don't support write to stdin!")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return await self.docker.write_stdin(data)
|
||||||
|
|
||||||
@check_installed
|
@check_installed
|
||||||
async def snapshot(self, tar_file):
|
async def snapshot(self, tar_file):
|
||||||
"""Snapshot a state of a addon."""
|
"""Snapshot a state of a addon."""
|
||||||
|
@ -14,7 +14,7 @@ from ..const import (
|
|||||||
ATTR_LOCATON, ATTR_REPOSITORY, ATTR_TIMEOUT, ATTR_NETWORK,
|
ATTR_LOCATON, ATTR_REPOSITORY, ATTR_TIMEOUT, ATTR_NETWORK,
|
||||||
ATTR_AUTO_UPDATE, ATTR_WEBUI, ATTR_AUDIO, ATTR_AUDIO_INPUT,
|
ATTR_AUTO_UPDATE, ATTR_WEBUI, ATTR_AUDIO, ATTR_AUDIO_INPUT,
|
||||||
ATTR_AUDIO_OUTPUT, ATTR_HASSIO_API, ATTR_BUILD_FROM, ATTR_SQUASH,
|
ATTR_AUDIO_OUTPUT, ATTR_HASSIO_API, ATTR_BUILD_FROM, ATTR_SQUASH,
|
||||||
ATTR_ARGS, ATTR_GPIO, ATTR_HOMEASSISTANT_API)
|
ATTR_ARGS, ATTR_GPIO, ATTR_HOMEASSISTANT_API, ATTR_STDIN)
|
||||||
from ..validate import NETWORK_PORT, DOCKER_PORTS, ALSA_CHANNEL
|
from ..validate import NETWORK_PORT, DOCKER_PORTS, ALSA_CHANNEL
|
||||||
|
|
||||||
|
|
||||||
@ -98,6 +98,7 @@ SCHEMA_ADDON_CONFIG = vol.Schema({
|
|||||||
vol.Optional(ATTR_GPIO, default=False): vol.Boolean(),
|
vol.Optional(ATTR_GPIO, default=False): vol.Boolean(),
|
||||||
vol.Optional(ATTR_HASSIO_API, default=False): vol.Boolean(),
|
vol.Optional(ATTR_HASSIO_API, default=False): vol.Boolean(),
|
||||||
vol.Optional(ATTR_HOMEASSISTANT_API, default=False): vol.Boolean(),
|
vol.Optional(ATTR_HOMEASSISTANT_API, default=False): vol.Boolean(),
|
||||||
|
vol.Optional(ATTR_STDIN, 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(SCHEMA_ELEMENT, [
|
vol.Coerce(str): vol.Any(SCHEMA_ELEMENT, [
|
||||||
|
@ -104,6 +104,7 @@ class RestAPI(object):
|
|||||||
'/addons/{addon}/rebuild', api_addons.rebuild)
|
'/addons/{addon}/rebuild', api_addons.rebuild)
|
||||||
self.webapp.router.add_get('/addons/{addon}/logs', api_addons.logs)
|
self.webapp.router.add_get('/addons/{addon}/logs', api_addons.logs)
|
||||||
self.webapp.router.add_get('/addons/{addon}/logo', api_addons.logo)
|
self.webapp.router.add_get('/addons/{addon}/logo', api_addons.logo)
|
||||||
|
self.webapp.router.add_post('/addons/{addon}/stdin', api_addons.stdin)
|
||||||
|
|
||||||
def register_security(self):
|
def register_security(self):
|
||||||
"""Register security function."""
|
"""Register security function."""
|
||||||
|
@ -13,7 +13,7 @@ from ..const import (
|
|||||||
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,
|
ATTR_AUDIO, ATTR_AUDIO_INPUT, ATTR_AUDIO_OUTPUT, ATTR_HASSIO_API,
|
||||||
ATTR_GPIO, ATTR_HOMEASSISTANT_API, BOOT_AUTO, BOOT_MANUAL,
|
ATTR_GPIO, ATTR_HOMEASSISTANT_API, ATTR_STDIN, BOOT_AUTO, BOOT_MANUAL,
|
||||||
CONTENT_TYPE_PNG, CONTENT_TYPE_BINARY)
|
CONTENT_TYPE_PNG, CONTENT_TYPE_BINARY)
|
||||||
from ..validate import DOCKER_PORTS
|
from ..validate import DOCKER_PORTS
|
||||||
|
|
||||||
@ -78,6 +78,7 @@ 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_STDIN: addon.with_stdin,
|
||||||
ATTR_HASSIO_API: addon.access_hassio_api,
|
ATTR_HASSIO_API: addon.access_hassio_api,
|
||||||
ATTR_HOMEASSISTANT_API: addon.access_homeassistant_api,
|
ATTR_HOMEASSISTANT_API: addon.access_homeassistant_api,
|
||||||
ATTR_AUDIO: addon.with_audio,
|
ATTR_AUDIO: addon.with_audio,
|
||||||
@ -129,6 +130,7 @@ 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_STDIN: addon.with_stdin,
|
||||||
ATTR_HASSIO_API: addon.access_hassio_api,
|
ATTR_HASSIO_API: addon.access_hassio_api,
|
||||||
ATTR_HOMEASSISTANT_API: addon.access_homeassistant_api,
|
ATTR_HOMEASSISTANT_API: addon.access_homeassistant_api,
|
||||||
ATTR_GPIO: addon.with_gpio,
|
ATTR_GPIO: addon.with_gpio,
|
||||||
@ -242,3 +244,13 @@ class APIAddons(object):
|
|||||||
|
|
||||||
with addon.path_logo.open('rb') as png:
|
with addon.path_logo.open('rb') as png:
|
||||||
return png.read()
|
return png.read()
|
||||||
|
|
||||||
|
@api_process
|
||||||
|
async def stdin(self, request):
|
||||||
|
"""Write to stdin of addon."""
|
||||||
|
addon = self._extract_addon(request)
|
||||||
|
if not addon.with_stdin:
|
||||||
|
raise RuntimeError("STDIN not supported by addons")
|
||||||
|
|
||||||
|
data = await request.read()
|
||||||
|
return asyncio.shield(addon.write_stdin(data), loop=self.loop)
|
||||||
|
@ -86,6 +86,7 @@ ATTR_STATE = 'state'
|
|||||||
ATTR_SCHEMA = 'schema'
|
ATTR_SCHEMA = 'schema'
|
||||||
ATTR_IMAGE = 'image'
|
ATTR_IMAGE = 'image'
|
||||||
ATTR_LOGO = 'logo'
|
ATTR_LOGO = 'logo'
|
||||||
|
ATTR_STDIN = 'stdin'
|
||||||
ATTR_ADDONS_REPOSITORIES = 'addons_repositories'
|
ATTR_ADDONS_REPOSITORIES = 'addons_repositories'
|
||||||
ATTR_REPOSITORY = 'repository'
|
ATTR_REPOSITORY = 'repository'
|
||||||
ATTR_REPOSITORIES = 'repositories'
|
ATTR_REPOSITORIES = 'repositories'
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
"""Init file for HassIO addon docker object."""
|
"""Init file for HassIO addon docker object."""
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
import docker
|
import docker
|
||||||
import requests
|
import requests
|
||||||
@ -171,6 +172,7 @@ class DockerAddon(DockerInterface):
|
|||||||
name=self.name,
|
name=self.name,
|
||||||
hostname=self.hostname,
|
hostname=self.hostname,
|
||||||
detach=True,
|
detach=True,
|
||||||
|
stdin_open=self.addon.with_stdin,
|
||||||
network_mode=self.network_mode,
|
network_mode=self.network_mode,
|
||||||
ports=self.ports,
|
ports=self.ports,
|
||||||
extra_hosts=self.network_mapping,
|
extra_hosts=self.network_mapping,
|
||||||
@ -278,3 +280,35 @@ class DockerAddon(DockerInterface):
|
|||||||
"""
|
"""
|
||||||
self._stop()
|
self._stop()
|
||||||
return self._run()
|
return self._run()
|
||||||
|
|
||||||
|
@docker_process
|
||||||
|
def write_stdin(self, data):
|
||||||
|
"""Write to add-on stdin."""
|
||||||
|
return self.loop.run_in_executor(None, self._write_stdin, data)
|
||||||
|
|
||||||
|
def _write_stdin(self, data):
|
||||||
|
"""Write to add-on stdin.
|
||||||
|
|
||||||
|
Need run inside executor.
|
||||||
|
"""
|
||||||
|
if not self._is_running():
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
# load needed docker objects
|
||||||
|
container = self.docker.containers.get(self.name)
|
||||||
|
socket = container.attach_socket(params={'stdin': 1, 'stream': 1})
|
||||||
|
except docker.errors.DockerException as err:
|
||||||
|
_LOGGER.error("Can't attach to %s stdin -> %s", self.name, err)
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
# write to stdin
|
||||||
|
data += b"\n"
|
||||||
|
os.write(socket.fileno(), data)
|
||||||
|
socket.close()
|
||||||
|
except OSError as err:
|
||||||
|
_LOGGER.error("Can't write to %s stdin -> %s", self.name, err)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
Loading…
x
Reference in New Issue
Block a user