diff --git a/bundles/binding/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/MpdBindingProvider.java b/bundles/binding/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/MpdBindingProvider.java
index de7ccc24814..eb0bede06e0 100644
--- a/bundles/binding/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/MpdBindingProvider.java
+++ b/bundles/binding/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/MpdBindingProvider.java
@@ -20,13 +20,15 @@
* taken into account.
*
* @author Thomas.Eichstaedt-Engelen
+ * @author Matthew Bowman
+ *
* @since 0.8.0
*/
public interface MpdBindingProvider extends BindingProvider {
/**
* Returns the matching player command (associated to itemName
- * and comnand
) or null
if no playerCommand could
+ * and command
) or null
if no playerCommand could
* be found.
*
* @param itemName the item for which to find a mpdPlayerCommand
@@ -37,6 +39,21 @@ public interface MpdBindingProvider extends BindingProvider {
*/
String getPlayerCommand(String itemName, String command);
+ /**
+ * Returns the matching player command param (associated to itemName
+ * and command
) or null
if no playerCommand param could
+ * be found.
+ *
+ * @param itemName the item for which to find a mpdPlayerCommand param
+ * @param command the openHAB command for which to find a mpdPlayerCommand param
+ *
+ * @return the matching mpdPlayerCommand param or null
if no matching
+ * mpdPlayerCommand param could be found.
+ *
+ * @since 1.6.0
+ */
+ String getPlayerCommandParam(String itemName, String command);
+
/**
* Returns all Items associated to playerId
and playerCommand
*
@@ -48,4 +65,19 @@ public interface MpdBindingProvider extends BindingProvider {
*/
String[] getItemNamesByPlayerAndPlayerCommand(String playerId, PlayerCommandTypeMapping playerCommand);
+ /**
+ * Returns all Items associated to playerId
and that have a
+ * playerCommand
=outputId
binding.
+ *
+ * @param playerId the id of the player for which items should be returned
+ * @param playerCommand the openHAB command for which items should be returned
+ * @param outputId the MPD output id for which items should be returned
+ *
+ * @return the name of all items which are associated to playerId
+ * and have a playerCommand
=outputId
binding.
+ *
+ * @since 1.6.0
+ */
+ String[] getItemNamesByPlayerOutputCommand(String playerId, PlayerCommandTypeMapping playerCommand, int outputId);
+
}
diff --git a/bundles/binding/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/internal/MpdBinding.java b/bundles/binding/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/internal/MpdBinding.java
index ce8e7d0f283..c59a0bf503d 100644
--- a/bundles/binding/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/internal/MpdBinding.java
+++ b/bundles/binding/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/internal/MpdBinding.java
@@ -26,11 +26,17 @@
import org.apache.commons.lang.StringUtils;
import org.bff.javampd.MPD;
+import org.bff.javampd.MPDAdmin;
+import org.bff.javampd.MPDOutput;
import org.bff.javampd.MPDPlayer;
import org.bff.javampd.MPDPlayer.PlayerStatus;
+import org.bff.javampd.events.OutputChangeEvent;
+import org.bff.javampd.events.OutputChangeListener;
import org.bff.javampd.events.PlayerBasicChangeEvent;
import org.bff.javampd.events.PlayerBasicChangeListener;
import org.bff.javampd.events.PlayerChangeEvent;
+import org.bff.javampd.events.TrackPositionChangeEvent;
+import org.bff.javampd.events.TrackPositionChangeListener;
import org.bff.javampd.events.VolumeChangeEvent;
import org.bff.javampd.events.VolumeChangeListener;
import org.bff.javampd.exception.MPDConnectionException;
@@ -45,6 +51,7 @@
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.types.Command;
+import org.openhab.core.types.State;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.quartz.CronScheduleBuilder;
@@ -70,6 +77,7 @@
*
* @author Thomas.Eichstaedt-Engelen
* @author Petr.Klus
+ * @author Matthew Bowman
*
* @since 0.8.0
*/
@@ -135,6 +143,11 @@ public void internalReceiveCommand(String itemName, Command command) {
String playerCommand =
provider.getPlayerCommand(itemName, matchingPlayerCommand);
if (StringUtils.isNotBlank(playerCommand)) {
+ String playerCommandParam =
+ provider.getPlayerCommandParam(itemName, matchingPlayerCommand);
+ if (playerCommandParam != null) {
+ params = playerCommandParam;
+ }
executePlayerCommand(playerCommand, params);
}
@@ -196,6 +209,17 @@ private void executePlayerCommand(String playerCommandLine, Object commandParams
case VOLUME_DECREASE: player.setVolume(player.getVolume() - VOLUME_CHANGE_SIZE); break;
case NEXT: player.playNext(); break;
case PREV: player.playPrev(); break;
+ case ENABLE:
+ case DISABLE:
+ Integer outputId = Integer.valueOf((String) commandParams);
+ MPDAdmin admin = daemon.getMPDAdmin();
+ MPDOutput output = new MPDOutput(outputId - 1); // internally mpd uses 0-based indexing
+ if (pCommand == PlayerCommandTypeMapping.ENABLE) {
+ admin.enableOutput(output);
+ } else {
+ admin.disableOutput(output);
+ }
+ break;
case VOLUME:
logger.debug("Volume adjustment received: '{}' '{}'", pCommand, commandParams);
player.setVolume(((PercentType) commandParams).intValue());
@@ -424,6 +448,39 @@ public void volumeChanged(VolumeChangeEvent vce) {
}
}
+
+ /**
+ * Handles MPD output change events.
+ *
+ * @param playerId the playerId which generated the event
+ * @param event the {@link OutputChangeEvent} that occurred
+ *
+ * @since 1.6.0
+ */
+ private void outputChanged(String playerId, OutputChangeEvent event) {
+ MPDOutput output = (MPDOutput) event.getSource();
+ logger.debug("Output {} changed on player {}, enabled = {}", output.getId(), playerId, output.isEnabled());
+ PlayerCommandTypeMapping playerCommand =
+ output.isEnabled() ? PlayerCommandTypeMapping.ENABLE
+ : PlayerCommandTypeMapping.DISABLE;
+ String[] itemNames = getItemsByPlayerCommandAndOutput(playerId, playerCommand, output);
+ for (String itemName : itemNames) {
+ eventPublisher.postUpdate(itemName, (State) playerCommand.type);
+ }
+ }
+
+
+ private String[] getItemsByPlayerCommandAndOutput(String playerId, PlayerCommandTypeMapping playerCommand, MPDOutput output) {
+ Set itemNames = new HashSet();
+ int outputId = output.getId() + 1; // internally mpd uses 0-based indexes
+ for (MpdBindingProvider provider : this.providers) {
+ itemNames.addAll(Arrays.asList(
+ provider.getItemNamesByPlayerOutputCommand(playerId, playerCommand, outputId)));
+ }
+ return itemNames.toArray(new String[itemNames.size()]);
+ }
+
+
private String[] getItemNamesByPlayerAndPlayerCommand(String playerId, PlayerCommandTypeMapping playerCommand) {
Set itemNames = new HashSet();
for (MpdBindingProvider provider : this.providers) {
@@ -546,7 +603,7 @@ private void connectAllPlayersAndMonitors() {
* @param host
* @param port
*/
- private void connect(String playerId) {
+ private void connect(final String playerId) {
MpdPlayerConfig config = null;
try {
@@ -559,6 +616,24 @@ private void connect(String playerId) {
mpdStandAloneMonitor.addVolumeChangeListener(this);
mpdStandAloneMonitor.addPlayerChangeListener(this);
mpdStandAloneMonitor.addTrackPositionChangeListener(this);
+
+ final MpdBinding self = this; // 'this' glue for the inner anon instance
+ mpdStandAloneMonitor.addOutputChangeListener(new OutputChangeListener() {
+
+ @Override
+ public void outputChanged(OutputChangeEvent e) {
+ // We have to 'wrap' the OutputChangeEvent listener
+ // callback and add the playerId so we know which
+ // player generated the event. There's not enough
+ // info on just the OutputChangeEvent to derive
+ // the source player. This 'workaround' is necessary
+ // to support output control on multiple MPD players.
+ self.outputChanged(playerId, e);
+
+ }
+
+ });
+
Thread monitorThread = new Thread(
mpdStandAloneMonitor, "MPD Monitor (player:" + playerId + ")");
monitorThread.start();
diff --git a/bundles/binding/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/internal/MpdGenericBindingProvider.java b/bundles/binding/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/internal/MpdGenericBindingProvider.java
index a11bb9b163c..edeba46e04d 100644
--- a/bundles/binding/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/internal/MpdGenericBindingProvider.java
+++ b/bundles/binding/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/internal/MpdGenericBindingProvider.java
@@ -35,10 +35,14 @@
*
*
* @author Thomas.Eichstaedt-Engelen
+ * @author Matthew Bowman
+ *
* @since 0.8.0
*/
public class MpdGenericBindingProvider extends AbstractGenericBindingProvider implements MpdBindingProvider {
+ private static final String PARAM_SUFFIX = ":param";
+
/**
* {@inheritDoc}
*/
@@ -86,6 +90,15 @@ protected void parseBindingConfig(String bindingConfigs, MpdBindingConfig config
String command = StringUtils.trim(configParts[0]);
String playerId = StringUtils.trim(configParts[1]);
String playerCommand = StringUtils.trim(configParts[2]);
+ // check for optional command=param binding
+ String[] playerCommandParts = playerCommand.split("=");
+ if (playerCommandParts.length == 2) {
+ // rewrite command=param -> command
+ playerCommand = StringUtils.trim(playerCommandParts[0]);
+ String playerCommandParam = StringUtils.trim(playerCommandParts[1]);
+ // save the param in the config
+ config.put(command + PARAM_SUFFIX, playerCommandParam);
+ }
// if there are more commands to parse do that recursively ...
if (StringUtils.isNotBlank(bindingConfigTail)) {
@@ -103,6 +116,14 @@ public String getPlayerCommand(String itemName, String command) {
return config != null ? config.get(command) : null;
}
+ /**
+ * {@inheritDoc}
+ */
+ public String getPlayerCommandParam(String itemName, String command) {
+ MpdBindingConfig config = (MpdBindingConfig) bindingConfigs.get(itemName);
+ return config != null ? config.get(command + PARAM_SUFFIX) : null;
+ }
+
/**
* {@inheritDoc}
*/
@@ -118,6 +139,39 @@ public String[] getItemNamesByPlayerAndPlayerCommand(String playerId, PlayerComm
itemNames.add(itemName);
}
else if (mpdConfig.containsKey(playerCommand.type.toString())) {
+ // we check to make sure the binding config contains
+ // playerId:playerCommand otherwise we get extra items
+ String actual = mpdConfig.get(playerCommand.type.toString());
+ String expected = playerId + ":" + playerCommand.toString().toLowerCase();
+ if (StringUtils.equals(actual, expected)) {
+ itemNames.add(itemName);
+ }
+ }
+ }
+ return itemNames.toArray(new String[itemNames.size()]);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String[] getItemNamesByPlayerOutputCommand(String playerId, PlayerCommandTypeMapping command, int outputId) {
+ Set itemNames = new HashSet();
+ for (String itemName : bindingConfigs.keySet()) {
+ MpdBindingConfig config = (MpdBindingConfig) bindingConfigs.get(itemName);
+ // We're looking for either...
+ // ---
+ // ON = :enable
+ // ON:param =
+ // --- or ---
+ // OFF = :disable
+ // OFF:param =
+ // ---
+ String k1 = command.type.toString();
+ String v1 = playerId + ":" + command.toString().toLowerCase();
+ String k2 = command.type.toString() + PARAM_SUFFIX;
+ String v2 = String.valueOf(outputId);
+ if (StringUtils.equals(config.get(k1), v1)
+ && StringUtils.equals(config.get(k2), v2)) {
itemNames.add(itemName);
}
}
diff --git a/bundles/binding/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/internal/PlayerCommandTypeMapping.java b/bundles/binding/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/internal/PlayerCommandTypeMapping.java
index 7d12cbbe21d..df78061e67a 100644
--- a/bundles/binding/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/internal/PlayerCommandTypeMapping.java
+++ b/bundles/binding/org.openhab.binding.mpd/src/main/java/org/openhab/binding/mpd/internal/PlayerCommandTypeMapping.java
@@ -87,6 +87,20 @@ public enum PlayerCommandTypeMapping {
command = "prev";
type = OnOffType.OFF;
}
+ },
+
+ ENABLE {
+ {
+ command = "enable";
+ type = OnOffType.ON;
+ }
+ },
+
+ DISABLE {
+ {
+ command = "disable";
+ type = OnOffType.OFF;
+ }
};
/** Represents the player command as it will be used in *.items configuration */