From a2789ac5409fb8bbda9613ddc72ed569c833b44c Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 11 Apr 2018 23:53:30 +0200 Subject: [PATCH 01/21] Extend Audio support --- API.md | 21 +++++- hassio/addons/addon.py | 57 +++++++++++++---- hassio/bootstrap.py | 2 + hassio/config.py | 27 ++------ hassio/const.py | 3 + hassio/coresys.py | 20 ++++++ hassio/docker/addon.py | 11 +++- hassio/docker/homeassistant.py | 9 ++- hassio/homeassistant.py | 5 ++ hassio/host/__init__.py | 1 + hassio/host/asound.tmpl | 17 +++++ hassio/host/audio.py | 114 +++++++++++++++++++++++++++++++++ hassio/host/audiodb.json | 18 ++++++ 13 files changed, 265 insertions(+), 40 deletions(-) create mode 100644 hassio/host/__init__.py create mode 100644 hassio/host/asound.tmpl create mode 100644 hassio/host/audio.py create mode 100644 hassio/host/audiodb.json diff --git a/API.md b/API.md index aac11806b..80e96c04c 100644 --- a/API.md +++ b/API.md @@ -255,7 +255,11 @@ Optional: } ``` -- GET `/host/hardware` +- POST `/host/reload` + +### Hardware + +- GET `/hardware/info` ```json { "serial": ["/dev/xy"], @@ -274,7 +278,20 @@ Optional: } ``` -- POST `/host/reload` +- GET `/hardware/audio` +```json +{ + "audio": { + "input": { + "0,0": "Mic" + }, + "output": { + "1,0": "Jack", + "1,1": "HDMI" + } + } +} +``` ### Network diff --git a/hassio/addons/addon.py b/hassio/addons/addon.py index b3d6660d8..8900b7dd6 100644 --- a/hassio/addons/addon.py +++ b/hassio/addons/addon.py @@ -1,4 +1,5 @@ """Init file for HassIO addons.""" +from contextlib import suppress from copy import deepcopy import logging import json @@ -372,15 +373,14 @@ class Addon(CoreSysAttributes): if not self.with_audio: return None - 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 + return self._data.user[self._id][ATTR_AUDIO_OUTPUT] + return self._audio.default.output @audio_output.setter def audio_output(self, value): - """Set/remove custom audio output settings.""" + """Set/reset audio output settings.""" if value is None: self._data.user[self._id].pop(ATTR_AUDIO_OUTPUT, None) else: @@ -392,14 +392,13 @@ class Addon(CoreSysAttributes): if not self.with_audio: return None - 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 + return self._data.user[self._id][ATTR_AUDIO_INPUT] + return self._audio.default.input @audio_input.setter def audio_input(self, value): - """Set/remove custom audio input settings.""" + """Set/reset audio input settings.""" if value is None: self._data.user[self._id].pop(ATTR_AUDIO_INPUT, None) else: @@ -504,6 +503,16 @@ class Addon(CoreSysAttributes): """Return path to custom AppArmor profile.""" return Path(self.path_location, 'apparmor') + @property + def path_asound(self): + """Return path to asound config.""" + return Path(self._config.path_tmp, f"{self.slug}_asound") + + @property + def path_extern_asound(self): + """Return path to asound config for docker.""" + return Path(self._config.path_extern_tmp, f"{self.slug}_asound") + def save_data(self): """Save data of addon.""" self._addons.data.save_data() @@ -526,6 +535,20 @@ class Addon(CoreSysAttributes): return False + def write_asound(self): + """Write asound config to file and return True on success.""" + asound_config = self._audio.asound( + alsa_input=self.audio_input, alsa_output=self.audio_output) + + try: + with self.path_asound.open('w') as config_file: + config_file.write(asound_config) + except OSError as err: + _LOGGER.error("Addon %s can't write asound: %s", self._id, err) + return False + + return True + @property def schema(self): """Create a schema for addon options.""" @@ -613,18 +636,24 @@ class Addon(CoreSysAttributes): @check_installed async def start(self): """Set options and start addon.""" + # Options if not self.write_options(): return False + # Sound + if self.with_audio and not self.write_asound(): + return False + return await self.instance.run() @check_installed - def stop(self): - """Stop addon. - - Return a coroutine. - """ - return self.instance.stop() + async def stop(self): + """Stop addon.""" + try: + return self.instance.stop() + finally: + with suppress(OSError): + self.path_asound.unlink() @check_installed async def update(self): diff --git a/hassio/bootstrap.py b/hassio/bootstrap.py index 82ece0f13..066cfd328 100644 --- a/hassio/bootstrap.py +++ b/hassio/bootstrap.py @@ -7,6 +7,7 @@ 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 @@ -28,6 +29,7 @@ def initialize_coresys(loop): # Initialize core objects coresys.updater = Updater(coresys) coresys.api = RestAPI(coresys) + coresys.audio = AlsaAudio(coresys) coresys.supervisor = Supervisor(coresys) coresys.homeassistant = HomeAssistant(coresys) coresys.addons = AddonManager(coresys) diff --git a/hassio/config.py b/hassio/config.py index baf31fb27..2f86ad5ed 100644 --- a/hassio/config.py +++ b/hassio/config.py @@ -6,7 +6,7 @@ from pathlib import Path, PurePath from .const import ( FILE_HASSIO_CONFIG, HASSIO_DATA, ATTR_TIMEZONE, ATTR_ADDONS_CUSTOM_LIST, - ATTR_AUDIO_INPUT, ATTR_AUDIO_OUTPUT, ATTR_LAST_BOOT, ATTR_WAIT_BOOT) + ATTR_LAST_BOOT, ATTR_WAIT_BOOT) from .utils.dt import parse_datetime from .utils.json import JsonConfig from .validate import SCHEMA_HASSIO_CONFIG @@ -136,6 +136,11 @@ class CoreConfig(JsonConfig): """Return hass.io temp folder.""" return Path(HASSIO_DATA, TMP_DATA) + @property + def path_extern_tmp(self): + """Return hass.io temp folder for docker.""" + return PurePath(self.path_extern_hassio, TMP_DATA) + @property def path_backup(self): """Return root backup data folder.""" @@ -174,23 +179,3 @@ class CoreConfig(JsonConfig): return self._data[ATTR_ADDONS_CUSTOM_LIST].remove(repo) - - @property - def audio_output(self): - """Return ALSA audio output card,dev.""" - return self._data.get(ATTR_AUDIO_OUTPUT) - - @audio_output.setter - def audio_output(self, value): - """Set ALSA audio output card,dev.""" - self._data[ATTR_AUDIO_OUTPUT] = value - - @property - def audio_input(self): - """Return ALSA audio input card,dev.""" - return self._data.get(ATTR_AUDIO_INPUT) - - @audio_input.setter - def audio_input(self, value): - """Set ALSA audio input card,dev.""" - self._data[ATTR_AUDIO_INPUT] = value diff --git a/hassio/const.py b/hassio/const.py index 795cbd327..ab1b3742a 100644 --- a/hassio/const.py +++ b/hassio/const.py @@ -27,6 +27,7 @@ DOCKER_NETWORK_RANGE = ip_network('172.30.33.0/24') LABEL_VERSION = 'io.hass.version' LABEL_ARCH = 'io.hass.arch' LABEL_TYPE = 'io.hass.type' +LABEL_MACHINE = 'io.hass.machine' META_ADDON = 'addon' META_SUPERVISOR = 'supervisor' @@ -161,6 +162,8 @@ ATTR_CRYPTO = 'crypto' ATTR_BRANCH = 'branch' ATTR_SECCOMP = 'seccomp' ATTR_APPARMOR = 'apparmor' +ATTR_CACHE = 'cache' +ATTR_DEFAULT = 'default' SERVICE_MQTT = 'mqtt' diff --git a/hassio/coresys.py b/hassio/coresys.py index 79a9a9047..1568567e6 100644 --- a/hassio/coresys.py +++ b/hassio/coresys.py @@ -42,6 +42,7 @@ class CoreSys(object): self._snapshots = None self._tasks = None self._services = None + self._audio = None @property def arch(self): @@ -50,6 +51,13 @@ class CoreSys(object): return self._supervisor.arch return None + @property + def machine(self): + """Return running machine type of hass.io system.""" + if self._homeassistant: + return self._homeassistant.machine + return None + @property def dev(self): """Return True if we run dev modus.""" @@ -196,6 +204,18 @@ class CoreSys(object): raise RuntimeError("Services already set!") self._services = value + @property + def audio(self): + """Return ALSA Audio object.""" + return self._audio + + @audio.setter + def audio(self, value): + """Set a ALSA Audio object.""" + if self._audio: + raise RuntimeError("Audio already set!") + self._audio = value + class CoreSysAttributes(object): """Inheret basic CoreSysAttributes.""" diff --git a/hassio/docker/addon.py b/hassio/docker/addon.py index e766be5e0..9dc363235 100644 --- a/hassio/docker/addon.py +++ b/hassio/docker/addon.py @@ -201,7 +201,7 @@ class DockerAddon(DockerInterface): 'bind': "/share", 'mode': addon_mapping[MAP_SHARE] }}) - # init other hardware mappings + # Init other hardware mappings if self.addon.with_gpio: volumes.update({ "/sys/class/gpio": { @@ -212,13 +212,20 @@ class DockerAddon(DockerInterface): }, }) - # host dbus system + # Host dbus system if self.addon.host_dbus: volumes.update({ "/var/run/dbus": { 'bind': "/var/run/dbus", 'mode': 'rw' }}) + # ALSA configuration + if self.addon.with_audio: + volumes.update({ + str(self.addon.path_extern_asound): { + 'bind': "/etc/asound.conf", 'mode': 'ro' + }}) + return volumes def _run(self): diff --git a/hassio/docker/homeassistant.py b/hassio/docker/homeassistant.py index 48d0c9aa0..1ac05b18c 100644 --- a/hassio/docker/homeassistant.py +++ b/hassio/docker/homeassistant.py @@ -4,7 +4,7 @@ import logging import docker from .interface import DockerInterface -from ..const import ENV_TOKEN, ENV_TIME +from ..const import ENV_TOKEN, ENV_TIME, LABEL_MACHINE _LOGGER = logging.getLogger(__name__) @@ -14,6 +14,13 @@ HASS_DOCKER_NAME = 'homeassistant' class DockerHomeAssistant(DockerInterface): """Docker hassio wrapper for HomeAssistant.""" + @property + def machine(self): + """Return machine of Home-Assistant docker image.""" + if self._meta and LABEL_MACHINE in self._meta['Config']['Labels']: + return self._meta['Config']['Labels'][LABEL_MACHINE] + return None + @property def image(self): """Return name of docker image.""" diff --git a/hassio/homeassistant.py b/hassio/homeassistant.py index 21916cfdc..1a22a5087 100644 --- a/hassio/homeassistant.py +++ b/hassio/homeassistant.py @@ -45,6 +45,11 @@ class HomeAssistant(JsonConfig, CoreSysAttributes): _LOGGER.info("No HomeAssistant docker %s found.", self.image) await self.install_landingpage() + @property + def machine(self): + """Return System Machines.""" + return self._docker.machine + @property def api_ip(self): """Return IP of HomeAssistant instance.""" diff --git a/hassio/host/__init__.py b/hassio/host/__init__.py new file mode 100644 index 000000000..01fd3820e --- /dev/null +++ b/hassio/host/__init__.py @@ -0,0 +1 @@ +"""Host function like audio/dbus/systemd.""" diff --git a/hassio/host/asound.tmpl b/hassio/host/asound.tmpl new file mode 100644 index 000000000..248482a73 --- /dev/null +++ b/hassio/host/asound.tmpl @@ -0,0 +1,17 @@ +pcm.!default { + type asym + capture.pcm "mic" + playback.pcm "speaker" +} +pcm.mic { + type plug + slave { + pcm "hw:{$input}" + } +} +pcm.speaker { + type plug + slave { + pcm "hw:{$output}" + } +} diff --git a/hassio/host/audio.py b/hassio/host/audio.py new file mode 100644 index 000000000..a1c522cb7 --- /dev/null +++ b/hassio/host/audio.py @@ -0,0 +1,114 @@ +"""Host Audio-support.""" +from collections import namedtuple +import logging +import json +from pathlib import Path +from string import Template + +from ..const import ( + ATTR_CACHE, ATTR_INPUT, ATTR_OUTPUT, ATTR_DEVICES, ATTR_NAME, ATTR_DEFAULT) +from ..coresys import CoreSysAttributes + +_LOGGER = logging.getLogger(__name__) + +DefaultConfig = namedtuple('DefaultConfig', ['input', 'output']) + + +class AlsaAudio(CoreSysAttributes): + """Handle Audio ALSA host data.""" + + def __init__(self, coresys): + """Initialize Alsa audio system.""" + self.coresys = coresys + self._data = { + ATTR_CACHE: 0, + ATTR_INPUT: {}, + ATTR_OUTPUT: {}, + } + + @property + def input_devices(self): + """Return list of ALSA input devices.""" + self._update_device() + return self._data[ATTR_INPUT] + + @property + def output_devices(self): + """Return list of ALSA output devices.""" + self._update_device() + return self._data[ATTR_OUTPUT] + + def _update_device(self): + """Update Internal device DB.""" + current_id = hash(frozenset(self._hardware.audio_devices)) + + # Need rebuild? + if current_id == self._data[ATTR_CACHE]: + return + + # Init database + _LOGGER.info("Update ALSA device list") + database = self._audio_database() + + # Process devices + for dev_id, dev_data in self._hardware.audio_devices.items(): + for chan_id, chan_type in dev_data[ATTR_DEVICES]: + alsa_id = f"{dev_id},{chan_id}" + if chan_type.endswith('playback'): + key = ATTR_OUTPUT + elif chan_type.endswith('capture'): + key = ATTR_INPUT + else: + _LOGGER.warning("Unknown channel type: %s", chan_type) + continue + + self._data[key][alsa_id] = database.get(self._machine, {}).get( + alsa_id, f"{dev_data[ATTR_NAME]}: {chan_id}") + + self._data[ATTR_CACHE] = current_id + + @staticmethod + def _audio_database(): + """Read local json audio data into dict.""" + json_file = Path(__file__).parent.joinpath('audiodb.json') + + try: + with json_file.open('r') as database: + return json.loads(database.read()) + except (ValueError, OSError) as err: + _LOGGER.warning("Can't read audio DB: %s", err) + + return {} + + @property + def default(self): + """Generate ALSA default setting.""" + if ATTR_DEFAULT in self._data: + return self._data[ATTR_DEFAULT] + + 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") + + self._data[ATTR_DEFAULT] = DefaultConfig(alsa_input, alsa_output) + return self._data[ATTR_DEFAULT] + + def asound(self, alsa_input=None, alsa_output=None): + """Generate a asound data.""" + alsa_input = alsa_input or self.default.input + alsa_output = alsa_output or self.default.output + + # Read Template + asound_file = Path(__file__).parent.joinpath('asound.tmpl') + try: + with asound_file.open('r') as asound: + asound_data = asound.read() + except OSError as err: + _LOGGER.error("Can't read asound.tmpl: %s", err) + return "" + + # Process Template + asound_template = Template(asound_data) + return asound_template.safe_substitute( + input=alsa_input, output=alsa_output + ) diff --git a/hassio/host/audiodb.json b/hassio/host/audiodb.json new file mode 100644 index 000000000..a28c1a746 --- /dev/null +++ b/hassio/host/audiodb.json @@ -0,0 +1,18 @@ +{ + "raspberrypi3": { + "bcm2835 - bcm2835 ALSA": { + "0,0": "Jack", + "0,1": "HDMI" + }, + "output": "0,0", + "input": "1,0" + }, + "raspberrypi2": { + "output": "0,0", + "input": "1,0" + }, + "raspberrypi": { + "output": "0,0", + "input": "1,0" + } +} From 0dd7f8fbaa3b3b3eed9a9ec9165a9dbd7435a84f Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 12 Apr 2018 22:07:41 +0200 Subject: [PATCH 02/21] Fix some comments --- API.md | 9 --------- hassio/addons/addon.py | 18 +++++++++++------- hassio/addons/validate.py | 6 +++--- hassio/api/addons.py | 4 +++- hassio/api/hardware.py | 34 ++++++++++++++++++++++++++++++++++ hassio/api/host.py | 34 +--------------------------------- hassio/bootstrap.py | 2 +- hassio/host/audio.py | 28 ++++++++++++++++++++++------ hassio/host/audiodb.json | 10 +++++----- hassio/validate.py | 11 ++++------- 10 files changed, 84 insertions(+), 72 deletions(-) create mode 100644 hassio/api/hardware.py 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) From b95ab3e95a11967e6b3818669ddbbf17b6207bc5 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 12 Apr 2018 22:15:08 +0200 Subject: [PATCH 03/21] Stage API --- hassio/api/__init__.py | 13 ++++++++++++- hassio/api/host.py | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/hassio/api/__init__.py b/hassio/api/__init__.py index c3d878751..653f910aa 100644 --- a/hassio/api/__init__.py +++ b/hassio/api/__init__.py @@ -7,6 +7,7 @@ from aiohttp import web from .addons import APIAddons from .discovery import APIDiscovery from .homeassistant import APIHomeAssistant +from .hardware import APIHardware from .host import APIHost from .network import APINetwork from .proxy import APIProxy @@ -37,6 +38,7 @@ class RestAPI(CoreSysAttributes): """Register REST API Calls.""" self._register_supervisor() self._register_host() + self._register_hardware() self._register_homeassistant() self._register_proxy() self._register_panel() @@ -53,7 +55,6 @@ class RestAPI(CoreSysAttributes): self.webapp.add_routes([ web.get('/host/info', api_host.info), - web.get('/host/hardware', api_host.hardware), web.post('/host/reboot', api_host.reboot), web.post('/host/shutdown', api_host.shutdown), web.post('/host/update', api_host.update), @@ -71,6 +72,16 @@ class RestAPI(CoreSysAttributes): web.post('/network/options', api_net.options), ]) + def _register_hardware(self): + """Register hardware function.""" + api_hardware = APIHardware() + api_hardware.coresys = self.coresys + + self.webapp.add_routes([ + web.get('/network/info', api_hardware.info), + web.get('/network/audio', api_hardware.audio), + ]) + def _register_supervisor(self): """Register supervisor function.""" api_supervisor = APISupervisor() diff --git a/hassio/api/host.py b/hassio/api/host.py index b5efdce51..a0b63f2c9 100644 --- a/hassio/api/host.py +++ b/hassio/api/host.py @@ -16,6 +16,7 @@ SCHEMA_VERSION = vol.Schema({ vol.Optional(ATTR_VERSION): vol.Coerce(str), }) + class APIHost(CoreSysAttributes): """Handle rest api for host functions.""" From 4e428c2e41eb030758fe406bc390de7e0be41ca7 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 12 Apr 2018 22:39:26 +0200 Subject: [PATCH 04/21] Fix options --- hassio/api/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/hassio/api/__init__.py b/hassio/api/__init__.py index 653f910aa..d610cd8e0 100644 --- a/hassio/api/__init__.py +++ b/hassio/api/__init__.py @@ -58,7 +58,6 @@ class RestAPI(CoreSysAttributes): web.post('/host/reboot', api_host.reboot), web.post('/host/shutdown', api_host.shutdown), web.post('/host/update', api_host.update), - web.post('/host/options', api_host.options), web.post('/host/reload', api_host.reload), ]) From 1e7858bf06560ac98dc233223c5b36ddc047c91c Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 12 Apr 2018 23:00:42 +0200 Subject: [PATCH 05/21] fix url --- hassio/api/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hassio/api/__init__.py b/hassio/api/__init__.py index d610cd8e0..b91d79485 100644 --- a/hassio/api/__init__.py +++ b/hassio/api/__init__.py @@ -77,8 +77,8 @@ class RestAPI(CoreSysAttributes): api_hardware.coresys = self.coresys self.webapp.add_routes([ - web.get('/network/info', api_hardware.info), - web.get('/network/audio', api_hardware.audio), + web.get('/hardware/info', api_hardware.info), + web.get('/hardware/audio', api_hardware.audio), ]) def _register_supervisor(self): From 2c714aa0032c7bf04735fee219547afa1aa127f7 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 12 Apr 2018 23:11:38 +0200 Subject: [PATCH 06/21] fix 1 --- hassio/host/audio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hassio/host/audio.py b/hassio/host/audio.py index 41ce51fd7..6e32b561a 100644 --- a/hassio/host/audio.py +++ b/hassio/host/audio.py @@ -52,7 +52,7 @@ class AlsaAudio(CoreSysAttributes): # Process devices for dev_id, dev_data in self._hardware.audio_devices.items(): - for chan_id, chan_type in dev_data[ATTR_DEVICES]: + for chan_id, chan_type in dev_data[ATTR_DEVICES].items(): alsa_id = f"{dev_id},{chan_id}" if chan_type.endswith('playback'): key = ATTR_OUTPUT From cf00ce7d78ce00d04eda34b2cb695bfea7e681c7 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 12 Apr 2018 23:50:58 +0200 Subject: [PATCH 07/21] fix lint --- hassio/host/audio.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hassio/host/audio.py b/hassio/host/audio.py index 6e32b561a..18b6be9a7 100644 --- a/hassio/host/audio.py +++ b/hassio/host/audio.py @@ -73,6 +73,7 @@ class AlsaAudio(CoreSysAttributes): json_file = Path(__file__).parent.joinpath('audiodb.json') try: + # pylint: disable=no-member with json_file.open('r') as database: return json.loads(database.read()) except (ValueError, OSError) as err: @@ -117,6 +118,7 @@ class AlsaAudio(CoreSysAttributes): # Read Template asound_file = Path(__file__).parent.joinpath('asound.tmpl') try: + # pylint: disable=no-member with asound_file.open('r') as asound: asound_data = asound.read() except OSError as err: From 3382688669d0935c8478146bf1c4ab4f08c4ff32 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 13 Apr 2018 00:03:03 +0200 Subject: [PATCH 08/21] Fix name --- hassio/bootstrap.py | 2 +- hassio/host/__init__.py | 1 + hassio/host/{audio.py => alsa.py} | 0 3 files changed, 2 insertions(+), 1 deletion(-) rename hassio/host/{audio.py => alsa.py} (100%) diff --git a/hassio/bootstrap.py b/hassio/bootstrap.py index 1a465dea4..3daf77db5 100644 --- a/hassio/bootstrap.py +++ b/hassio/bootstrap.py @@ -17,7 +17,7 @@ from .snapshots import SnapshotManager from .tasks import Tasks from .updater import Updater from .services import ServiceManager -from .host.audio import AlsaAudio +from .host import AlsaAudio _LOGGER = logging.getLogger(__name__) diff --git a/hassio/host/__init__.py b/hassio/host/__init__.py index 01fd3820e..16b30ad18 100644 --- a/hassio/host/__init__.py +++ b/hassio/host/__init__.py @@ -1 +1,2 @@ """Host function like audio/dbus/systemd.""" +from .alsa import AlsaAudio # noqa diff --git a/hassio/host/audio.py b/hassio/host/alsa.py similarity index 100% rename from hassio/host/audio.py rename to hassio/host/alsa.py From 0cca8f522b833d91b7043b186ab2c0adf2821a2d Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 13 Apr 2018 21:19:57 +0200 Subject: [PATCH 09/21] rename audio object --- hassio/addons/addon.py | 6 +++--- hassio/api/hardware.py | 4 ++-- hassio/coresys.py | 16 ++++++++-------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/hassio/addons/addon.py b/hassio/addons/addon.py index cf0a7a3cc..e17f3f7a5 100644 --- a/hassio/addons/addon.py +++ b/hassio/addons/addon.py @@ -376,7 +376,7 @@ class Addon(CoreSysAttributes): if self.is_installed and \ ATTR_AUDIO_OUTPUT in self._data.user[self._id]: return self._data.user[self._id][ATTR_AUDIO_OUTPUT] - return self._audio.default.output + return self._alsa.default.output @audio_output.setter def audio_output(self, value): @@ -394,7 +394,7 @@ class Addon(CoreSysAttributes): if self.is_installed and ATTR_AUDIO_INPUT in self._data.user[self._id]: return self._data.user[self._id][ATTR_AUDIO_INPUT] - return self._audio.default.input + return self._alsa.default.input @audio_input.setter def audio_input(self, value): @@ -537,7 +537,7 @@ class Addon(CoreSysAttributes): def write_asound(self): """Write asound config to file and return True on success.""" - asound_config = self._audio.asound( + asound_config = self._alsa.asound( alsa_input=self.audio_input, alsa_output=self.audio_output) try: diff --git a/hassio/api/hardware.py b/hassio/api/hardware.py index add5515f6..e5e22a35f 100644 --- a/hassio/api/hardware.py +++ b/hassio/api/hardware.py @@ -28,7 +28,7 @@ class APIHardware(CoreSysAttributes): """Show ALSA audio devices.""" return { ATTR_AUDIO: { - ATTR_INPUT: self._audio.input_devices, - ATTR_OUTPUT: self._audio.output_devices, + ATTR_INPUT: self._alsa.input_devices, + ATTR_OUTPUT: self._alsa.output_devices, } } diff --git a/hassio/coresys.py b/hassio/coresys.py index 1568567e6..f17889d3a 100644 --- a/hassio/coresys.py +++ b/hassio/coresys.py @@ -42,7 +42,7 @@ class CoreSys(object): self._snapshots = None self._tasks = None self._services = None - self._audio = None + self._alsa = None @property def arch(self): @@ -205,16 +205,16 @@ class CoreSys(object): self._services = value @property - def audio(self): + def alsa(self): """Return ALSA Audio object.""" - return self._audio + return self._alsa - @audio.setter - def audio(self, value): + @alsa.setter + def alsa(self, value): """Set a ALSA Audio object.""" - if self._audio: - raise RuntimeError("Audio already set!") - self._audio = value + if self._alsa: + raise RuntimeError("ALSA already set!") + self._alsa = value class CoreSysAttributes(object): From 2f62b7046cd077197a8f4e05b9ea55cb66128ec2 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 13 Apr 2018 22:04:01 +0200 Subject: [PATCH 10/21] cleanup --- hassio/const.py | 2 -- hassio/host/alsa.py | 38 ++++++++++++++++---------------------- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/hassio/const.py b/hassio/const.py index ab1b3742a..926e5d940 100644 --- a/hassio/const.py +++ b/hassio/const.py @@ -162,8 +162,6 @@ ATTR_CRYPTO = 'crypto' ATTR_BRANCH = 'branch' ATTR_SECCOMP = 'seccomp' ATTR_APPARMOR = 'apparmor' -ATTR_CACHE = 'cache' -ATTR_DEFAULT = 'default' SERVICE_MQTT = 'mqtt' diff --git a/hassio/host/alsa.py b/hassio/host/alsa.py index 18b6be9a7..1e2bd0edd 100644 --- a/hassio/host/alsa.py +++ b/hassio/host/alsa.py @@ -5,8 +5,7 @@ import json from pathlib import Path from string import Template -from ..const import ( - ATTR_CACHE, ATTR_INPUT, ATTR_OUTPUT, ATTR_DEVICES, ATTR_NAME, ATTR_DEFAULT) +from ..const import ATTR_INPUT, ATTR_OUTPUT, ATTR_DEVICES, ATTR_NAME from ..coresys import CoreSysAttributes _LOGGER = logging.getLogger(__name__) @@ -21,21 +20,22 @@ class AlsaAudio(CoreSysAttributes): """Initialize Alsa audio system.""" self.coresys = coresys self._data = { - ATTR_CACHE: 0, ATTR_INPUT: {}, ATTR_OUTPUT: {}, } + self._cache = 0 + self._default = None @property def input_devices(self): """Return list of ALSA input devices.""" - self._update_device() + #self._update_device() return self._data[ATTR_INPUT] @property def output_devices(self): """Return list of ALSA output devices.""" - self._update_device() + #self._update_device() return self._data[ATTR_OUTPUT] def _update_device(self): @@ -43,7 +43,7 @@ class AlsaAudio(CoreSysAttributes): current_id = hash(frozenset(self._hardware.audio_devices)) # Need rebuild? - if current_id == self._data[ATTR_CACHE]: + if current_id == self._cache: return # Init database @@ -65,7 +65,7 @@ class AlsaAudio(CoreSysAttributes): self._data[key][alsa_id] = database.get(self._machine, {}).get( alsa_id, f"{dev_data[ATTR_NAME]}: {chan_id}") - self._data[ATTR_CACHE] = current_id + self._cache = current_id @staticmethod def _audio_database(): @@ -84,31 +84,25 @@ class AlsaAudio(CoreSysAttributes): @property def default(self): """Generate ALSA default setting.""" - if ATTR_DEFAULT in self._data: - default = self._data[ATTR_DEFAULT] - else: - default = None - # Init defaults - if default is None: + if self._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) - default = self._data[ATTR_DEFAULT] = \ - DefaultConfig(alsa_input, alsa_output) + self._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) + if self._default.output is None and self.output_devices: + self._default.output = next(iter(self.output_devices)) + _LOGGER.info("Detect output device %s", self._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) + if self._default.input is None and self.input_devices: + self._default.input = next(iter(self.input_devices)) + _LOGGER.info("Detect input device %s", self._default.input) - return default + return self._default def asound(self, alsa_input=None, alsa_output=None): """Generate a asound data.""" From d1c4f342fcf61e708688ba7afe803cc228a3b561 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 13 Apr 2018 22:16:55 +0200 Subject: [PATCH 11/21] Fix bootstrap --- hassio/bootstrap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hassio/bootstrap.py b/hassio/bootstrap.py index 3daf77db5..8ce5f2f41 100644 --- a/hassio/bootstrap.py +++ b/hassio/bootstrap.py @@ -29,7 +29,7 @@ def initialize_coresys(loop): # Initialize core objects coresys.updater = Updater(coresys) coresys.api = RestAPI(coresys) - coresys.audio = AlsaAudio(coresys) + coresys.alsa = AlsaAudio(coresys) coresys.supervisor = Supervisor(coresys) coresys.homeassistant = HomeAssistant(coresys) coresys.addons = AddonManager(coresys) From 19879e32878f4af491b53a74b9ca739f7cb0ae27 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 13 Apr 2018 22:42:27 +0200 Subject: [PATCH 12/21] test --- hassio/host/alsa.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hassio/host/alsa.py b/hassio/host/alsa.py index 1e2bd0edd..3a85cb61e 100644 --- a/hassio/host/alsa.py +++ b/hassio/host/alsa.py @@ -29,13 +29,13 @@ class AlsaAudio(CoreSysAttributes): @property def input_devices(self): """Return list of ALSA input devices.""" - #self._update_device() + self._update_device() return self._data[ATTR_INPUT] @property def output_devices(self): """Return list of ALSA output devices.""" - #self._update_device() + self._update_device() return self._data[ATTR_OUTPUT] def _update_device(self): @@ -67,8 +67,7 @@ class AlsaAudio(CoreSysAttributes): self._cache = current_id - @staticmethod - def _audio_database(): + def _audio_database(self): """Read local json audio data into dict.""" json_file = Path(__file__).parent.joinpath('audiodb.json') From ef7a3753967a2dec2d0cfb37d206408a153bb61c Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 13 Apr 2018 22:56:36 +0200 Subject: [PATCH 13/21] test 2 --- hassio/host/alsa.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hassio/host/alsa.py b/hassio/host/alsa.py index 3a85cb61e..99e855a61 100644 --- a/hassio/host/alsa.py +++ b/hassio/host/alsa.py @@ -40,11 +40,11 @@ class AlsaAudio(CoreSysAttributes): def _update_device(self): """Update Internal device DB.""" - current_id = hash(frozenset(self._hardware.audio_devices)) + #current_id = hash(frozenset(self._hardware.audio_devices)) # Need rebuild? - if current_id == self._cache: - return + #if current_id == self._cache: + # return # Init database _LOGGER.info("Update ALSA device list") @@ -67,7 +67,8 @@ class AlsaAudio(CoreSysAttributes): self._cache = current_id - def _audio_database(self): + @staticmethod + def _audio_database(): """Read local json audio data into dict.""" json_file = Path(__file__).parent.joinpath('audiodb.json') From 5becd51b50e0cc7fb2361f52583db8dd96c370b8 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 13 Apr 2018 23:11:20 +0200 Subject: [PATCH 14/21] test 4 --- hassio/host/alsa.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hassio/host/alsa.py b/hassio/host/alsa.py index 99e855a61..e00cf4418 100644 --- a/hassio/host/alsa.py +++ b/hassio/host/alsa.py @@ -40,15 +40,15 @@ class AlsaAudio(CoreSysAttributes): def _update_device(self): """Update Internal device DB.""" - #current_id = hash(frozenset(self._hardware.audio_devices)) + current_id = hash(frozenset(self._hardware.audio_devices)) # Need rebuild? - #if current_id == self._cache: - # return + if current_id == self._cache: + return # Init database _LOGGER.info("Update ALSA device list") - database = self._audio_database() + #database = self._audio_database() # Process devices for dev_id, dev_data in self._hardware.audio_devices.items(): @@ -62,8 +62,8 @@ class AlsaAudio(CoreSysAttributes): _LOGGER.warning("Unknown channel type: %s", chan_type) continue - self._data[key][alsa_id] = database.get(self._machine, {}).get( - alsa_id, f"{dev_data[ATTR_NAME]}: {chan_id}") + #self._data[key][alsa_id] = database.get(self._machine, {}).get( + # alsa_id, f"{dev_data[ATTR_NAME]}: {chan_id}") self._cache = current_id From a41912be0a51b8941bf385cdfb5acf394fe619e7 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 13 Apr 2018 23:21:42 +0200 Subject: [PATCH 15/21] fix db --- hassio/host/alsa.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hassio/host/alsa.py b/hassio/host/alsa.py index e00cf4418..8313400bd 100644 --- a/hassio/host/alsa.py +++ b/hassio/host/alsa.py @@ -48,7 +48,7 @@ class AlsaAudio(CoreSysAttributes): # Init database _LOGGER.info("Update ALSA device list") - #database = self._audio_database() + database = self._audio_database() # Process devices for dev_id, dev_data in self._hardware.audio_devices.items(): From 093ef17fb700b723e285a0c9a5abaf1e9f7e7452 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 13 Apr 2018 23:31:40 +0200 Subject: [PATCH 16/21] find the error --- hassio/host/alsa.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hassio/host/alsa.py b/hassio/host/alsa.py index 8313400bd..1a64c68f9 100644 --- a/hassio/host/alsa.py +++ b/hassio/host/alsa.py @@ -62,8 +62,11 @@ class AlsaAudio(CoreSysAttributes): _LOGGER.warning("Unknown channel type: %s", chan_type) continue - #self._data[key][alsa_id] = database.get(self._machine, {}).get( - # alsa_id, f"{dev_data[ATTR_NAME]}: {chan_id}") + try: + self._data[key][alsa_id] = database.get(self._machine, {}).get( + alsa_id, f"{dev_data[ATTR_NAME]}: {chan_id}") + except: + _LOGGER.exception("hmm") self._cache = current_id From 743a21821988bc2fa9d888534f3963587c494aa0 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 13 Apr 2018 23:45:03 +0200 Subject: [PATCH 17/21] fix bug --- hassio/homeassistant.py | 2 +- hassio/host/alsa.py | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/hassio/homeassistant.py b/hassio/homeassistant.py index 1a22a5087..e1973ad3c 100644 --- a/hassio/homeassistant.py +++ b/hassio/homeassistant.py @@ -48,7 +48,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes): @property def machine(self): """Return System Machines.""" - return self._docker.machine + return self.instance.machine @property def api_ip(self): diff --git a/hassio/host/alsa.py b/hassio/host/alsa.py index 1a64c68f9..8a8edd454 100644 --- a/hassio/host/alsa.py +++ b/hassio/host/alsa.py @@ -62,11 +62,8 @@ class AlsaAudio(CoreSysAttributes): _LOGGER.warning("Unknown channel type: %s", chan_type) continue - try: - self._data[key][alsa_id] = database.get(self._machine, {}).get( - alsa_id, f"{dev_data[ATTR_NAME]}: {chan_id}") - except: - _LOGGER.exception("hmm") + self._data[key][alsa_id] = database.get(self._machine, {}).get( + alsa_id, f"{dev_data[ATTR_NAME]}: {chan_id}") self._cache = current_id From c5ee2ebc49f61d5d4fec2d7755269532e020a8fd Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 13 Apr 2018 23:58:46 +0200 Subject: [PATCH 18/21] fix v2 --- hassio/host/alsa.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/hassio/host/alsa.py b/hassio/host/alsa.py index 8a8edd454..b79f46e83 100644 --- a/hassio/host/alsa.py +++ b/hassio/host/alsa.py @@ -85,22 +85,25 @@ class AlsaAudio(CoreSysAttributes): def default(self): """Generate ALSA default setting.""" # Init defaults - if self._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) + try: + if self._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._default = DefaultConfig(alsa_input, alsa_output) + self._default = DefaultConfig(alsa_input, alsa_output) - # Search exists/new output - if self._default.output is None and self.output_devices: - self._default.output = next(iter(self.output_devices)) - _LOGGER.info("Detect output device %s", self._default.output) + # Search exists/new output + if self._default.output is None and self.output_devices: + self._default.output = next(iter(self.output_devices)) + _LOGGER.info("Detect output device %s", self._default.output) - # Search exists/new input - if self._default.input is None and self.input_devices: - self._default.input = next(iter(self.input_devices)) - _LOGGER.info("Detect input device %s", self._default.input) + # Search exists/new input + if self._default.input is None and self.input_devices: + self._default.input = next(iter(self.input_devices)) + _LOGGER.info("Detect input device %s", self._default.input) + except: + _LOGGER.exception("hmm2") return self._default From 41fed656c12106d59112dc78f6ebbfea2ba92a56 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Sat, 14 Apr 2018 00:19:29 +0200 Subject: [PATCH 19/21] Use now attr --- hassio/docker/__init__.py | 5 +++-- hassio/homeassistant.py | 5 +++-- hassio/host/alsa.py | 35 +++++++++++++++++------------------ 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/hassio/docker/__init__.py b/hassio/docker/__init__.py index 58bff4657..a5733ddbc 100644 --- a/hassio/docker/__init__.py +++ b/hassio/docker/__init__.py @@ -1,8 +1,8 @@ """Init file for HassIO docker object.""" from contextlib import suppress -from collections import namedtuple import logging +import attr import docker from .network import DockerNetwork @@ -10,7 +10,8 @@ from ..const import SOCKET_DOCKER _LOGGER = logging.getLogger(__name__) -CommandReturn = namedtuple('CommandReturn', ['exit_code', 'output']) +# pylint: disable=invalid-name +CommandReturn = attr.make_class('CommandReturn', ['exit_code', 'output']) class DockerAPI(object): diff --git a/hassio/homeassistant.py b/hassio/homeassistant.py index e1973ad3c..899050b83 100644 --- a/hassio/homeassistant.py +++ b/hassio/homeassistant.py @@ -1,6 +1,5 @@ """HomeAssistant control object.""" import asyncio -from collections import namedtuple import logging import os import re @@ -9,6 +8,7 @@ import time import aiohttp from aiohttp.hdrs import CONTENT_TYPE +import attr from .const import ( FILE_HASSIO_HOMEASSISTANT, ATTR_IMAGE, ATTR_LAST_VERSION, ATTR_UUID, @@ -24,7 +24,8 @@ _LOGGER = logging.getLogger(__name__) RE_YAML_ERROR = re.compile(r"homeassistant\.util\.yaml") -ConfigResult = namedtuple('ConfigResult', ['valid', 'log']) +# pylint: disable=invalid-name +ConfigResult = attr.make_class('ConfigResult', ['valid', 'log']) class HomeAssistant(JsonConfig, CoreSysAttributes): diff --git a/hassio/host/alsa.py b/hassio/host/alsa.py index b79f46e83..6f8818525 100644 --- a/hassio/host/alsa.py +++ b/hassio/host/alsa.py @@ -1,16 +1,18 @@ """Host Audio-support.""" -from collections import namedtuple import logging import json from pathlib import Path from string import Template +import attr + from ..const import ATTR_INPUT, ATTR_OUTPUT, ATTR_DEVICES, ATTR_NAME from ..coresys import CoreSysAttributes _LOGGER = logging.getLogger(__name__) -DefaultConfig = namedtuple('DefaultConfig', ['input', 'output']) +# pylint: disable=invalid-name +DefaultConfig = attr.make_class('DefaultConfig', ['input', 'output']) class AlsaAudio(CoreSysAttributes): @@ -85,25 +87,22 @@ class AlsaAudio(CoreSysAttributes): def default(self): """Generate ALSA default setting.""" # Init defaults - try: - if self._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) + if self._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._default = DefaultConfig(alsa_input, alsa_output) + self._default = DefaultConfig(alsa_input, alsa_output) - # Search exists/new output - if self._default.output is None and self.output_devices: - self._default.output = next(iter(self.output_devices)) - _LOGGER.info("Detect output device %s", self._default.output) + # Search exists/new output + if self._default.output is None and self.output_devices: + self._default.output = next(iter(self.output_devices)) + _LOGGER.info("Detect output device %s", self._default.output) - # Search exists/new input - if self._default.input is None and self.input_devices: - self._default.input = next(iter(self.input_devices)) - _LOGGER.info("Detect input device %s", self._default.input) - except: - _LOGGER.exception("hmm2") + # Search exists/new input + if self._default.input is None and self.input_devices: + self._default.input = next(iter(self.input_devices)) + _LOGGER.info("Detect input device %s", self._default.input) return self._default From 31caed20fa503cd06b1cec265ba0d48909c08688 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Sat, 14 Apr 2018 00:27:31 +0200 Subject: [PATCH 20/21] Fix device name --- hassio/host/alsa.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hassio/host/alsa.py b/hassio/host/alsa.py index 6f8818525..0d5171f21 100644 --- a/hassio/host/alsa.py +++ b/hassio/host/alsa.py @@ -56,6 +56,9 @@ class AlsaAudio(CoreSysAttributes): for dev_id, dev_data in self._hardware.audio_devices.items(): for chan_id, chan_type in dev_data[ATTR_DEVICES].items(): alsa_id = f"{dev_id},{chan_id}" + dev_name = dev_data[ATTR_NAME] + + # Lookup type if chan_type.endswith('playback'): key = ATTR_OUTPUT elif chan_type.endswith('capture'): @@ -64,8 +67,10 @@ class AlsaAudio(CoreSysAttributes): _LOGGER.warning("Unknown channel type: %s", chan_type) continue - self._data[key][alsa_id] = database.get(self._machine, {}).get( - alsa_id, f"{dev_data[ATTR_NAME]}: {chan_id}") + # Use name from DB or a generic name + self._data[key][alsa_id] = database.get( + self._machine, {}).get( + dev_name, {}).get(alsa_id, f"{dev_name}: {chan_id}") self._cache = current_id From c4299b51cde8afa0d8ddf09d753c282a0aa8ffad Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Sat, 14 Apr 2018 00:30:28 +0200 Subject: [PATCH 21/21] Clear device on changes --- hassio/host/alsa.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hassio/host/alsa.py b/hassio/host/alsa.py index 0d5171f21..6baca457c 100644 --- a/hassio/host/alsa.py +++ b/hassio/host/alsa.py @@ -48,6 +48,10 @@ class AlsaAudio(CoreSysAttributes): if current_id == self._cache: return + # Clean old stuff + self._data[ATTR_INPUT].clear() + self._data[ATTR_OUTPUT].clear() + # Init database _LOGGER.info("Update ALSA device list") database = self._audio_database()