mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-10 10:46:29 +00:00
Extend Audio support
This commit is contained in:
parent
7d02bb2fe9
commit
a2789ac540
21
API.md
21
API.md
@ -255,7 +255,11 @@ Optional:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- GET `/host/hardware`
|
- POST `/host/reload`
|
||||||
|
|
||||||
|
### Hardware
|
||||||
|
|
||||||
|
- GET `/hardware/info`
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"serial": ["/dev/xy"],
|
"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
|
### Network
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
"""Init file for HassIO addons."""
|
"""Init file for HassIO addons."""
|
||||||
|
from contextlib import suppress
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
@ -372,15 +373,14 @@ class Addon(CoreSysAttributes):
|
|||||||
if not self.with_audio:
|
if not self.with_audio:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
setting = self._config.audio_output
|
|
||||||
if self.is_installed and \
|
if self.is_installed and \
|
||||||
ATTR_AUDIO_OUTPUT in self._data.user[self._id]:
|
ATTR_AUDIO_OUTPUT in self._data.user[self._id]:
|
||||||
setting = self._data.user[self._id][ATTR_AUDIO_OUTPUT]
|
return self._data.user[self._id][ATTR_AUDIO_OUTPUT]
|
||||||
return setting
|
return self._audio.default.output
|
||||||
|
|
||||||
@audio_output.setter
|
@audio_output.setter
|
||||||
def audio_output(self, value):
|
def audio_output(self, value):
|
||||||
"""Set/remove custom audio output settings."""
|
"""Set/reset audio output settings."""
|
||||||
if value is None:
|
if value is None:
|
||||||
self._data.user[self._id].pop(ATTR_AUDIO_OUTPUT, None)
|
self._data.user[self._id].pop(ATTR_AUDIO_OUTPUT, None)
|
||||||
else:
|
else:
|
||||||
@ -392,14 +392,13 @@ class Addon(CoreSysAttributes):
|
|||||||
if not self.with_audio:
|
if not self.with_audio:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
setting = self._config.audio_input
|
|
||||||
if self.is_installed and ATTR_AUDIO_INPUT in self._data.user[self._id]:
|
if self.is_installed and ATTR_AUDIO_INPUT in self._data.user[self._id]:
|
||||||
setting = self._data.user[self._id][ATTR_AUDIO_INPUT]
|
return self._data.user[self._id][ATTR_AUDIO_INPUT]
|
||||||
return setting
|
return self._audio.default.input
|
||||||
|
|
||||||
@audio_input.setter
|
@audio_input.setter
|
||||||
def audio_input(self, value):
|
def audio_input(self, value):
|
||||||
"""Set/remove custom audio input settings."""
|
"""Set/reset audio input settings."""
|
||||||
if value is None:
|
if value is None:
|
||||||
self._data.user[self._id].pop(ATTR_AUDIO_INPUT, None)
|
self._data.user[self._id].pop(ATTR_AUDIO_INPUT, None)
|
||||||
else:
|
else:
|
||||||
@ -504,6 +503,16 @@ class Addon(CoreSysAttributes):
|
|||||||
"""Return path to custom AppArmor profile."""
|
"""Return path to custom AppArmor profile."""
|
||||||
return Path(self.path_location, 'apparmor')
|
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):
|
def save_data(self):
|
||||||
"""Save data of addon."""
|
"""Save data of addon."""
|
||||||
self._addons.data.save_data()
|
self._addons.data.save_data()
|
||||||
@ -526,6 +535,20 @@ class Addon(CoreSysAttributes):
|
|||||||
|
|
||||||
return False
|
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
|
@property
|
||||||
def schema(self):
|
def schema(self):
|
||||||
"""Create a schema for addon options."""
|
"""Create a schema for addon options."""
|
||||||
@ -613,18 +636,24 @@ class Addon(CoreSysAttributes):
|
|||||||
@check_installed
|
@check_installed
|
||||||
async def start(self):
|
async def start(self):
|
||||||
"""Set options and start addon."""
|
"""Set options and start addon."""
|
||||||
|
# Options
|
||||||
if not self.write_options():
|
if not self.write_options():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# Sound
|
||||||
|
if self.with_audio and not self.write_asound():
|
||||||
|
return False
|
||||||
|
|
||||||
return await self.instance.run()
|
return await self.instance.run()
|
||||||
|
|
||||||
@check_installed
|
@check_installed
|
||||||
def stop(self):
|
async def stop(self):
|
||||||
"""Stop addon.
|
"""Stop addon."""
|
||||||
|
try:
|
||||||
Return a coroutine.
|
return self.instance.stop()
|
||||||
"""
|
finally:
|
||||||
return self.instance.stop()
|
with suppress(OSError):
|
||||||
|
self.path_asound.unlink()
|
||||||
|
|
||||||
@check_installed
|
@check_installed
|
||||||
async def update(self):
|
async def update(self):
|
||||||
|
@ -7,6 +7,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
from colorlog import ColoredFormatter
|
from colorlog import ColoredFormatter
|
||||||
|
|
||||||
|
from .audio import AlsaAudio
|
||||||
from .addons import AddonManager
|
from .addons import AddonManager
|
||||||
from .api import RestAPI
|
from .api import RestAPI
|
||||||
from .const import SOCKET_DOCKER
|
from .const import SOCKET_DOCKER
|
||||||
@ -28,6 +29,7 @@ def initialize_coresys(loop):
|
|||||||
# Initialize core objects
|
# Initialize core objects
|
||||||
coresys.updater = Updater(coresys)
|
coresys.updater = Updater(coresys)
|
||||||
coresys.api = RestAPI(coresys)
|
coresys.api = RestAPI(coresys)
|
||||||
|
coresys.audio = AlsaAudio(coresys)
|
||||||
coresys.supervisor = Supervisor(coresys)
|
coresys.supervisor = Supervisor(coresys)
|
||||||
coresys.homeassistant = HomeAssistant(coresys)
|
coresys.homeassistant = HomeAssistant(coresys)
|
||||||
coresys.addons = AddonManager(coresys)
|
coresys.addons = AddonManager(coresys)
|
||||||
|
@ -6,7 +6,7 @@ from pathlib import Path, PurePath
|
|||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
FILE_HASSIO_CONFIG, HASSIO_DATA, ATTR_TIMEZONE, ATTR_ADDONS_CUSTOM_LIST,
|
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.dt import parse_datetime
|
||||||
from .utils.json import JsonConfig
|
from .utils.json import JsonConfig
|
||||||
from .validate import SCHEMA_HASSIO_CONFIG
|
from .validate import SCHEMA_HASSIO_CONFIG
|
||||||
@ -136,6 +136,11 @@ class CoreConfig(JsonConfig):
|
|||||||
"""Return hass.io temp folder."""
|
"""Return hass.io temp folder."""
|
||||||
return Path(HASSIO_DATA, TMP_DATA)
|
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
|
@property
|
||||||
def path_backup(self):
|
def path_backup(self):
|
||||||
"""Return root backup data folder."""
|
"""Return root backup data folder."""
|
||||||
@ -174,23 +179,3 @@ class CoreConfig(JsonConfig):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self._data[ATTR_ADDONS_CUSTOM_LIST].remove(repo)
|
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
|
|
||||||
|
@ -27,6 +27,7 @@ DOCKER_NETWORK_RANGE = ip_network('172.30.33.0/24')
|
|||||||
LABEL_VERSION = 'io.hass.version'
|
LABEL_VERSION = 'io.hass.version'
|
||||||
LABEL_ARCH = 'io.hass.arch'
|
LABEL_ARCH = 'io.hass.arch'
|
||||||
LABEL_TYPE = 'io.hass.type'
|
LABEL_TYPE = 'io.hass.type'
|
||||||
|
LABEL_MACHINE = 'io.hass.machine'
|
||||||
|
|
||||||
META_ADDON = 'addon'
|
META_ADDON = 'addon'
|
||||||
META_SUPERVISOR = 'supervisor'
|
META_SUPERVISOR = 'supervisor'
|
||||||
@ -161,6 +162,8 @@ ATTR_CRYPTO = 'crypto'
|
|||||||
ATTR_BRANCH = 'branch'
|
ATTR_BRANCH = 'branch'
|
||||||
ATTR_SECCOMP = 'seccomp'
|
ATTR_SECCOMP = 'seccomp'
|
||||||
ATTR_APPARMOR = 'apparmor'
|
ATTR_APPARMOR = 'apparmor'
|
||||||
|
ATTR_CACHE = 'cache'
|
||||||
|
ATTR_DEFAULT = 'default'
|
||||||
|
|
||||||
SERVICE_MQTT = 'mqtt'
|
SERVICE_MQTT = 'mqtt'
|
||||||
|
|
||||||
|
@ -42,6 +42,7 @@ class CoreSys(object):
|
|||||||
self._snapshots = None
|
self._snapshots = None
|
||||||
self._tasks = None
|
self._tasks = None
|
||||||
self._services = None
|
self._services = None
|
||||||
|
self._audio = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def arch(self):
|
def arch(self):
|
||||||
@ -50,6 +51,13 @@ class CoreSys(object):
|
|||||||
return self._supervisor.arch
|
return self._supervisor.arch
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def machine(self):
|
||||||
|
"""Return running machine type of hass.io system."""
|
||||||
|
if self._homeassistant:
|
||||||
|
return self._homeassistant.machine
|
||||||
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dev(self):
|
def dev(self):
|
||||||
"""Return True if we run dev modus."""
|
"""Return True if we run dev modus."""
|
||||||
@ -196,6 +204,18 @@ class CoreSys(object):
|
|||||||
raise RuntimeError("Services already set!")
|
raise RuntimeError("Services already set!")
|
||||||
self._services = value
|
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):
|
class CoreSysAttributes(object):
|
||||||
"""Inheret basic CoreSysAttributes."""
|
"""Inheret basic CoreSysAttributes."""
|
||||||
|
@ -201,7 +201,7 @@ class DockerAddon(DockerInterface):
|
|||||||
'bind': "/share", 'mode': addon_mapping[MAP_SHARE]
|
'bind': "/share", 'mode': addon_mapping[MAP_SHARE]
|
||||||
}})
|
}})
|
||||||
|
|
||||||
# init other hardware mappings
|
# Init other hardware mappings
|
||||||
if self.addon.with_gpio:
|
if self.addon.with_gpio:
|
||||||
volumes.update({
|
volumes.update({
|
||||||
"/sys/class/gpio": {
|
"/sys/class/gpio": {
|
||||||
@ -212,13 +212,20 @@ class DockerAddon(DockerInterface):
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
# host dbus system
|
# Host dbus system
|
||||||
if self.addon.host_dbus:
|
if self.addon.host_dbus:
|
||||||
volumes.update({
|
volumes.update({
|
||||||
"/var/run/dbus": {
|
"/var/run/dbus": {
|
||||||
'bind': "/var/run/dbus", 'mode': 'rw'
|
'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
|
return volumes
|
||||||
|
|
||||||
def _run(self):
|
def _run(self):
|
||||||
|
@ -4,7 +4,7 @@ import logging
|
|||||||
import docker
|
import docker
|
||||||
|
|
||||||
from .interface import DockerInterface
|
from .interface import DockerInterface
|
||||||
from ..const import ENV_TOKEN, ENV_TIME
|
from ..const import ENV_TOKEN, ENV_TIME, LABEL_MACHINE
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -14,6 +14,13 @@ HASS_DOCKER_NAME = 'homeassistant'
|
|||||||
class DockerHomeAssistant(DockerInterface):
|
class DockerHomeAssistant(DockerInterface):
|
||||||
"""Docker hassio wrapper for HomeAssistant."""
|
"""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
|
@property
|
||||||
def image(self):
|
def image(self):
|
||||||
"""Return name of docker image."""
|
"""Return name of docker image."""
|
||||||
|
@ -45,6 +45,11 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
_LOGGER.info("No HomeAssistant docker %s found.", self.image)
|
_LOGGER.info("No HomeAssistant docker %s found.", self.image)
|
||||||
await self.install_landingpage()
|
await self.install_landingpage()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def machine(self):
|
||||||
|
"""Return System Machines."""
|
||||||
|
return self._docker.machine
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def api_ip(self):
|
def api_ip(self):
|
||||||
"""Return IP of HomeAssistant instance."""
|
"""Return IP of HomeAssistant instance."""
|
||||||
|
1
hassio/host/__init__.py
Normal file
1
hassio/host/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
"""Host function like audio/dbus/systemd."""
|
17
hassio/host/asound.tmpl
Normal file
17
hassio/host/asound.tmpl
Normal file
@ -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}"
|
||||||
|
}
|
||||||
|
}
|
114
hassio/host/audio.py
Normal file
114
hassio/host/audio.py
Normal file
@ -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
|
||||||
|
)
|
18
hassio/host/audiodb.json
Normal file
18
hassio/host/audiodb.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user