Skip to content
This repository has been archived by the owner on May 17, 2021. It is now read-only.

Adds support for enabling / disabling MPD outputs #1408

Merged
merged 5 commits into from
Sep 9, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 <code>itemName</code>
* and <code>comnand</code>) or <code>null</code> if no playerCommand could
* and <code>command</code>) or <code>null</code> if no playerCommand could
* be found.
*
* @param itemName the item for which to find a mpdPlayerCommand
Expand All @@ -37,6 +39,21 @@ public interface MpdBindingProvider extends BindingProvider {
*/
String getPlayerCommand(String itemName, String command);

/**
* Returns the matching player command param (associated to <code>itemName</code>
* and <code>command</code>) or <code>null</code> 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 <code>null</code> if no matching
* mpdPlayerCommand param could be found.
*
* @since 1.6.0
*/
String getPlayerCommandParam(String itemName, String command);

/**
* Returns all Items associated to <code>playerId</code> and <code>playerCommand</code>
*
Expand All @@ -48,4 +65,19 @@ public interface MpdBindingProvider extends BindingProvider {
*/
String[] getItemNamesByPlayerAndPlayerCommand(String playerId, PlayerCommandTypeMapping playerCommand);

/**
* Returns all Items associated to <code>playerId</code> and that have a
* <code>playerCommand</code>=<code>outputId</code> 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 <code>playerId</code>
* and have a <code>playerCommand</code>=<code>outputId</code> binding.
*
* @since 1.6.0
*/
String[] getItemNamesByPlayerOutputCommand(String playerId, PlayerCommandTypeMapping playerCommand, int outputId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -70,6 +77,7 @@
*
* @author Thomas.Eichstaedt-Engelen
* @author Petr.Klus
* @author Matthew Bowman
*
* @since 0.8.0
*/
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -424,6 +448,39 @@ public void volumeChanged(VolumeChangeEvent vce) {
}
}


/**
* Handles MPD output change events.
*
* @param playerId the playerId which generated the <code>event</code>
* @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<String> itemNames = new HashSet<String>();
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<String> itemNames = new HashSet<String>();
for (MpdBindingProvider provider : this.providers) {
Expand Down Expand Up @@ -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 {

Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,14 @@
* </ul>
*
* @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}
*/
Expand Down Expand Up @@ -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)) {
Expand All @@ -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}
*/
Expand All @@ -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<String> itemNames = new HashSet<String>();
for (String itemName : bindingConfigs.keySet()) {
MpdBindingConfig config = (MpdBindingConfig) bindingConfigs.get(itemName);
// We're looking for either...
// ---
// ON = <player-id>:enable
// ON:param = <output-id>
// --- or ---
// OFF = <player-id>:disable
// OFF:param = <output-id>
// ---
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);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down