Home Assistant Unofficial Reference 2024.12.1
switch.py
Go to the documentation of this file.
1 """Switch logic for loading/unloading pulseaudio loopback modules."""
2 
3 from __future__ import annotations
4 
5 import logging
6 from typing import Any
7 
8 from pulsectl import Pulse, PulseError
9 import voluptuous as vol
10 
12  PLATFORM_SCHEMA as SWITCH_PLATFORM_SCHEMA,
13  SwitchEntity,
14 )
15 from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PORT
16 from homeassistant.core import HomeAssistant
18 from homeassistant.helpers.entity_platform import AddEntitiesCallback
19 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
20 
21 DOMAIN = "pulseaudio_loopback"
22 
23 _LOGGER = logging.getLogger(__name__)
24 
25 CONF_SINK_NAME = "sink_name"
26 CONF_SOURCE_NAME = "source_name"
27 
28 DEFAULT_NAME = "paloopback"
29 DEFAULT_PORT = 4713
30 
31 IGNORED_SWITCH_WARN = "Switch is already in the desired state. Ignoring."
32 
33 PLATFORM_SCHEMA = SWITCH_PLATFORM_SCHEMA.extend(
34  {
35  vol.Required(CONF_SINK_NAME): cv.string,
36  vol.Required(CONF_SOURCE_NAME): cv.string,
37  vol.Optional(CONF_HOST): cv.string,
38  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
39  vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
40  }
41 )
42 
43 
45  hass: HomeAssistant,
46  config: ConfigType,
47  add_entities: AddEntitiesCallback,
48  discovery_info: DiscoveryInfoType | None = None,
49 ) -> None:
50  """Read in all of our configuration, and initialize the loopback switch."""
51  name = config.get(CONF_NAME)
52  sink_name = config.get(CONF_SINK_NAME)
53  source_name = config.get(CONF_SOURCE_NAME)
54  host = config.get(CONF_HOST)
55  port = config.get(CONF_PORT)
56 
57  hass.data.setdefault(DOMAIN, {})
58 
59  server_id = str.format("{0}:{1}", host, port)
60 
61  if host:
62  connect_to_server = server_id
63  else:
64  connect_to_server = None
65 
66  if server_id in hass.data[DOMAIN]:
67  server = hass.data[DOMAIN][server_id]
68  else:
69  server = Pulse(server=connect_to_server, connect=False, threading_lock=True)
70  hass.data[DOMAIN][server_id] = server
71 
72  add_entities([PALoopbackSwitch(name, server, sink_name, source_name)], True)
73 
74 
76  """Representation the presence or absence of a PA loopback module."""
77 
78  def __init__(self, name, pa_server, sink_name, source_name):
79  """Initialize the Pulseaudio switch."""
80  self._module_idx_module_idx = None
81  self._name_name = name
82  self._sink_name_sink_name = sink_name
83  self._source_name_source_name = source_name
84  self._pa_svr_pa_svr = pa_server
85 
86  def _get_module_idx(self):
87  try:
88  self._pa_svr_pa_svr.connect()
89 
90  for module in self._pa_svr_pa_svr.module_list():
91  if module.name != "module-loopback":
92  continue
93 
94  if f"sink={self._sink_name}" not in module.argument:
95  continue
96 
97  if f"source={self._source_name}" not in module.argument:
98  continue
99 
100  return module.index
101 
102  except PulseError:
103  return None
104 
105  return None
106 
107  @property
108  def available(self) -> bool:
109  """Return true when connected to server."""
110  return self._pa_svr_pa_svr.connected
111 
112  @property
113  def name(self):
114  """Return the name of the switch."""
115  return self._name_name
116 
117  @property
118  def is_on(self):
119  """Return true if device is on."""
120  return self._module_idx_module_idx is not None
121 
122  def turn_on(self, **kwargs: Any) -> None:
123  """Turn the device on."""
124  if not self.is_onis_onis_on:
125  self._pa_svr_pa_svr.module_load(
126  "module-loopback",
127  args=f"sink={self._sink_name} source={self._source_name}",
128  )
129  else:
130  _LOGGER.warning(IGNORED_SWITCH_WARN)
131 
132  def turn_off(self, **kwargs: Any) -> None:
133  """Turn the device off."""
134  if self.is_onis_onis_on:
135  self._pa_svr_pa_svr.module_unload(self._module_idx_module_idx)
136  else:
137  _LOGGER.warning(IGNORED_SWITCH_WARN)
138 
139  def update(self) -> None:
140  """Refresh state in case an alternate process modified this data."""
141  self._module_idx_module_idx = self._get_module_idx_get_module_idx()
def __init__(self, name, pa_server, sink_name, source_name)
Definition: switch.py:78
None add_entities(AsusWrtRouter router, AddEntitiesCallback async_add_entities, set[str] tracked)
None setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback add_entities, DiscoveryInfoType|None discovery_info=None)
Definition: switch.py:49