diff --git a/API.md b/API.md index 80e96c04c..11ab099c9 100644 --- a/API.md +++ b/API.md @@ -236,15 +236,6 @@ return: } ``` -- POST `/host/options` - -```json -{ - "audio_input": "0,0", - "audio_output": "0,0" -} -``` - - POST `/host/update` Optional: diff --git a/hassio/addons/addon.py b/hassio/addons/addon.py index 8900b7dd6..cf0a7a3cc 100644 --- a/hassio/addons/addon.py +++ b/hassio/addons/addon.py @@ -621,6 +621,11 @@ class Addon(CoreSysAttributes): "Remove Home-Assistant addon data folder %s", self.path_data) shutil.rmtree(str(self.path_data)) + # Cleanup audio settings + if self.path_asound.exists(): + with suppress(OSError): + self.path_asound.unlink() + self._set_uninstall() return True @@ -647,13 +652,12 @@ class Addon(CoreSysAttributes): return await self.instance.run() @check_installed - async def stop(self): - """Stop addon.""" - try: - return self.instance.stop() - finally: - with suppress(OSError): - self.path_asound.unlink() + def stop(self): + """Stop addon. + + Return a coroutine. + """ + return self.instance.stop() @check_installed async def update(self): diff --git a/hassio/addons/validate.py b/hassio/addons/validate.py index c5ed81d28..cc0fde405 100644 --- a/hassio/addons/validate.py +++ b/hassio/addons/validate.py @@ -19,7 +19,7 @@ from ..const import ( ATTR_ARGS, ATTR_GPIO, ATTR_HOMEASSISTANT_API, ATTR_STDIN, ATTR_LEGACY, ATTR_HOST_DBUS, ATTR_AUTO_UART, ATTR_SERVICES, ATTR_DISCOVERY, ATTR_SECCOMP, ATTR_APPARMOR) -from ..validate import NETWORK_PORT, DOCKER_PORTS, ALSA_CHANNEL +from ..validate import NETWORK_PORT, DOCKER_PORTS, ALSA_DEVICE _LOGGER = logging.getLogger(__name__) @@ -165,8 +165,8 @@ SCHEMA_ADDON_USER = vol.Schema({ vol.Optional(ATTR_BOOT): vol.In([BOOT_AUTO, BOOT_MANUAL]), vol.Optional(ATTR_NETWORK): DOCKER_PORTS, - vol.Optional(ATTR_AUDIO_OUTPUT): ALSA_CHANNEL, - vol.Optional(ATTR_AUDIO_INPUT): ALSA_CHANNEL, + vol.Optional(ATTR_AUDIO_OUTPUT): ALSA_DEVICE, + vol.Optional(ATTR_AUDIO_INPUT): ALSA_DEVICE, }, extra=vol.REMOVE_EXTRA) diff --git a/hassio/api/addons.py b/hassio/api/addons.py index bb00750e2..987e82f36 100644 --- a/hassio/api/addons.py +++ b/hassio/api/addons.py @@ -20,7 +20,7 @@ from ..const import ( ATTR_DISCOVERY, ATTR_SECCOMP, ATTR_APPARMOR, CONTENT_TYPE_PNG, CONTENT_TYPE_BINARY, CONTENT_TYPE_TEXT) from ..coresys import CoreSysAttributes -from ..validate import DOCKER_PORTS +from ..validate import DOCKER_PORTS, ALSA_DEVICE _LOGGER = logging.getLogger(__name__) @@ -33,6 +33,8 @@ SCHEMA_OPTIONS = vol.Schema({ vol.Optional(ATTR_BOOT): vol.In([BOOT_AUTO, BOOT_MANUAL]), vol.Optional(ATTR_NETWORK): vol.Any(None, DOCKER_PORTS), vol.Optional(ATTR_AUTO_UPDATE): vol.Boolean(), + vol.Optional(ATTR_AUDIO_OUTPUT): ALSA_DEVICE, + vol.Optional(ATTR_AUDIO_INPUT): ALSA_DEVICE, }) diff --git a/hassio/api/hardware.py b/hassio/api/hardware.py new file mode 100644 index 000000000..add5515f6 --- /dev/null +++ b/hassio/api/hardware.py @@ -0,0 +1,34 @@ +"""Init file for HassIO hardware rest api.""" +import logging + +from .utils import api_process +from ..const import ( + ATTR_SERIAL, ATTR_DISK, ATTR_GPIO, ATTR_AUDIO, ATTR_INPUT, ATTR_OUTPUT) +from ..coresys import CoreSysAttributes + +_LOGGER = logging.getLogger(__name__) + + +class APIHardware(CoreSysAttributes): + """Handle rest api for hardware functions.""" + + @api_process + async def info(self, request): + """Show hardware info.""" + return { + ATTR_SERIAL: list(self._hardware.serial_devices), + ATTR_INPUT: list(self._hardware.input_devices), + ATTR_DISK: list(self._hardware.disk_devices), + ATTR_GPIO: list(self._hardware.gpio_devices), + ATTR_AUDIO: self._hardware.audio_devices, + } + + @api_process + async def audio(self, request): + """Show ALSA audio devices.""" + return { + ATTR_AUDIO: { + ATTR_INPUT: self._audio.input_devices, + ATTR_OUTPUT: self._audio.output_devices, + } + } diff --git a/hassio/api/host.py b/hassio/api/host.py index 7bc69826e..b5efdce51 100644 --- a/hassio/api/host.py +++ b/hassio/api/host.py @@ -7,10 +7,8 @@ import voluptuous as vol from .utils import api_process_hostcontrol, api_process, api_validate from ..const import ( ATTR_VERSION, ATTR_LAST_VERSION, ATTR_TYPE, ATTR_HOSTNAME, ATTR_FEATURES, - ATTR_OS, ATTR_SERIAL, ATTR_INPUT, ATTR_DISK, ATTR_AUDIO, ATTR_AUDIO_INPUT, - ATTR_AUDIO_OUTPUT, ATTR_GPIO) + ATTR_OS) from ..coresys import CoreSysAttributes -from ..validate import ALSA_CHANNEL _LOGGER = logging.getLogger(__name__) @@ -18,12 +16,6 @@ SCHEMA_VERSION = vol.Schema({ vol.Optional(ATTR_VERSION): vol.Coerce(str), }) -SCHEMA_OPTIONS = vol.Schema({ - vol.Optional(ATTR_AUDIO_OUTPUT): ALSA_CHANNEL, - vol.Optional(ATTR_AUDIO_INPUT): ALSA_CHANNEL, -}) - - class APIHost(CoreSysAttributes): """Handle rest api for host functions.""" @@ -39,19 +31,6 @@ class APIHost(CoreSysAttributes): ATTR_OS: self._host_control.os_info, } - @api_process - async def options(self, request): - """Process host options.""" - body = await api_validate(SCHEMA_OPTIONS, request) - - if ATTR_AUDIO_OUTPUT in body: - self._config.audio_output = body[ATTR_AUDIO_OUTPUT] - if ATTR_AUDIO_INPUT in body: - self._config.audio_input = body[ATTR_AUDIO_INPUT] - - self._config.save_data() - return True - @api_process_hostcontrol def reboot(self, request): """Reboot host.""" @@ -79,14 +58,3 @@ class APIHost(CoreSysAttributes): return await asyncio.shield( self._host_control.update(version=version), loop=self._loop) - - @api_process - async def hardware(self, request): - """Return local hardware infos.""" - return { - ATTR_SERIAL: list(self._hardware.serial_devices), - ATTR_INPUT: list(self._hardware.input_devices), - ATTR_DISK: list(self._hardware.disk_devices), - ATTR_GPIO: list(self._hardware.gpio_devices), - ATTR_AUDIO: self._hardware.audio_devices, - } diff --git a/hassio/bootstrap.py b/hassio/bootstrap.py index 066cfd328..1a465dea4 100644 --- a/hassio/bootstrap.py +++ b/hassio/bootstrap.py @@ -7,7 +7,6 @@ from pathlib import Path from colorlog import ColoredFormatter -from .audio import AlsaAudio from .addons import AddonManager from .api import RestAPI from .const import SOCKET_DOCKER @@ -18,6 +17,7 @@ from .snapshots import SnapshotManager from .tasks import Tasks from .updater import Updater from .services import ServiceManager +from .host.audio import AlsaAudio _LOGGER = logging.getLogger(__name__) diff --git a/hassio/host/audio.py b/hassio/host/audio.py index a1c522cb7..41ce51fd7 100644 --- a/hassio/host/audio.py +++ b/hassio/host/audio.py @@ -84,14 +84,30 @@ class AlsaAudio(CoreSysAttributes): def default(self): """Generate ALSA default setting.""" if ATTR_DEFAULT in self._data: - return self._data[ATTR_DEFAULT] + default = self._data[ATTR_DEFAULT] + else: + default = None - database = self._audio_database() - alsa_input = database.get(self._machine, {}).get(ATTR_INPUT, "0,0") - alsa_output = database.get(self._machine, {}).get(ATTR_OUTPUT, "0,0") + # Init defaults + if default is None: + database = self._audio_database() + alsa_input = database.get(self._machine, {}).get(ATTR_INPUT) + alsa_output = database.get(self._machine, {}).get(ATTR_OUTPUT) - self._data[ATTR_DEFAULT] = DefaultConfig(alsa_input, alsa_output) - return self._data[ATTR_DEFAULT] + default = self._data[ATTR_DEFAULT] = \ + DefaultConfig(alsa_input, alsa_output) + + # Search exists/new output + if default.output is None and self.output_devices: + default.output = next(iter(self.output_devices)) + _LOGGER.info("Detect output device %s", default.output) + + # Search exists/new input + if default.input is None and self.input_devices: + default.input = next(iter(self.input_devices)) + _LOGGER.info("Detect input device %s", default.input) + + return default def asound(self, alsa_input=None, alsa_output=None): """Generate a asound data.""" diff --git a/hassio/host/audiodb.json b/hassio/host/audiodb.json index a28c1a746..f6cccd456 100644 --- a/hassio/host/audiodb.json +++ b/hassio/host/audiodb.json @@ -1,18 +1,18 @@ { "raspberrypi3": { "bcm2835 - bcm2835 ALSA": { - "0,0": "Jack", - "0,1": "HDMI" + "0,0": "Raspberry Jack", + "0,1": "Raspberry HDMI" }, "output": "0,0", - "input": "1,0" + "input": null }, "raspberrypi2": { "output": "0,0", - "input": "1,0" + "input": null }, "raspberrypi": { "output": "0,0", - "input": "1,0" + "input": null } } diff --git a/hassio/validate.py b/hassio/validate.py index 05d23cac6..480c8592f 100644 --- a/hassio/validate.py +++ b/hassio/validate.py @@ -7,18 +7,17 @@ import pytz from .const import ( ATTR_IMAGE, ATTR_LAST_VERSION, ATTR_CHANNEL, ATTR_TIMEZONE, - ATTR_ADDONS_CUSTOM_LIST, ATTR_AUDIO_OUTPUT, ATTR_AUDIO_INPUT, - ATTR_PASSWORD, ATTR_HOMEASSISTANT, ATTR_HASSIO, ATTR_BOOT, ATTR_LAST_BOOT, - ATTR_SSL, ATTR_PORT, ATTR_WATCHDOG, ATTR_WAIT_BOOT, ATTR_UUID, - CHANNEL_STABLE, CHANNEL_BETA, CHANNEL_DEV) + ATTR_ADDONS_CUSTOM_LIST, ATTR_PASSWORD, ATTR_HOMEASSISTANT, ATTR_HASSIO, + ATTR_BOOT, ATTR_LAST_BOOT, ATTR_SSL, ATTR_PORT, ATTR_WATCHDOG, + ATTR_WAIT_BOOT, ATTR_UUID, CHANNEL_STABLE, CHANNEL_BETA, CHANNEL_DEV) RE_REPOSITORY = re.compile(r"^(?P[^#]+)(?:#(?P[\w\-]+))?$") NETWORK_PORT = vol.All(vol.Coerce(int), vol.Range(min=1, max=65535)) -ALSA_CHANNEL = vol.Match(r"\d+,\d+") WAIT_BOOT = vol.All(vol.Coerce(int), vol.Range(min=1, max=60)) DOCKER_IMAGE = vol.Match(r"^[\w{}]+/[\-\w{}]+$") +ALSA_DEVICE = vol.Any(None, vol.Match(r"\d+,\d+")) CHANNELS = vol.In([CHANNEL_STABLE, CHANNEL_BETA, CHANNEL_DEV]) @@ -110,7 +109,5 @@ SCHEMA_HASSIO_CONFIG = vol.Schema({ vol.Optional(ATTR_ADDONS_CUSTOM_LIST, default=[ "https://github.com/hassio-addons/repository", ]): REPOSITORIES, - vol.Optional(ATTR_AUDIO_OUTPUT): ALSA_CHANNEL, - vol.Optional(ATTR_AUDIO_INPUT): ALSA_CHANNEL, vol.Optional(ATTR_WAIT_BOOT, default=5): WAIT_BOOT, }, extra=vol.REMOVE_EXTRA)