## Thing Configuration
-The Pulseaudio bridge requires the host (ip address or a hostname) and a port (default: 4712) as a configuration value in order for the binding to know where to access it.
-You can use `pactl -s <ip-address|hostname> list sinks | grep "name:"` to find the name of a sink.
+The Pulseaudio bridge requires the host (ip address or a hostname) and a port (default: 4712) as a configuration value in order for the binding to know where to access it.
+A Pulseaudio device requires at least an identifier. For sinks and sources, you can use the name or the description. For sink inputs and source outputs, you can use the name or the application name.
+To know without hesitation the correct value to use, you should use the command line utility `pactl`. For example, to find the name of a sink:
+`pactl -s <ip-address|hostname> list sinks | grep "name:"`
+If you need to narrow the identification of a device (in case name or description are not consistent and sufficient), you can use the `additionalFilters` parameter (optional/advanced parameter), in the form of one or several (separator '###') regular expression(s), each one matching a property value of the pulseaudio device. You can use every properties listed with `pactl`.
+
## Channels
```
Bridge pulseaudio:bridge:<bridgname> "<Bridge Label>" @ "<Room>" [ host="<ipAddress>", port=4712 ] {
Things:
- Thing sink multiroom "Snapcast" @ "Room" [name="alsa_card.pci-0000_00_1f.3", activateSimpleProtocolSink=true, simpleProtocolSinkPort=4711] // the name corresponds to `pactl list sinks` output
+ Thing sink multiroom "Snapcast" @ "Room" [name="alsa_card.pci-0000_00_1f.3", activateSimpleProtocolSink=true, simpleProtocolSinkPort=4711, additionalFilters="analog-stereo###internal"]
Thing source microphone "microphone" @ "Room" [name="alsa_input.pci-0000_00_14.2.analog-stereo"]
Thing sink-input openhabTTS "OH-Voice" @ "Room" [name="alsa_output.pci-0000_00_1f.3.hdmi-stereo-extra1"]
Thing source-output remotePulseSink "Other Room Speaker" @ "Other Room" [name="alsa_input.pci-0000_00_14.2.analog-stereo"]
public static final String BRIDGE_PARAMETER_PORT = "port";
public static final String BRIDGE_PARAMETER_REFRESH_INTERVAL = "refresh";
- public static final String DEVICE_PARAMETER_NAME = "name";
+ public static final String DEVICE_PARAMETER_NAME_OR_DESCRIPTION = "name";
+ public static final String DEVICE_PARAMETER_ADDITIONAL_FILTERS = "additionalFilters";
public static final String DEVICE_PARAMETER_AUDIO_SINK_ACTIVATION = "activateSimpleProtocolSink";
public static final String DEVICE_PARAMETER_AUDIO_SINK_PORT = "simpleProtocolSinkPort";
public static final String DEVICE_PARAMETER_AUDIO_SINK_IDLE_TIMEOUT = "simpleProtocolSinkIdleTimeout";
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.Random;
+import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.pulseaudio.internal.cli.Parser;
+import org.openhab.binding.pulseaudio.internal.handler.DeviceIdentifier;
import org.openhab.binding.pulseaudio.internal.items.AbstractAudioDeviceConfig;
import org.openhab.binding.pulseaudio.internal.items.AbstractAudioDeviceConfig.State;
import org.openhab.binding.pulseaudio.internal.items.Module;
}
/**
- * retrieves a {@link AbstractAudioDeviceConfig} by its name
+ * retrieves a {@link AbstractAudioDeviceConfig} by its identifier
+ * If several devices correspond to the deviceIdentifier, returns the first one (aphabetical order)
*
+ * @param The device identifier to match against
* @return the corresponding {@link AbstractAudioDeviceConfig} to the given <code>name</code>
*/
- public @Nullable AbstractAudioDeviceConfig getGenericAudioItem(String name) {
- for (AbstractAudioDeviceConfig item : items) {
- if (item.getPaName().equalsIgnoreCase(name)) {
- return item;
- }
+ public @Nullable AbstractAudioDeviceConfig getGenericAudioItem(DeviceIdentifier deviceIdentifier) {
+ List<AbstractAudioDeviceConfig> matchingDevices = items.stream()
+ .filter(device -> device.matches(deviceIdentifier))
+ .sorted(Comparator.comparing(AbstractAudioDeviceConfig::getPaName)).collect(Collectors.toList());
+ if (matchingDevices.size() == 1) {
+ return matchingDevices.get(0);
+ } else if (matchingDevices.size() > 1) {
+ logger.debug(
+ "Cannot select exactly one audio device, so choosing the first. To choose without ambiguity between the {} devices matching the identifier {}, you can maybe use a more restrictive 'additionalFilter' parameter",
+ matchingDevices.size(), deviceIdentifier.getNameOrDescription());
+ return matchingDevices.get(0);
}
return null;
}
private ThingUID getPulseaudioDeviceUID(ThingTypeUID thingTypeUID, @Nullable ThingUID thingUID,
Configuration configuration, @Nullable ThingUID bridgeUID) {
if (thingUID == null) {
- String name = (String) configuration.get(PulseaudioBindingConstants.DEVICE_PARAMETER_NAME);
+ String name = (String) configuration.get(PulseaudioBindingConstants.DEVICE_PARAMETER_NAME_OR_DESCRIPTION);
return new ThingUID(thingTypeUID, name, bridgeUID == null ? null : bridgeUID.getId());
}
return thingUID;
if (serviceRegistration != null) {
PulseaudioDeviceDiscoveryService service = (PulseaudioDeviceDiscoveryService) bundleContext
.getService(serviceRegistration.getReference());
- service.deactivate();
+ if (service != null) {
+ service.deactivate();
+ }
serviceRegistration.unregister();
}
discoveryServiceReg.remove(thingHandler);
}
}
if (properties.containsKey("name")) {
- Sink sink = new Sink(id, properties.get("name"),
+ Sink sink = new Sink(id, properties.get("name"), properties.get("device.description"), properties,
client.getModule(getNumberValue(properties.get("module"))));
if (properties.containsKey("state")) {
try {
if (properties.containsKey("sink")) {
String name = properties.containsKey("media.name") ? properties.get("media.name")
: properties.get("sink");
- SinkInput item = new SinkInput(id, name, client.getModule(getNumberValue(properties.get("module"))));
+ SinkInput item = new SinkInput(id, name, properties.get("application.name"), properties,
+ client.getModule(getNumberValue(properties.get("module"))));
if (properties.containsKey("state")) {
try {
item.setState(AbstractAudioDeviceConfig.State.valueOf(properties.get("state")));
}
}
if (properties.containsKey("name")) {
- Source source = new Source(id, properties.get("name"),
+ Source source = new Source(id, properties.get("name"), properties.get("device.description"), properties,
client.getModule(getNumberValue(properties.get("module"))));
if (properties.containsKey("state")) {
try {
}
}
if (properties.containsKey("source")) {
- SourceOutput item = new SourceOutput(id, properties.get("source"),
- client.getModule(getNumberValue(properties.get("module"))));
+ SourceOutput item = new SourceOutput(id, properties.get("source"), properties.get("application.name"),
+ properties, client.getModule(getNumberValue(properties.get("module"))));
if (properties.containsKey("state")) {
try {
item.setState(AbstractAudioDeviceConfig.State.valueOf(properties.get("state")));
package org.openhab.binding.pulseaudio.internal.discovery;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
-import java.util.stream.Collectors;
+import java.util.regex.PatternSyntaxException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.pulseaudio.internal.PulseaudioBindingConstants;
+import org.openhab.binding.pulseaudio.internal.handler.DeviceIdentifier;
import org.openhab.binding.pulseaudio.internal.handler.DeviceStatusListener;
import org.openhab.binding.pulseaudio.internal.handler.PulseaudioBridgeHandler;
import org.openhab.binding.pulseaudio.internal.handler.PulseaudioHandler;
import org.openhab.binding.pulseaudio.internal.items.SinkInput;
import org.openhab.binding.pulseaudio.internal.items.Source;
import org.openhab.binding.pulseaudio.internal.items.SourceOutput;
+import org.openhab.core.config.core.Configuration;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
@Override
public void onDeviceAdded(Thing bridge, AbstractAudioDeviceConfig device) {
- if (getAlreadyConfiguredThings().contains(device.getPaName())) {
+ if (getAlreadyConfiguredThings().stream().anyMatch(deviceIdentifier -> device.matches(deviceIdentifier))) {
return;
}
ThingTypeUID thingType = null;
Map<String, Object> properties = new HashMap<>();
// All devices need this parameter
- properties.put(PulseaudioBindingConstants.DEVICE_PARAMETER_NAME, uidName);
+ properties.put(PulseaudioBindingConstants.DEVICE_PARAMETER_NAME_OR_DESCRIPTION, uidName);
if (device instanceof Sink) {
if (((Sink) device).isCombinedSink()) {
thingType = PulseaudioBindingConstants.COMBINED_SINK_THING_TYPE;
}
}
- public Set<String> getAlreadyConfiguredThings() {
- return pulseaudioBridgeHandler.getThing().getThings().stream().map(Thing::getConfiguration)
- .map(conf -> (String) conf.get(PulseaudioBindingConstants.DEVICE_PARAMETER_NAME))
- .collect(Collectors.toSet());
+ public Set<DeviceIdentifier> getAlreadyConfiguredThings() {
+ Set<DeviceIdentifier> alreadyConfiguredThings = new HashSet<>();
+ for (Thing thing : pulseaudioBridgeHandler.getThing().getThings()) {
+ Configuration configuration = thing.getConfiguration();
+ try {
+ alreadyConfiguredThings.add(new DeviceIdentifier(
+ (String) configuration.get(PulseaudioBindingConstants.DEVICE_PARAMETER_NAME_OR_DESCRIPTION),
+ (String) configuration.get(PulseaudioBindingConstants.DEVICE_PARAMETER_ADDITIONAL_FILTERS)));
+ } catch (PatternSyntaxException p) {
+ logger.debug(
+ "There is an error with an already configured things. Cannot compare with discovery, skipping it");
+ }
+ }
+ return alreadyConfiguredThings;
}
@Override
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.pulseaudio.internal.handler;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+import java.util.stream.Collectors;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * All informations needed to precisely identify a device
+ *
+ * @author Gwendal Roulleau - Initial contribution
+ *
+ */
+@NonNullByDefault
+public class DeviceIdentifier {
+
+ private String nameOrDescription;
+ private List<Pattern> additionalFilters = new ArrayList<>();
+
+ public DeviceIdentifier(String nameOrDescription, @Nullable String additionalFilters)
+ throws PatternSyntaxException {
+ super();
+ this.nameOrDescription = nameOrDescription;
+ if (additionalFilters != null && !additionalFilters.isEmpty()) {
+ Arrays.asList(additionalFilters.split("###")).stream()
+ .forEach(ad -> this.additionalFilters.add(Pattern.compile(ad)));
+ }
+ }
+
+ public String getNameOrDescription() {
+ return nameOrDescription;
+ }
+
+ public List<Pattern> getAdditionalFilters() {
+ return additionalFilters;
+ }
+
+ @Override
+ public String toString() {
+ List<Pattern> additionalFiltersFinal = additionalFilters;
+ String additionalPatternToString = additionalFiltersFinal.stream().map(Pattern::pattern)
+ .collect(Collectors.joining("###"));
+ return "DeviceIdentifier [nameOrDescription=" + nameOrDescription + ", additionalFilter="
+ + additionalPatternToString + "]";
+ }
+}
} else {
// browse all child handlers to update status according to the result of the query to the pulse audio server
for (PulseaudioHandler pulseaudioHandler : childHandlersInitialized) {
- pulseaudioHandler.deviceUpdate(getDevice(pulseaudioHandler.getName()));
+ pulseaudioHandler.deviceUpdate(getDevice(pulseaudioHandler.getDeviceIdentifier()));
}
}
// browse query result to notify add event
}
}
- public @Nullable AbstractAudioDeviceConfig getDevice(String name) {
- return getClient().getGenericAudioItem(name);
+ public @Nullable AbstractAudioDeviceConfig getDevice(@Nullable DeviceIdentifier deviceIdentifier) {
+ return deviceIdentifier == null ? null : getClient().getGenericAudioItem(deviceIdentifier);
}
public PulseaudioClient getClient() {
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;
import java.util.stream.Stream;
SOURCE_THING_TYPE, SOURCE_OUTPUT_THING_TYPE).collect(Collectors.toSet()));
private final Logger logger = LoggerFactory.getLogger(PulseaudioHandler.class);
- private String name = "";
+ private @Nullable DeviceIdentifier deviceIdentifier;
private @Nullable PulseAudioAudioSink audioSink;
private @Nullable PulseAudioAudioSource audioSource;
private @Nullable Integer savedVolume;
@Override
public void initialize() {
Configuration config = getThing().getConfiguration();
- name = (String) config.get(DEVICE_PARAMETER_NAME);
+ try {
+ deviceIdentifier = new DeviceIdentifier((String) config.get(DEVICE_PARAMETER_NAME_OR_DESCRIPTION),
+ (String) config.get(DEVICE_PARAMETER_ADDITIONAL_FILTERS));
+ } catch (PatternSyntaxException p) {
+ deviceIdentifier = null;
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+ "Incorrect regular expression: " + (String) config.get(DEVICE_PARAMETER_ADDITIONAL_FILTERS));
+ return;
+ }
initializeWithTheBridge();
}
- public String getName() {
- return name;
+ public @Nullable DeviceIdentifier getDeviceIdentifier() {
+ return deviceIdentifier;
}
private void audioSinkSetup() {
@Override
public void dispose() {
- logger.trace("Thing {} {} disposed.", getThing().getUID(), name);
+ logger.trace("Thing {} {} disposed.", getThing().getUID(), safeGetDeviceNameOrDescription());
super.dispose();
audioSinkUnsetup();
audioSourceUnsetup();
} else if (pulseaudioBridgeHandler.getThing().getStatus() != ThingStatus.ONLINE) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
} else {
- deviceUpdate(pulseaudioBridgeHandler.getDevice(name));
+ deviceUpdate(pulseaudioBridgeHandler.getDevice(deviceIdentifier));
}
}
private synchronized @Nullable PulseaudioBridgeHandler getPulseaudioBridgeHandler() {
Bridge bridge = getBridge();
if (bridge == null) {
- logger.debug("Required bridge not defined for device {}.", name);
+ logger.debug("Required bridge not defined for device {}.", safeGetDeviceNameOrDescription());
return null;
}
ThingHandler handler = bridge.getHandler();
if (handler instanceof PulseaudioBridgeHandler) {
return (PulseaudioBridgeHandler) handler;
} else {
- logger.debug("No available bridge handler found for device {} bridge {} .", name, bridge.getUID());
+ logger.debug("No available bridge handler found for device {} bridge {} .",
+ safeGetDeviceNameOrDescription(), bridge.getUID());
return null;
}
}
return;
}
- AbstractAudioDeviceConfig device = briHandler.getDevice(name);
+ AbstractAudioDeviceConfig device = briHandler.getDevice(deviceIdentifier);
if (device == null) {
- logger.warn("device {} not found", name);
+ logger.warn("device {} not found", safeGetDeviceNameOrDescription());
deviceUpdate(null);
return;
} else {
if (command instanceof IncreaseDecreaseType) {
// refresh to get the current volume level
briHandler.getClient().update();
- device = briHandler.getDevice(name);
+ device = briHandler.getDevice(deviceIdentifier);
if (device == null) {
logger.warn("missing device info, aborting");
return;
if (briHandler != null) {
// refresh to get the current volume level
briHandler.getClient().update();
- AbstractAudioDeviceConfig device = briHandler.getDevice(name);
+ AbstractAudioDeviceConfig device = briHandler.getDevice(deviceIdentifier);
if (device != null) {
savedVolume = savedVolumeFinal = device.getVolume();
}
logger.warn("bridge is not ready");
return;
}
- AbstractAudioDeviceConfig device = briHandler.getDevice(name);
+ AbstractAudioDeviceConfig device = briHandler.getDevice(deviceIdentifier);
if (device == null) {
logger.warn("missing device info, aborting");
return;
}
public void deviceUpdate(@Nullable AbstractAudioDeviceConfig device) {
- if (device != null && device.getPaName().equals(name)) {
+ if (device != null) {
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
logger.debug("Updating states of {} id: {}", device, VOLUME_CHANNEL);
int actualVolume = device.getVolume();
}
audioSinkSetup();
audioSourceSetup();
- } else if (device == null) {
+ } else {
updateState(VOLUME_CHANNEL, UnDefType.UNDEF);
updateState(MUTE_CHANNEL, UnDefType.UNDEF);
updateState(STATE_CHANNEL, UnDefType.UNDEF);
if (briHandler == null) {
throw new IOException("bridge is not ready");
}
- AbstractAudioDeviceConfig device = briHandler.getDevice(name);
+ AbstractAudioDeviceConfig device = briHandler.getDevice(deviceIdentifier);
if (device == null) {
- throw new IOException("missing device info, device appears to be offline");
+ throw new IOException(
+ "missing device info, device " + safeGetDeviceNameOrDescription() + " appears to be offline");
}
String simpleTcpPortPrefName = (device instanceof Source) ? DEVICE_PARAMETER_AUDIO_SOURCE_PORT
: DEVICE_PARAMETER_AUDIO_SINK_PORT;
var idleTimeout = 3000;
var handler = getPulseaudioBridgeHandler();
if (handler != null) {
- AbstractAudioDeviceConfig device = handler.getDevice(name);
+ AbstractAudioDeviceConfig device = handler.getDevice(deviceIdentifier);
String idleTimeoutPropName = (device instanceof Source) ? DEVICE_PARAMETER_AUDIO_SOURCE_IDLE_TIMEOUT
: DEVICE_PARAMETER_AUDIO_SINK_IDLE_TIMEOUT;
var idleTimeoutB = (BigDecimal) getThing().getConfiguration().get(idleTimeoutPropName);
return idleTimeout;
}
+ private String safeGetDeviceNameOrDescription() {
+ DeviceIdentifier deviceIdentifierFinal = deviceIdentifier;
+ return deviceIdentifierFinal == null ? "UNKNOWN" : deviceIdentifierFinal.getNameOrDescription();
+ }
+
public int getBasicProtocolSOTimeout() {
var soTimeout = (BigDecimal) getThing().getConfiguration().get(DEVICE_PARAMETER_AUDIO_SOCKET_SO_TIMEOUT);
return soTimeout != null ? soTimeout.intValue() : 500;
*/
package org.openhab.binding.pulseaudio.internal.items;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.pulseaudio.internal.handler.DeviceIdentifier;
/**
* GenericAudioItems are any kind of items that deal with audio data and can be
protected boolean muted;
protected int volume;
protected @Nullable Module module;
+ protected String secondaryIdentifier;
+ protected Map<String, String> properties;
- public AbstractAudioDeviceConfig(int id, String name, @Nullable Module module) {
+ public AbstractAudioDeviceConfig(int id, String name, @Nullable String secondaryIdentifier,
+ Map<String, String> properties, @Nullable Module module) {
super(id, name);
this.module = module;
+ this.secondaryIdentifier = secondaryIdentifier == null ? "" : secondaryIdentifier;
+ this.properties = properties;
+ }
+
+ /**
+ *
+ * @param deviceIdentifier The device identifier to check against
+ * @return true if this device match the requested identifier, false otherwise
+ */
+ public boolean matches(DeviceIdentifier deviceIdentifier) {
+ boolean matches = getPaName().equalsIgnoreCase(deviceIdentifier.getNameOrDescription())
+ || secondaryIdentifier.equalsIgnoreCase(deviceIdentifier.getNameOrDescription());
+ if (!matches) {
+ return false; // stop analysis right here, no need to parse properties
+ } else {
+ List<Pattern> additionalFilters = deviceIdentifier.getAdditionalFilters();
+ if (additionalFilters.isEmpty()) { // the additionalFilter property is not defined, don't check against
+ return true;
+ } else {
+ for (Pattern patternToMatch : additionalFilters) {
+ if (!properties.values().stream().anyMatch(value -> patternToMatch.matcher(value).find())) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
}
public @Nullable Module getModule() {
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
protected List<String> combinedSinkNames;
protected List<Sink> combinedSinks;
- public Sink(int id, String name, @Nullable Module module) {
- super(id, name, module);
+ public Sink(int id, String name, String description, Map<String, String> properties, @Nullable Module module) {
+ super(id, name, description, properties, module);
combinedSinkNames = new ArrayList<>();
combinedSinks = new ArrayList<>();
}
*/
package org.openhab.binding.pulseaudio.internal.items;
+import java.util.Map;
+
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
@Nullable
private Sink sink;
- public SinkInput(int id, String name, @Nullable Module module) {
- super(id, name, module);
+ public SinkInput(int id, String name, String description, Map<String, String> properties, @Nullable Module module) {
+ super(id, name, description, properties, module);
}
public @Nullable Sink getSink() {
*/
package org.openhab.binding.pulseaudio.internal.items;
+import java.util.Map;
+
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
@Nullable
protected Sink monitorOf;
- public Source(int id, String name, @Nullable Module module) {
- super(id, name, module);
+ public Source(int id, String name, String description, Map<String, String> properties, @Nullable Module module) {
+ super(id, name, description, properties, module);
}
public @Nullable Sink getMonitorOf() {
*/
package org.openhab.binding.pulseaudio.internal.items;
+import java.util.Map;
+
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
@Nullable
private Source source;
- public SourceOutput(int id, String name, @Nullable Module module) {
- super(id, name, module);
+ public SourceOutput(int id, String name, String description, Map<String, String> properties,
+ @Nullable Module module) {
+ super(id, name, description, properties, module);
}
public @Nullable Source getSource() {
thing-type.config.pulseaudio.sink.activateSimpleProtocolSink.label = Create an Audio Sink with simple-protocol-tcp
thing-type.config.pulseaudio.sink.activateSimpleProtocolSink.description = Activation of a corresponding sink in OpenHAB (module-simple-protocol-tcp must be available on the pulseaudio server)
thing-type.config.pulseaudio.sink.name.label = Name
-thing-type.config.pulseaudio.sink.name.description = The name of one specific device.
+thing-type.config.pulseaudio.sink.name.description = The name of one specific device. You can also use the description.
+thing-type.config.pulseaudio.sink.additionalFilters.label = Additional Filters
+thing-type.config.pulseaudio.sink.additionalFilters.description = Additional filters to select the proper device on the pulseaudio server, in case of ambiguity. To be selected, the device should have at least a property value matching this regular expression. You can use multiple regular expressions (separator is ###).
thing-type.config.pulseaudio.sink.simpleProtocolSOTimeout.label = Simple Protocol SO Timeout
thing-type.config.pulseaudio.sink.simpleProtocolSOTimeout.description = Socket SO timeout when connecting to pulseaudio server though module-simple-protocol-tcp. You can tune this option if the socket disconnect frequently.
thing-type.config.pulseaudio.sink.simpleProtocolSinkIdleTimeout.label = Idle Timeout
thing-type.config.pulseaudio.sink.simpleProtocolSinkPort.label = Simple Protocol Port
thing-type.config.pulseaudio.sink.simpleProtocolSinkPort.description = Default Port to allocate for use by module-simple-protocol-tcp on the pulseaudio server
thing-type.config.pulseaudio.sinkInput.name.label = Name
-thing-type.config.pulseaudio.sinkInput.name.description = The name of one specific device.
+thing-type.config.pulseaudio.sinkInput.name.description = The name of one specific device. You can also use the application name.
+thing-type.config.pulseaudio.sinkInput.additionalFilters.label = Additional Filters
+thing-type.config.pulseaudio.sinkInput.additionalFilters.description = Additional filters to select the proper device on the pulseaudio server, in case of ambiguity. To be selected, the device should have at least a property value matching this regular expression. You can use multiple regular expressions (separator is ###).
thing-type.config.pulseaudio.source.activateSimpleProtocolSource.label = Create an Audio Source with simple-protocol-tcp
thing-type.config.pulseaudio.source.activateSimpleProtocolSource.description = Activation of a corresponding source in OpenHAB (module-simple-protocol-tcp must be available on the pulseaudio server)
thing-type.config.pulseaudio.source.name.label = Name
-thing-type.config.pulseaudio.source.name.description = The name of one specific device.
+thing-type.config.pulseaudio.source.name.description = The name of one specific device. You can also use the description.
+thing-type.config.pulseaudio.source.additionalFilters.label = Additional Filters
+thing-type.config.pulseaudio.source.additionalFilters.description = Additional filters to select the proper device on the pulseaudio server, in case of ambiguity. To be selected, the device should have at least a property value matching this regular expression. You can use multiple regular expressions (separator is ###).
thing-type.config.pulseaudio.source.simpleProtocolSOTimeout.label = Simple Protocol SO Timeout
thing-type.config.pulseaudio.source.simpleProtocolSOTimeout.description = Socket SO timeout when connecting to pulseaudio server though module-simple-protocol-tcp. You can tune this option if the socket disconnect frequently.
thing-type.config.pulseaudio.source.simpleProtocolSourceChannels.label = Simple Protocol Channels
thing-type.config.pulseaudio.source.simpleProtocolSourceRate.label = Simple Protocol Rate
thing-type.config.pulseaudio.source.simpleProtocolSourceRate.description = The audio sample rate to be used by module-simple-protocol-tcp on the pulseaudio server
thing-type.config.pulseaudio.sourceOutput.name.label = Name
-thing-type.config.pulseaudio.sourceOutput.name.description = The name of one specific device.
+thing-type.config.pulseaudio.sourceOutput.name.description = The name of one specific device. You can also use the application name.
+thing-type.config.pulseaudio.sourceOutput.additionalFilters.label = Additional Filters
+thing-type.config.pulseaudio.sourceOutput.additionalFilters.description = Additional filters to select the proper device on the pulseaudio server, in case of ambiguity. To be selected, the device should have at least a property value matching this regular expression. You can use multiple regular expressions (separator is ###).
# channel types
<config-description>
<parameter name="name" type="text" required="true">
<label>Name</label>
- <description>The name of one specific device.</description>
+ <description>The name of one specific device. You can also use the application name.</description>
+ </parameter>
+ <parameter name="additionalFilters" type="text" required="false">
+ <label>Additional Filters</label>
+ <advanced>true</advanced>
+ <description>Additional filters to select the proper device on the pulseaudio server, in case of ambiguity.
+ To be
+ selected, the device should have at least a property value matching this regular expression. You can use
+ multiple
+ regular expressions (separator ###).</description>
</parameter>
</config-description>
</thing-type>
<config-description>
<parameter name="name" type="text" required="true">
<label>Name</label>
- <description>The name of one specific device.</description>
+ <description>The name of one specific device. You can also use the description.</description>
</parameter>
<parameter name="activateSimpleProtocolSink" type="boolean" required="false">
<label>Create an Audio Sink with simple-protocol-tcp</label>
pulseaudio server)</description>
<default>false</default>
</parameter>
+ <parameter name="additionalFilters" type="text" required="false">
+ <label>Additional Filters</label>
+ <advanced>true</advanced>
+ <description>Additional filters to select the proper device on the pulseaudio server, in case of ambiguity.
+ To be
+ selected, the device should have at least a property value matching this regular expression. You can use
+ multiple
+ regular expressions (separator ###).</description>
+ </parameter>
<parameter name="simpleProtocolSinkPort" type="integer" required="false">
<label>Simple Protocol Port</label>
<description>Default Port to allocate for use by module-simple-protocol-tcp on the pulseaudio server</description>
<config-description>
<parameter name="name" type="text" required="true">
<label>Name</label>
- <description>The name of one specific device.</description>
+ <description>The name of one specific device. You can also use the application name.</description>
+ </parameter>
+ <parameter name="additionalFilters" type="text" required="false">
+ <label>Additional Filters</label>
+ <advanced>true</advanced>
+ <description>Additional filters to select the proper device on the pulseaudio server, in case of ambiguity.
+ To be
+ selected, the device should have at least a property value matching this regular expression. You can use
+ multiple
+ regular expressions (separator is ###).</description>
</parameter>
</config-description>
</thing-type>
<config-description>
<parameter name="name" type="text" required="true">
<label>Name</label>
- <description>The name of one specific device.</description>
+ <description>The name of one specific device. You can also use the description.</description>
+ </parameter>
+ <parameter name="additionalFilters" type="text" required="false">
+ <label>Additional Filters</label>
+ <advanced>true</advanced>
+ <description>Additional filters to select the proper device on the pulseaudio server, in case of ambiguity.
+ To be
+ selected, the device should have at least a property value matching this regular expression. You can use
+ multiple
+ regular expressions (separator ###).</description>
</parameter>
<parameter name="activateSimpleProtocolSource" type="boolean" required="false">
<label>Create an Audio Source with simple-protocol-tcp</label>