[pulseaudio] Fix exception handling when connecting (#12423)

Fix bridge/thing status update
Also update log levels

Fix #12419
Fix #12424

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
This commit is contained in:
lolodomo 2022-03-08 00:01:54 +01:00 committed by GitHub
parent 20f6b52e71
commit e3ca3b01bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 161 additions and 126 deletions

View File

@ -18,7 +18,6 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.PrintStream; import java.io.PrintStream;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.net.NoRouteToHostException;
import java.net.Socket; import java.net.Socket;
import java.net.SocketException; import java.net.SocketException;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
@ -129,20 +128,18 @@ public class PulseaudioClient {
*/ */
private static final String MODULE_COMBINE_SINK = "module-combine-sink"; private static final String MODULE_COMBINE_SINK = "module-combine-sink";
public PulseaudioClient(String host, int port, PulseAudioBindingConfiguration configuration) throws IOException { public PulseaudioClient(String host, int port, PulseAudioBindingConfiguration configuration) {
this.host = host; this.host = host;
this.port = port; this.port = port;
this.configuration = configuration; this.configuration = configuration;
items = new ArrayList<>(); items = new ArrayList<>();
modules = new ArrayList<>(); modules = new ArrayList<>();
connect();
update();
} }
public boolean isConnected() { public boolean isConnected() {
return client != null ? client.isConnected() : false; Socket clientSocket = client;
return clientSocket != null ? clientSocket.isConnected() : false;
} }
/** /**
@ -378,9 +375,6 @@ public class PulseaudioClient {
* 0 - 65536) * 0 - 65536)
*/ */
public void setVolume(AbstractAudioDeviceConfig item, int vol) { public void setVolume(AbstractAudioDeviceConfig item, int vol) {
if (item == null) {
return;
}
String itemCommandName = getItemCommandName(item); String itemCommandName = getItemCommandName(item);
if (itemCommandName == null) { if (itemCommandName == null) {
return; return;
@ -485,7 +479,7 @@ public class PulseaudioClient {
.map(portS -> Integer.parseInt(portS)); .map(portS -> Integer.parseInt(portS));
} }
private @NonNull Optional<@NonNull String> extractArgumentFromLine(String argumentWanted, String argumentLine) { private Optional<@NonNull String> extractArgumentFromLine(String argumentWanted, String argumentLine) {
String argument = null; String argument = null;
int startPortIndex = argumentLine.indexOf(argumentWanted + "="); int startPortIndex = argumentLine.indexOf(argumentWanted + "=");
if (startPortIndex != -1) { if (startPortIndex != -1) {
@ -525,11 +519,8 @@ public class PulseaudioClient {
* @param vol the new volume percent value the {@link AbstractAudioDeviceConfig} should be changed to (possible * @param vol the new volume percent value the {@link AbstractAudioDeviceConfig} should be changed to (possible
* values from 0 - 100) * values from 0 - 100)
*/ */
public void setVolumePercent(@Nullable AbstractAudioDeviceConfig item, int vol) { public void setVolumePercent(AbstractAudioDeviceConfig item, int vol) {
int volumeToSet = vol; int volumeToSet = vol;
if (item == null) {
return;
}
if (volumeToSet <= 100) { if (volumeToSet <= 100) {
volumeToSet = toAbsoluteVolume(volumeToSet); volumeToSet = toAbsoluteVolume(volumeToSet);
} }
@ -662,15 +653,16 @@ public class PulseaudioClient {
private synchronized void sendRawCommand(String command) { private synchronized void sendRawCommand(String command) {
checkConnection(); checkConnection();
if (client != null && client.isConnected()) { Socket clientSocket = client;
if (clientSocket != null && clientSocket.isConnected()) {
try { try {
PrintStream out = new PrintStream(client.getOutputStream(), true); PrintStream out = new PrintStream(clientSocket.getOutputStream(), true);
logger.trace("sending command {} to pa-server {}", command, host); logger.trace("sending command {} to pa-server {}", command, host);
out.print(command + "\r\n"); out.print(command + "\r\n");
out.close(); out.close();
client.close(); clientSocket.close();
} catch (IOException e) { } catch (IOException e) {
logger.error("{}", e.getLocalizedMessage(), e); logger.warn("{}", e.getMessage(), e);
} }
} }
} }
@ -679,12 +671,13 @@ public class PulseaudioClient {
logger.trace("_sendRawRequest({})", command); logger.trace("_sendRawRequest({})", command);
checkConnection(); checkConnection();
String result = ""; String result = "";
if (client != null && client.isConnected()) { Socket clientSocket = client;
if (clientSocket != null && clientSocket.isConnected()) {
try { try {
PrintStream out = new PrintStream(client.getOutputStream(), true); PrintStream out = new PrintStream(clientSocket.getOutputStream(), true);
out.print(command + "\r\n"); out.print(command + "\r\n");
InputStream instr = client.getInputStream(); InputStream instr = clientSocket.getInputStream();
try { try {
byte[] buff = new byte[1024]; byte[] buff = new byte[1024];
@ -709,42 +702,52 @@ public class PulseaudioClient {
} catch (SocketException e) { } catch (SocketException e) {
logger.warn("Socket exception while sending pulseaudio command: {}", e.getMessage()); logger.warn("Socket exception while sending pulseaudio command: {}", e.getMessage());
} catch (IOException e) { } catch (IOException e) {
logger.error("Exception while reading socket: {}", e.getMessage()); logger.warn("Exception while reading socket: {}", e.getMessage());
} }
instr.close(); instr.close();
out.close(); out.close();
client.close(); clientSocket.close();
return result; return result;
} catch (IOException e) { } catch (IOException e) {
logger.error("{}", e.getLocalizedMessage(), e); logger.warn("{}", e.getMessage(), e);
} }
} }
return result; return result;
} }
private void checkConnection() { private void checkConnection() {
if (client == null || client.isClosed() || !client.isConnected()) { try {
try { connect();
connect(); } catch (IOException e) {
} catch (IOException e) { logger.debug("{}", e.getMessage(), e);
logger.error("{}", e.getLocalizedMessage(), e);
}
} }
} }
/** /**
* Connects to the pulseaudio server (timeout 500ms) * Connects to the pulseaudio server (timeout 500ms)
*/ */
private void connect() throws IOException { public void connect() throws IOException {
try { Socket clientSocket = client;
client = new Socket(host, port); if (clientSocket == null || clientSocket.isClosed() || !clientSocket.isConnected()) {
client.setSoTimeout(500); logger.trace("Try to connect...");
} catch (UnknownHostException e) { try {
logger.error("unknown socket host {}", host); client = new Socket(host, port);
} catch (NoRouteToHostException e) { client.setSoTimeout(500);
logger.error("no route to host {}", host); logger.trace("connected");
} catch (SocketException e) { } catch (UnknownHostException e) {
logger.error("cannot connect to host {} : {}", host, e.getMessage()); client = null;
throw new IOException("Unknown host", e);
} catch (IllegalArgumentException e) {
client = null;
throw new IOException("Invalid port", e);
} catch (SecurityException | SocketException e) {
client = null;
throw new IOException(
String.format("Cannot connect socket: %s", e.getMessage() != null ? e.getMessage() : ""), e);
} catch (IOException e) {
client = null;
throw e;
}
} }
} }
@ -752,11 +755,12 @@ public class PulseaudioClient {
* Disconnects from the pulseaudio server * Disconnects from the pulseaudio server
*/ */
public void disconnect() { public void disconnect() {
if (client != null) { Socket clientSocket = client;
if (clientSocket != null) {
try { try {
client.close(); clientSocket.close();
} catch (IOException e) { } catch (IOException e) {
logger.error("{}", e.getLocalizedMessage(), e); logger.debug("{}", e.getMessage(), e);
} }
} }
} }

View File

@ -34,6 +34,7 @@ import org.openhab.core.config.core.Configuration;
import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseBridgeHandler; import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.types.Command; import org.openhab.core.types.Command;
@ -67,11 +68,22 @@ public class PulseaudioBridgeHandler extends BaseBridgeHandler implements PulseA
private HashSet<String> lastActiveDevices = new HashSet<>(); private HashSet<String> lastActiveDevices = new HashSet<>();
private ScheduledFuture<?> pollingJob; private ScheduledFuture<?> pollingJob;
private Runnable pollingRunnable = () -> {
update();
};
private synchronized void update() { private synchronized void update() {
try {
client.connect();
if (getThing().getStatus() != ThingStatus.ONLINE) {
updateStatus(ThingStatus.ONLINE);
logger.debug("Established connection to Pulseaudio server on Host '{}':'{}'.", host, port);
}
} catch (IOException e) {
logger.debug("{}", e.getMessage(), e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
String.format("Couldn't connect to Pulsaudio server [Host '%s':'%d']: %s", host, port,
e.getMessage() != null ? e.getMessage() : ""));
return;
}
client.update(); client.update();
for (AbstractAudioDeviceConfig device : client.getItems()) { for (AbstractAudioDeviceConfig device : client.getItems()) {
if (lastActiveDevices != null && lastActiveDevices.contains(device.getPaName())) { if (lastActiveDevices != null && lastActiveDevices.contains(device.getPaName())) {
@ -79,7 +91,7 @@ public class PulseaudioBridgeHandler extends BaseBridgeHandler implements PulseA
try { try {
deviceStatusListener.onDeviceStateChanged(getThing().getUID(), device); deviceStatusListener.onDeviceStateChanged(getThing().getUID(), device);
} catch (Exception e) { } catch (Exception e) {
logger.error("An exception occurred while calling the DeviceStatusListener", e); logger.warn("An exception occurred while calling the DeviceStatusListener", e);
} }
} }
} else { } else {
@ -88,7 +100,7 @@ public class PulseaudioBridgeHandler extends BaseBridgeHandler implements PulseA
deviceStatusListener.onDeviceAdded(getThing(), device); deviceStatusListener.onDeviceAdded(getThing(), device);
deviceStatusListener.onDeviceStateChanged(getThing().getUID(), device); deviceStatusListener.onDeviceStateChanged(getThing().getUID(), device);
} catch (Exception e) { } catch (Exception e) {
logger.error("An exception occurred while calling the DeviceStatusListener", e); logger.warn("An exception occurred while calling the DeviceStatusListener", e);
} }
lastActiveDevices.add(device.getPaName()); lastActiveDevices.add(device.getPaName());
} }
@ -106,13 +118,7 @@ public class PulseaudioBridgeHandler extends BaseBridgeHandler implements PulseA
if (command instanceof RefreshType) { if (command instanceof RefreshType) {
client.update(); client.update();
} else { } else {
logger.warn("received invalid command for pulseaudio bridge '{}'.", host); logger.debug("received unexpected command for pulseaudio bridge '{}'.", host);
}
}
private synchronized void startAutomaticRefresh() {
if (pollingJob == null || pollingJob.isCancelled()) {
pollingJob = scheduler.scheduleWithFixedDelay(pollingRunnable, 0, refreshInterval, TimeUnit.MILLISECONDS);
} }
} }
@ -140,26 +146,15 @@ public class PulseaudioBridgeHandler extends BaseBridgeHandler implements PulseA
} }
if (host != null && !host.isEmpty()) { if (host != null && !host.isEmpty()) {
Runnable connectRunnable = () -> { client = new PulseaudioClient(host, port, configuration);
try { updateStatus(ThingStatus.UNKNOWN);
client = new PulseaudioClient(host, port, configuration); if (pollingJob == null || pollingJob.isCancelled()) {
if (client.isConnected()) { pollingJob = scheduler.scheduleWithFixedDelay(this::update, 0, refreshInterval, TimeUnit.MILLISECONDS);
updateStatus(ThingStatus.ONLINE); }
logger.info("Established connection to Pulseaudio server on Host '{}':'{}'.", host, port);
startAutomaticRefresh();
}
} catch (IOException e) {
logger.error("Couldn't connect to Pulsaudio server [Host '{}':'{}']: {}", host, port,
e.getLocalizedMessage());
updateStatus(ThingStatus.OFFLINE);
}
};
scheduler.schedule(connectRunnable, 0, TimeUnit.SECONDS);
} else { } else {
logger.warn( updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, String.format(
"Couldn't connect to Pulseaudio server because of missing connection parameters [Host '{}':'{}'].", "Couldn't connect to Pulseaudio server because of missing connection parameters [Host '%s':'%d']",
host, port); host, port));
updateStatus(ThingStatus.OFFLINE);
} }
this.configuration.addPulseAudioBindingConfigurationListener(this); this.configuration.addPulseAudioBindingConfigurationListener(this);
@ -168,8 +163,10 @@ public class PulseaudioBridgeHandler extends BaseBridgeHandler implements PulseA
@Override @Override
public void dispose() { public void dispose() {
this.configuration.removePulseAudioBindingConfigurationListener(this); this.configuration.removePulseAudioBindingConfigurationListener(this);
if (pollingJob != null) { ScheduledFuture<?> job = pollingJob;
pollingJob.cancel(true); if (job != null) {
job.cancel(true);
pollingJob = null;
} }
if (client != null) { if (client != null) {
client.disconnect(); client.disconnect();

View File

@ -50,6 +50,8 @@ import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID; import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.BaseThingHandler; import org.openhab.core.thing.binding.BaseThingHandler;
@ -101,15 +103,13 @@ public class PulseaudioHandler extends BaseThingHandler implements DeviceStatusL
Configuration config = getThing().getConfiguration(); Configuration config = getThing().getConfiguration();
name = (String) config.get(DEVICE_PARAMETER_NAME); name = (String) config.get(DEVICE_PARAMETER_NAME);
// until we get an update put the Thing offline updateStatus(ThingStatus.UNKNOWN);
updateStatus(ThingStatus.OFFLINE);
deviceOnlineWatchdog(); deviceOnlineWatchdog();
// if it's a SINK thing, then maybe we have to activate the audio sink // if it's a SINK thing, then maybe we have to activate the audio sink
if (PulseaudioBindingConstants.SINK_THING_TYPE.equals(thing.getThingTypeUID())) { if (SINK_THING_TYPE.equals(thing.getThingTypeUID())) {
// check the property to see if we it's enabled : // check the property to see if we it's enabled :
Boolean sinkActivated = (Boolean) thing.getConfiguration() Boolean sinkActivated = (Boolean) thing.getConfiguration().get(DEVICE_PARAMETER_AUDIO_SINK_ACTIVATION);
.get(PulseaudioBindingConstants.DEVICE_PARAMETER_AUDIO_SINK_ACTIVATION);
if (sinkActivated != null && sinkActivated) { if (sinkActivated != null && sinkActivated) {
audioSinkSetup(); audioSinkSetup();
} }
@ -182,22 +182,25 @@ public class PulseaudioHandler extends BaseThingHandler implements DeviceStatusL
@Override @Override
public void dispose() { public void dispose() {
if (refreshJob != null && !refreshJob.isCancelled()) { ScheduledFuture<?> job = refreshJob;
refreshJob.cancel(true); if (job != null && !job.isCancelled()) {
job.cancel(true);
refreshJob = null; refreshJob = null;
} }
updateStatus(ThingStatus.OFFLINE); PulseaudioBridgeHandler briHandler = bridgeHandler;
if (bridgeHandler != null) { if (briHandler != null) {
bridgeHandler.unregisterDeviceStatusListener(this); briHandler.unregisterDeviceStatusListener(this);
bridgeHandler = null; bridgeHandler = null;
} }
logger.trace("Thing {} {} disposed.", getThing().getUID(), name); logger.trace("Thing {} {} disposed.", getThing().getUID(), name);
super.dispose(); super.dispose();
if (audioSink != null) { PulseAudioAudioSink sink = audioSink;
audioSink.disconnect(); if (sink != null) {
sink.disconnect();
} }
if (audioSource != null) { PulseAudioAudioSource source = audioSource;
audioSource.disconnect(); if (source != null) {
source.disconnect();
} }
// Unregister the potential pulse audio sink's audio sink // Unregister the potential pulse audio sink's audio sink
ServiceRegistration<AudioSink> sinkReg = audioSinkRegistrations.remove(getThing().getUID().toString()); ServiceRegistration<AudioSink> sinkReg = audioSinkRegistrations.remove(getThing().getUID().toString());
@ -213,20 +216,42 @@ public class PulseaudioHandler extends BaseThingHandler implements DeviceStatusL
} }
} }
@Override
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE
&& getThing().getStatusInfo().getStatusDetail() == ThingStatusDetail.BRIDGE_OFFLINE) {
// Bridge is now ONLINE, restart the refresh job to get an update of the thing status without waiting
// its next planned run
ScheduledFuture<?> job = refreshJob;
if (job != null && !job.isCancelled()) {
job.cancel(true);
refreshJob = null;
}
deviceOnlineWatchdog();
} else if (bridgeStatusInfo.getStatus() == ThingStatus.OFFLINE
|| bridgeStatusInfo.getStatus() == ThingStatus.UNKNOWN) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
}
}
private void deviceOnlineWatchdog() { private void deviceOnlineWatchdog() {
Runnable runnable = () -> { Runnable runnable = () -> {
try { try {
PulseaudioBridgeHandler bridgeHandler = getPulseaudioBridgeHandler(); PulseaudioBridgeHandler bridgeHandler = getPulseaudioBridgeHandler();
if (bridgeHandler != null) { if (bridgeHandler != null) {
if (bridgeHandler.getDevice(name) == null) { if (bridgeHandler.getThing().getStatus() == ThingStatus.ONLINE) {
updateStatus(ThingStatus.OFFLINE); if (bridgeHandler.getDevice(name) == null) {
this.bridgeHandler = null; updateStatus(ThingStatus.OFFLINE);
this.bridgeHandler = null;
} else {
updateStatus(ThingStatus.ONLINE);
}
} else { } else {
updateStatus(ThingStatus.ONLINE); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
} }
} else { } else {
logger.debug("Bridge for pulseaudio device {} not found.", name); logger.debug("Bridge for pulseaudio device {} not found.", name);
updateStatus(ThingStatus.OFFLINE); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
} }
} catch (Exception e) { } catch (Exception e) {
logger.debug("Exception occurred during execution: {}", e.getMessage(), e); logger.debug("Exception occurred during execution: {}", e.getMessage(), e);
@ -258,17 +283,17 @@ public class PulseaudioHandler extends BaseThingHandler implements DeviceStatusL
@Override @Override
public void handleCommand(ChannelUID channelUID, Command command) { public void handleCommand(ChannelUID channelUID, Command command) {
PulseaudioBridgeHandler bridge = getPulseaudioBridgeHandler(); PulseaudioBridgeHandler briHandler = getPulseaudioBridgeHandler();
if (bridge == null) { if (briHandler == null) {
logger.warn("pulseaudio server bridge handler not found. Cannot handle command without bridge."); logger.debug("pulseaudio server bridge handler not found. Cannot handle command without bridge.");
return; return;
} }
if (command instanceof RefreshType) { if (command instanceof RefreshType) {
bridge.handleCommand(channelUID, command); briHandler.handleCommand(channelUID, command);
return; return;
} }
AbstractAudioDeviceConfig device = bridge.getDevice(name); AbstractAudioDeviceConfig device = briHandler.getDevice(name);
if (device == null) { if (device == null) {
logger.warn("device {} not found", name); logger.warn("device {} not found", name);
updateStatus(ThingStatus.OFFLINE); updateStatus(ThingStatus.OFFLINE);
@ -279,8 +304,8 @@ public class PulseaudioHandler extends BaseThingHandler implements DeviceStatusL
if (channelUID.getId().equals(VOLUME_CHANNEL)) { if (channelUID.getId().equals(VOLUME_CHANNEL)) {
if (command instanceof IncreaseDecreaseType) { if (command instanceof IncreaseDecreaseType) {
// refresh to get the current volume level // refresh to get the current volume level
bridge.getClient().update(); briHandler.getClient().update();
device = bridge.getDevice(name); device = briHandler.getDevice(name);
if (device == null) { if (device == null) {
logger.warn("missing device info, aborting"); logger.warn("missing device info, aborting");
return; return;
@ -293,24 +318,24 @@ public class PulseaudioHandler extends BaseThingHandler implements DeviceStatusL
if (command.equals(IncreaseDecreaseType.DECREASE)) { if (command.equals(IncreaseDecreaseType.DECREASE)) {
newVolume = Math.max(0, oldVolume - 5); newVolume = Math.max(0, oldVolume - 5);
} }
bridge.getClient().setVolumePercent(device, newVolume); briHandler.getClient().setVolumePercent(device, newVolume);
updateState = new PercentType(newVolume); updateState = new PercentType(newVolume);
savedVolume = newVolume; savedVolume = newVolume;
} else if (command instanceof PercentType) { } else if (command instanceof PercentType) {
DecimalType volume = (DecimalType) command; DecimalType volume = (DecimalType) command;
bridge.getClient().setVolumePercent(device, volume.intValue()); briHandler.getClient().setVolumePercent(device, volume.intValue());
updateState = (PercentType) command; updateState = (PercentType) command;
savedVolume = volume.intValue(); savedVolume = volume.intValue();
} else if (command instanceof DecimalType) { } else if (command instanceof DecimalType) {
// set volume // set volume
DecimalType volume = (DecimalType) command; DecimalType volume = (DecimalType) command;
bridge.getClient().setVolume(device, volume.intValue()); briHandler.getClient().setVolume(device, volume.intValue());
updateState = (DecimalType) command; updateState = (DecimalType) command;
savedVolume = volume.intValue(); savedVolume = volume.intValue();
} }
} else if (channelUID.getId().equals(MUTE_CHANNEL)) { } else if (channelUID.getId().equals(MUTE_CHANNEL)) {
if (command instanceof OnOffType) { if (command instanceof OnOffType) {
bridge.getClient().setMute(device, OnOffType.ON.equals(command)); briHandler.getClient().setMute(device, OnOffType.ON.equals(command));
updateState = (OnOffType) command; updateState = (OnOffType) command;
} }
} else if (channelUID.getId().equals(SLAVES_CHANNEL)) { } else if (channelUID.getId().equals(SLAVES_CHANNEL)) {
@ -318,32 +343,32 @@ public class PulseaudioHandler extends BaseThingHandler implements DeviceStatusL
if (command instanceof StringType) { if (command instanceof StringType) {
List<Sink> slaves = new ArrayList<>(); List<Sink> slaves = new ArrayList<>();
for (String slaveName : command.toString().split(",")) { for (String slaveName : command.toString().split(",")) {
Sink slave = bridge.getClient().getSink(slaveName.trim()); Sink slave = briHandler.getClient().getSink(slaveName.trim());
if (slave != null) { if (slave != null) {
slaves.add(slave); slaves.add(slave);
} }
} }
if (!slaves.isEmpty()) { if (!slaves.isEmpty()) {
bridge.getClient().setCombinedSinkSlaves(((Sink) device), slaves); briHandler.getClient().setCombinedSinkSlaves(((Sink) device), slaves);
} }
} }
} else { } else {
logger.error("{} is no combined sink", device); logger.warn("{} is no combined sink", device);
} }
} else if (channelUID.getId().equals(ROUTE_TO_SINK_CHANNEL)) { } else if (channelUID.getId().equals(ROUTE_TO_SINK_CHANNEL)) {
if (device instanceof SinkInput) { if (device instanceof SinkInput) {
Sink newSink = null; Sink newSink = null;
if (command instanceof DecimalType) { if (command instanceof DecimalType) {
newSink = bridge.getClient().getSink(((DecimalType) command).intValue()); newSink = briHandler.getClient().getSink(((DecimalType) command).intValue());
} else { } else {
newSink = bridge.getClient().getSink(command.toString()); newSink = briHandler.getClient().getSink(command.toString());
} }
if (newSink != null) { if (newSink != null) {
logger.debug("rerouting {} to {}", device, newSink); logger.debug("rerouting {} to {}", device, newSink);
bridge.getClient().moveSinkInput(((SinkInput) device), newSink); briHandler.getClient().moveSinkInput(((SinkInput) device), newSink);
updateState = new StringType(newSink.getPaName()); updateState = new StringType(newSink.getPaName());
} else { } else {
logger.error("no sink {} found", command.toString()); logger.warn("no sink {} found", command.toString());
} }
} }
} }
@ -361,25 +386,31 @@ public class PulseaudioHandler extends BaseThingHandler implements DeviceStatusL
*/ */
public int getLastVolume() { public int getLastVolume() {
if (savedVolume == null) { if (savedVolume == null) {
PulseaudioBridgeHandler bridge = getPulseaudioBridgeHandler(); PulseaudioBridgeHandler briHandler = getPulseaudioBridgeHandler();
// refresh to get the current volume level if (briHandler != null) {
bridge.getClient().update(); // refresh to get the current volume level
AbstractAudioDeviceConfig device = bridge.getDevice(name); briHandler.getClient().update();
if (device != null) { AbstractAudioDeviceConfig device = briHandler.getDevice(name);
savedVolume = device.getVolume(); if (device != null) {
savedVolume = device.getVolume();
}
} }
} }
return savedVolume == null ? 50 : savedVolume; return savedVolume == null ? 50 : savedVolume;
} }
public void setVolume(int volume) { public void setVolume(int volume) {
PulseaudioBridgeHandler bridge = getPulseaudioBridgeHandler(); PulseaudioBridgeHandler briHandler = getPulseaudioBridgeHandler();
AbstractAudioDeviceConfig device = bridge.getDevice(name); if (briHandler == null) {
logger.warn("bridge is not ready");
return;
}
AbstractAudioDeviceConfig device = briHandler.getDevice(name);
if (device == null) { if (device == null) {
logger.warn("missing device info, aborting"); logger.warn("missing device info, aborting");
return; return;
} }
bridge.getClient().setVolumePercent(device, volume); briHandler.getClient().setVolumePercent(device, volume);
updateState(VOLUME_CHANNEL, new PercentType(volume)); updateState(VOLUME_CHANNEL, new PercentType(volume));
savedVolume = volume; savedVolume = volume;
} }
@ -411,7 +442,7 @@ public class PulseaudioHandler extends BaseThingHandler implements DeviceStatusL
if (bridge != null) { if (bridge != null) {
return (String) bridge.getConfiguration().get(PulseaudioBindingConstants.BRIDGE_PARAMETER_HOST); return (String) bridge.getConfiguration().get(PulseaudioBindingConstants.BRIDGE_PARAMETER_HOST);
} else { } else {
logger.error("A bridge must be configured for this pulseaudio thing"); logger.warn("A bridge must be configured for this pulseaudio thing");
return "null"; return "null";
} }
} }
@ -425,8 +456,11 @@ public class PulseaudioHandler extends BaseThingHandler implements DeviceStatusL
* @throws InterruptedException when interrupted during the loading module wait * @throws InterruptedException when interrupted during the loading module wait
*/ */
public int getSimpleTcpPort() throws IOException, InterruptedException { public int getSimpleTcpPort() throws IOException, InterruptedException {
var bridgeHandler = getPulseaudioBridgeHandler(); var briHandler = getPulseaudioBridgeHandler();
AbstractAudioDeviceConfig device = bridgeHandler.getDevice(name); if (briHandler == null) {
throw new IOException("bridge is not ready");
}
AbstractAudioDeviceConfig device = briHandler.getDevice(name);
if (device == null) { if (device == null) {
throw new IOException("missing device info, device appears to be offline"); throw new IOException("missing device info, device appears to be offline");
} }
@ -439,7 +473,7 @@ public class PulseaudioHandler extends BaseThingHandler implements DeviceStatusL
BigDecimal simpleRate = (BigDecimal) getThing().getConfiguration().get(DEVICE_PARAMETER_AUDIO_SOURCE_RATE); BigDecimal simpleRate = (BigDecimal) getThing().getConfiguration().get(DEVICE_PARAMETER_AUDIO_SOURCE_RATE);
BigDecimal simpleChannels = (BigDecimal) getThing().getConfiguration() BigDecimal simpleChannels = (BigDecimal) getThing().getConfiguration()
.get(DEVICE_PARAMETER_AUDIO_SOURCE_CHANNELS); .get(DEVICE_PARAMETER_AUDIO_SOURCE_CHANNELS);
return getPulseaudioBridgeHandler().getClient() return briHandler.getClient()
.loadModuleSimpleProtocolTcpIfNeeded(device, simpleTcpPort, simpleFormat, simpleRate, simpleChannels) .loadModuleSimpleProtocolTcpIfNeeded(device, simpleTcpPort, simpleFormat, simpleRate, simpleChannels)
.orElse(simpleTcpPort); .orElse(simpleTcpPort);
} }