core/homeassistant/components/pulseaudio_loopback/switch.py

127 lines
3.6 KiB
Python

"""Switch logic for loading/unloading pulseaudio loopback modules."""
import logging
from pulsectl import Pulse, PulseError
import voluptuous as vol
from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchEntity
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PORT
import homeassistant.helpers.config_validation as cv
DOMAIN = "pulseaudio_loopback"
_LOGGER = logging.getLogger(__name__)
CONF_SINK_NAME = "sink_name"
CONF_SOURCE_NAME = "source_name"
DEFAULT_NAME = "paloopback"
DEFAULT_PORT = 4713
IGNORED_SWITCH_WARN = "Switch is already in the desired state. Ignoring."
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_SINK_NAME): cv.string,
vol.Required(CONF_SOURCE_NAME): cv.string,
vol.Optional(CONF_HOST): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
}
)
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Read in all of our configuration, and initialize the loopback switch."""
name = config.get(CONF_NAME)
sink_name = config.get(CONF_SINK_NAME)
source_name = config.get(CONF_SOURCE_NAME)
host = config.get(CONF_HOST)
port = config.get(CONF_PORT)
hass.data.setdefault(DOMAIN, {})
server_id = str.format("{0}:{1}", host, port)
if host:
connect_to_server = server_id
else:
connect_to_server = None
if server_id in hass.data[DOMAIN]:
server = hass.data[DOMAIN][server_id]
else:
server = Pulse(server=connect_to_server, connect=False, threading_lock=True)
hass.data[DOMAIN][server_id] = server
add_entities([PALoopbackSwitch(name, server, sink_name, source_name)], True)
class PALoopbackSwitch(SwitchEntity):
"""Representation the presence or absence of a PA loopback module."""
def __init__(self, name, pa_server, sink_name, source_name):
"""Initialize the Pulseaudio switch."""
self._module_idx = None
self._name = name
self._sink_name = sink_name
self._source_name = source_name
self._pa_svr = pa_server
def _get_module_idx(self):
try:
self._pa_svr.connect()
for module in self._pa_svr.module_list():
if module.name != "module-loopback":
continue
if f"sink={self._sink_name}" not in module.argument:
continue
if f"source={self._source_name}" not in module.argument:
continue
return module.index
except PulseError:
return None
return None
@property
def available(self):
"""Return true when connected to server."""
return self._pa_svr.connected
@property
def name(self):
"""Return the name of the switch."""
return self._name
@property
def is_on(self):
"""Return true if device is on."""
return self._module_idx is not None
def turn_on(self, **kwargs):
"""Turn the device on."""
if not self.is_on:
self._pa_svr.module_load(
"module-loopback",
args=f"sink={self._sink_name} source={self._source_name}",
)
else:
_LOGGER.warning(IGNORED_SWITCH_WARN)
def turn_off(self, **kwargs):
"""Turn the device off."""
if self.is_on:
self._pa_svr.module_unload(self._module_idx)
else:
_LOGGER.warning(IGNORED_SWITCH_WARN)
def update(self):
"""Refresh state in case an alternate process modified this data."""
self._module_idx = self._get_module_idx()