]> git.basschouten.com Git - openhab-addons.git/commitdiff
[squeezebox] Cleanup / code simplification (#10941)
authormaniac103 <dannybaumann@web.de>
Sun, 18 Jul 2021 17:16:48 +0000 (19:16 +0200)
committerGitHub <noreply@github.com>
Sun, 18 Jul 2021 17:16:48 +0000 (19:16 +0200)
* [squeezebox] Reduce boilerplate by using lambda expressions.
* [squeezebox] Make code dealing with key/value responses more readable.
* Fix off-by-one mistake.
* [squeezebox] Simplify CLI response parsing code.
* [squeezebox] Optimize some redundant code.

Signed-off-by: Danny Baumann <dannybaumann@web.de>
bundles/org.openhab.binding.squeezebox/src/main/java/org/openhab/binding/squeezebox/internal/discovery/SqueezeBoxPlayerDiscoveryParticipant.java
bundles/org.openhab.binding.squeezebox/src/main/java/org/openhab/binding/squeezebox/internal/handler/SqueezeBoxPlayer.java
bundles/org.openhab.binding.squeezebox/src/main/java/org/openhab/binding/squeezebox/internal/handler/SqueezeBoxPlayerHandler.java
bundles/org.openhab.binding.squeezebox/src/main/java/org/openhab/binding/squeezebox/internal/handler/SqueezeBoxPlayerState.java
bundles/org.openhab.binding.squeezebox/src/main/java/org/openhab/binding/squeezebox/internal/handler/SqueezeBoxServerHandler.java

index 5cd1b03bdb7b4f8f042c146dbd192862ef21f229..b6a749f5c2ff6e2d6414e3283d672ae176643267 100644 (file)
@@ -85,32 +85,31 @@ public class SqueezeBoxPlayerDiscoveryParticipant extends AbstractDiscoveryServi
     public void playerAdded(SqueezeBoxPlayer player) {
         ThingUID bridgeUID = squeezeBoxServerHandler.getThing().getUID();
 
-        ThingUID thingUID = new ThingUID(SQUEEZEBOXPLAYER_THING_TYPE, bridgeUID,
-                player.getMacAddress().replace(":", ""));
+        ThingUID thingUID = new ThingUID(SQUEEZEBOXPLAYER_THING_TYPE, bridgeUID, player.macAddress.replace(":", ""));
 
         if (!playerThingExists(thingUID)) {
-            logger.debug("player added {} : {} ", player.getMacAddress(), player.getName());
+            logger.debug("player added {} : {} ", player.macAddress, player.name);
 
             Map<String, Object> properties = new HashMap<>(1);
             String representationPropertyName = "mac";
-            properties.put(representationPropertyName, player.getMacAddress());
+            properties.put(representationPropertyName, player.macAddress);
 
             // Added other properties
-            properties.put("modelId", player.getModel());
-            properties.put("name", player.getName());
-            properties.put("uid", player.getUuid());
-            properties.put("ip", player.getIpAddr());
+            properties.put("modelId", player.model);
+            properties.put("name", player.name);
+            properties.put("uid", player.uuid);
+            properties.put("ip", player.ipAddr);
 
             DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
-                    .withRepresentationProperty(representationPropertyName).withBridge(bridgeUID)
-                    .withLabel(player.getName()).build();
+                    .withRepresentationProperty(representationPropertyName).withBridge(bridgeUID).withLabel(player.name)
+                    .build();
 
             thingDiscovered(discoveryResult);
         }
     }
 
     private boolean playerThingExists(ThingUID newThingUID) {
-        return squeezeBoxServerHandler.getThing().getThing(newThingUID) != null ? true : false;
+        return squeezeBoxServerHandler.getThing().getThing(newThingUID) != null;
     }
 
     /**
index 1a47fc71e975463da22092b50e3b8c585b38e116..955fe41e20199d768a06b62cf174335c057c91b9 100644 (file)
  */
 package org.openhab.binding.squeezebox.internal.handler;
 
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
 /**
  * Represents a Squeeze Player
  *
  * @author Dan Cunningham - Initial contribution
  *
  */
+@NonNullByDefault
 public class SqueezeBoxPlayer {
-    public String macAddress;
-    public String name;
-    public String ipAddr;
-    public String model;
-    public String uuid;
-
-    public SqueezeBoxPlayer() {
-        super();
-    }
-
-    /**
-     * UID of player
-     *
-     * @return
-     */
-    public String getUuid() {
-        return uuid;
-    }
-
-    /**
-     * UID of player
-     *
-     * @param uuid
-     */
-    public void setUuid(String uuid) {
-        this.uuid = uuid;
-    }
-
-    /**
-     * Mac Address of player
-     *
-     * @param macAddress
-     */
-    public String getMacAddress() {
-        return macAddress;
-    }
-
-    /**
-     * Mac Address of player
-     *
-     * @param macAddress
-     */
-    public void setMacAddress(String macAddress) {
-        this.macAddress = macAddress;
-    }
-
-    /**
-     * The name (label) of a player
-     *
-     * @return
-     */
-    public String getName() {
-        return name;
-    }
-
-    /**
-     * The name (label) of a player
-     *
-     * @param name
-     */
-    public void setName(String name) {
+    public final String macAddress;
+    @Nullable
+    public final String name;
+    @Nullable
+    public final String ipAddr;
+    @Nullable
+    public final String model;
+    @Nullable
+    public final String uuid;
+
+    public SqueezeBoxPlayer(String mac, @Nullable String name, @Nullable String ip, @Nullable String model,
+            @Nullable String uuid) {
+        this.macAddress = mac;
         this.name = name;
-    }
-
-    /**
-     * The ip address of a player
-     *
-     * @return
-     */
-    public String getIpAddr() {
-        return ipAddr;
-    }
-
-    /**
-     * The ip address of a player
-     *
-     * @param ipAddr
-     */
-    public void setIpAddr(String ipAddr) {
-        this.ipAddr = ipAddr;
-    }
-
-    /**
-     * The type of player
-     *
-     * @return
-     */
-    public String getModel() {
-        return model;
-    }
-
-    /**
-     * The type of player
-     *
-     * @param model
-     */
-    public void setModel(String model) {
+        this.ipAddr = ip;
         this.model = model;
+        this.uuid = uuid;
     }
 }
index a94461276336ed5d2aeda9863ea0c727c74e8b3a..2750de9b811c06354509e255f20174b079261edc 100644 (file)
@@ -577,83 +577,52 @@ public class SqueezeBoxPlayerHandler extends BaseThingHandler implements Squeeze
      * @return
      */
     int currentVolume() {
-        if (stateMap.containsKey(CHANNEL_VOLUME)) {
-            return ((DecimalType) stateMap.get(CHANNEL_VOLUME)).intValue();
-        } else {
-            return 0;
-        }
+        return cachedStateAsInt(CHANNEL_VOLUME);
     }
 
     int currentPlayingTime() {
-        if (stateMap.containsKey(CHANNEL_CURRENT_PLAYING_TIME)) {
-            return ((DecimalType) stateMap.get(CHANNEL_CURRENT_PLAYING_TIME)).intValue();
-        } else {
-            return 0;
-        }
+        return cachedStateAsInt(CHANNEL_CURRENT_PLAYING_TIME);
     }
 
     int currentNumberPlaylistTracks() {
-        if (stateMap.containsKey(CHANNEL_NUMBER_PLAYLIST_TRACKS)) {
-            return ((DecimalType) stateMap.get(CHANNEL_NUMBER_PLAYLIST_TRACKS)).intValue();
-        } else {
-            return 0;
-        }
+        return cachedStateAsInt(CHANNEL_NUMBER_PLAYLIST_TRACKS);
     }
 
     int currentPlaylistIndex() {
-        if (stateMap.containsKey(CHANNEL_PLAYLIST_INDEX)) {
-            return ((DecimalType) stateMap.get(CHANNEL_PLAYLIST_INDEX)).intValue();
-        } else {
-            return 0;
-        }
+        return cachedStateAsInt(CHANNEL_PLAYLIST_INDEX);
     }
 
     boolean currentPower() {
-        if (stateMap.containsKey(CHANNEL_POWER)) {
-            return (stateMap.get(CHANNEL_POWER).equals(OnOffType.ON) ? true : false);
-        } else {
-            return false;
-        }
+        return cachedStateAsBoolean(CHANNEL_POWER, OnOffType.ON);
     }
 
     boolean currentStop() {
-        if (stateMap.containsKey(CHANNEL_STOP)) {
-            return (stateMap.get(CHANNEL_STOP).equals(OnOffType.ON) ? true : false);
-        } else {
-            return false;
-        }
+        return cachedStateAsBoolean(CHANNEL_STOP, OnOffType.ON);
     }
 
     boolean currentControl() {
-        if (stateMap.containsKey(CHANNEL_CONTROL)) {
-            return (stateMap.get(CHANNEL_CONTROL).equals(PlayPauseType.PLAY) ? true : false);
-        } else {
-            return false;
-        }
+        return cachedStateAsBoolean(CHANNEL_CONTROL, PlayPauseType.PLAY);
     }
 
     boolean currentMute() {
-        if (stateMap.containsKey(CHANNEL_MUTE)) {
-            return (stateMap.get(CHANNEL_MUTE).equals(OnOffType.ON) ? true : false);
-        } else {
-            return false;
-        }
+        return cachedStateAsBoolean(CHANNEL_MUTE, OnOffType.ON);
     }
 
     int currentShuffle() {
-        if (stateMap.containsKey(CHANNEL_CURRENT_PLAYLIST_SHUFFLE)) {
-            return ((DecimalType) stateMap.get(CHANNEL_CURRENT_PLAYLIST_SHUFFLE)).intValue();
-        } else {
-            return 0;
-        }
+        return cachedStateAsInt(CHANNEL_CURRENT_PLAYLIST_SHUFFLE);
     }
 
     int currentRepeat() {
-        if (stateMap.containsKey(CHANNEL_CURRENT_PLAYLIST_REPEAT)) {
-            return ((DecimalType) stateMap.get(CHANNEL_CURRENT_PLAYLIST_REPEAT)).intValue();
-        } else {
-            return 0;
-        }
+        return cachedStateAsInt(CHANNEL_CURRENT_PLAYLIST_REPEAT);
+    }
+
+    private boolean cachedStateAsBoolean(String key, @NonNull State activeState) {
+        return activeState.equals(stateMap.get(key));
+    }
+
+    private int cachedStateAsInt(String key) {
+        State state = stateMap.get(key);
+        return state instanceof DecimalType ? ((DecimalType) state).intValue() : 0;
     }
 
     /**
index 82603069f32e2244c02a0376cf96a76a9374f130..a8223f29ec7d27219f5c8808ab71dd77de171406 100644 (file)
@@ -60,7 +60,7 @@ class SqueezeBoxPlayerState {
     }
 
     boolean isShuffling() {
-        return savedShuffle == 0 ? false : true;
+        return savedShuffle != 0;
     }
 
     int getShuffle() {
@@ -68,7 +68,7 @@ class SqueezeBoxPlayerState {
     }
 
     boolean isRepeating() {
-        return savedRepeat == 0 ? false : true;
+        return savedRepeat != 0;
     }
 
     int getRepeat() {
index fc5194d1817bb9af49c4f5bd9598e40ed52a0016..a7f9a36ae23b944d2a461b2936053c2101a90342 100644 (file)
@@ -37,7 +37,9 @@ import java.util.Set;
 import java.util.concurrent.Future;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
+import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.openhab.binding.squeezebox.internal.config.SqueezeBoxServerConfig;
 import org.openhab.binding.squeezebox.internal.dto.ButtonDTO;
 import org.openhab.binding.squeezebox.internal.dto.ButtonDTODeserializer;
@@ -535,69 +537,65 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
             }
         }
 
+        @NonNullByDefault
+        private class KeyValue {
+            final String key;
+            final String value;
+
+            public KeyValue(String key, String value) {
+                this.key = key;
+                this.value = value;
+            }
+        };
+
+        private List<KeyValue> decodeKeyValueResponse(String[] response) {
+            final List<KeyValue> keysAndValues = new ArrayList<>();
+            if (response != null) {
+                for (String line : response) {
+                    final String decoded = decode(line);
+                    int colonPos = decoded.indexOf(":");
+                    if (colonPos < 0) {
+                        continue;
+                    }
+                    keysAndValues.add(new KeyValue(decoded.substring(0, colonPos), decoded.substring(colonPos + 1)));
+                }
+            }
+            return keysAndValues;
+        }
+
         private void handlePlayersList(String message) {
             final Set<String> connectedPlayers = new HashSet<>();
 
             // Split out players
             String[] playersList = message.split("playerindex\\S*\\s");
             for (String playerParams : playersList) {
-
                 // For each player, split out parameters and decode parameter
-                String[] parameterList = playerParams.split("\\s");
-                for (int i = 0; i < parameterList.length; i++) {
-                    parameterList[i] = decode(parameterList[i]);
-                }
-
-                // parse out the MAC address first
-                String macAddress = null;
-                for (String parameter : parameterList) {
-                    if (parameter.contains("playerid")) {
-                        macAddress = parameter.substring(parameter.indexOf(":") + 1);
-                        break;
-                    }
-                }
+                final Map<String, String> keysAndValues = decodeKeyValueResponse(playerParams.split("\\s")).stream()
+                        .collect(Collectors.toMap(kv -> kv.key, kv -> kv.value));
+                final String macAddress = keysAndValues.get("playerid");
 
                 // if none found then ignore this set of params
                 if (macAddress == null) {
                     continue;
                 }
 
-                final SqueezeBoxPlayer player = new SqueezeBoxPlayer();
-                player.setMacAddress(macAddress);
-                // populate the player state
-                for (String parameter : parameterList) {
-                    if (parameter.startsWith("ip:")) {
-                        player.setIpAddr(parameter.substring(parameter.indexOf(":") + 1));
-                    } else if (parameter.startsWith("uuid:")) {
-                        player.setUuid(parameter.substring(parameter.indexOf(":") + 1));
-                    } else if (parameter.startsWith("name:")) {
-                        player.setName(parameter.substring(parameter.indexOf(":") + 1));
-                    } else if (parameter.startsWith("model:")) {
-                        player.setModel(parameter.substring(parameter.indexOf(":") + 1));
-                    } else if (parameter.startsWith("connected:")) {
-                        if ("1".equals(parameter.substring(parameter.indexOf(":") + 1))) {
-                            connectedPlayers.add(macAddress);
-                        }
-                    }
+                final SqueezeBoxPlayer player = new SqueezeBoxPlayer(macAddress, keysAndValues.get("name"),
+                        keysAndValues.get("ip"), keysAndValues.get("model"), keysAndValues.get("uuid"));
+                if ("1".equals(keysAndValues.get("connected"))) {
+                    connectedPlayers.add(macAddress);
                 }
 
                 // Save player if we haven't seen it yet
                 if (!players.containsKey(macAddress)) {
                     players.put(macAddress, player);
-                    updatePlayer(new PlayerUpdateEvent() {
-                        @Override
-                        public void updateListener(SqueezeBoxPlayerEventListener listener) {
-                            listener.playerAdded(player);
-                        }
-                    });
+                    updatePlayer(listener -> listener.playerAdded(player));
                     // tell the server we want to subscribe to player updates
-                    sendCommand(player.getMacAddress() + " status - 1 subscribe:10 tags:yagJlNKjc");
+                    sendCommand(player.macAddress + " status - 1 subscribe:10 tags:yagJlNKjc");
                 }
             }
             for (final SqueezeBoxPlayer player : players.values()) {
-                final String mac = player.getMacAddress();
-                final boolean connected = connectedPlayers.contains(mac);
-                updatePlayer(listener -> listener.connectedStateChangeEvent(mac, connected));
+                final boolean connected = connectedPlayers.contains(player.macAddress);
+                updatePlayer(listener -> listener.connectedStateChangeEvent(player.macAddress, connected));
             }
         }
 
@@ -630,12 +628,7 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
                     break;
                 case "ir":
                     final String ircode = messageParts[2];
-                    updatePlayer(new PlayerUpdateEvent() {
-                        @Override
-                        public void updateListener(SqueezeBoxPlayerEventListener listener) {
-                            listener.irCodeChangeEvent(mac, ircode);
-                        }
-                    });
+                    updatePlayer(listener -> listener.irCodeChangeEvent(mac, ircode));
                     break;
                 default:
                     logger.trace("Unhandled player update message type '{}'.", messageType);
@@ -651,23 +644,20 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
             switch (action) {
                 case "volume":
                     String volumeStringValue = decode(messageParts[3]);
-                    updatePlayer(new PlayerUpdateEvent() {
-                        @Override
-                        public void updateListener(SqueezeBoxPlayerEventListener listener) {
-                            try {
-                                int volume = Integer.parseInt(volumeStringValue);
-
-                                // Check if we received a relative volume change, or an absolute
-                                // volume value.
-                                if (volumeStringValue.contains("+") || (volumeStringValue.contains("-"))) {
-                                    listener.relativeVolumeChangeEvent(mac, volume);
-                                } else {
-                                    listener.absoluteVolumeChangeEvent(mac, volume);
-                                }
-                            } catch (NumberFormatException e) {
-                                logger.warn("Unable to parse volume [{}] received from mixer message.",
-                                        volumeStringValue, e);
+                    updatePlayer(listener -> {
+                        try {
+                            int volume = Integer.parseInt(volumeStringValue);
+
+                            // Check if we received a relative volume change, or an absolute
+                            // volume value.
+                            if (volumeStringValue.contains("+") || (volumeStringValue.contains("-"))) {
+                                listener.relativeVolumeChangeEvent(mac, volume);
+                            } else {
+                                listener.absoluteVolumeChangeEvent(mac, volume);
                             }
+                        } catch (NumberFormatException e) {
+                            logger.warn("Unable to parse volume [{}] received from mixer message.", volumeStringValue,
+                                    e);
                         }
                     });
                     break;
@@ -703,148 +693,89 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
             String coverid = null;
             String artworkUrl = null;
 
-            for (String messagePart : messageParts) {
+            for (KeyValue entry : decodeKeyValueResponse(messageParts)) {
                 // Parameter Power
-                if (messagePart.startsWith("power%3A")) {
-                    final boolean power = "1".matches(messagePart.substring("power%3A".length()));
-                    updatePlayer(new PlayerUpdateEvent() {
-                        @Override
-                        public void updateListener(SqueezeBoxPlayerEventListener listener) {
-                            listener.powerChangeEvent(mac, power);
-                        }
-                    });
+                if ("power".equals(entry.key)) {
+                    final boolean power = "1".equals(entry.value);
+                    updatePlayer(listener -> listener.powerChangeEvent(mac, power));
                 }
                 // Parameter Volume
-                else if (messagePart.startsWith("mixer%20volume%3A")) {
-                    String value = messagePart.substring("mixer%20volume%3A".length());
-                    final int volume = (int) Double.parseDouble(value);
-                    updatePlayer(new PlayerUpdateEvent() {
-                        @Override
-                        public void updateListener(SqueezeBoxPlayerEventListener listener) {
-                            listener.absoluteVolumeChangeEvent(mac, volume);
-                        }
-                    });
+                else if ("mixer volume".equals(entry.key)) {
+                    final int volume = (int) Double.parseDouble(entry.value);
+                    updatePlayer(listener -> listener.absoluteVolumeChangeEvent(mac, volume));
                 }
                 // Parameter Mode
-                else if (messagePart.startsWith("mode%3A")) {
-                    final String mode = messagePart.substring("mode%3A".length());
-                    updatePlayer(new PlayerUpdateEvent() {
-                        @Override
-                        public void updateListener(SqueezeBoxPlayerEventListener listener) {
-                            listener.modeChangeEvent(mac, mode);
-                        }
-                    });
+                else if ("mode".equals(entry.key)) {
+                    updatePlayer(listener -> listener.modeChangeEvent(mac, entry.value));
                 }
                 // Parameter Playing Time
-                else if (messagePart.startsWith("time%3A")) {
-                    String value = messagePart.substring("time%3A".length());
-                    final int time = (int) Double.parseDouble(value);
-                    updatePlayer(new PlayerUpdateEvent() {
-                        @Override
-                        public void updateListener(SqueezeBoxPlayerEventListener listener) {
-                            listener.currentPlayingTimeEvent(mac, time);
-                        }
-                    });
+                else if ("time".equals(entry.key)) {
+                    final int time = (int) Double.parseDouble(entry.value);
+                    updatePlayer(listener -> listener.currentPlayingTimeEvent(mac, time));
                 }
                 // Parameter duration
-                else if (messagePart.startsWith("duration%3A")) {
-                    String value = messagePart.substring("duration%3A".length());
-                    final int duration = (int) Double.parseDouble(value);
-                    updatePlayer(new PlayerUpdateEvent() {
-                        @Override
-                        public void updateListener(SqueezeBoxPlayerEventListener listener) {
-                            listener.durationEvent(mac, duration);
-                        }
-                    });
+                else if ("duration".equals(entry.key)) {
+                    final int duration = (int) Double.parseDouble(entry.value);
+                    updatePlayer(listener -> listener.durationEvent(mac, duration));
                 }
                 // Parameter Playing Playlist Index
-                else if (messagePart.startsWith("playlist_cur_index%3A")) {
-                    String value = messagePart.substring("playlist_cur_index%3A".length());
-                    final int index = (int) Double.parseDouble(value);
-                    updatePlayer(new PlayerUpdateEvent() {
-                        @Override
-                        public void updateListener(SqueezeBoxPlayerEventListener listener) {
-                            listener.currentPlaylistIndexEvent(mac, index);
-                        }
-                    });
+                else if ("playlist_cur_index".equals(entry.key)) {
+                    final int index = (int) Double.parseDouble(entry.value);
+                    updatePlayer(listener -> listener.currentPlaylistIndexEvent(mac, index));
                 }
                 // Parameter Playlist Number Tracks
-                else if (messagePart.startsWith("playlist_tracks%3A")) {
-                    String value = messagePart.substring("playlist_tracks%3A".length());
-                    final int track = (int) Double.parseDouble(value);
-                    updatePlayer(new PlayerUpdateEvent() {
-                        @Override
-                        public void updateListener(SqueezeBoxPlayerEventListener listener) {
-                            listener.numberPlaylistTracksEvent(mac, track);
-                        }
-                    });
+                else if ("playlist_tracks".equals(entry.key)) {
+                    final int track = (int) Double.parseDouble(entry.value);
+                    updatePlayer(listener -> listener.numberPlaylistTracksEvent(mac, track));
                 }
                 // Parameter Playlist Repeat Mode
-                else if (messagePart.startsWith("playlist%20repeat%3A")) {
-                    String value = messagePart.substring("playlist%20repeat%3A".length());
-                    final int repeat = (int) Double.parseDouble(value);
-                    updatePlayer(new PlayerUpdateEvent() {
-                        @Override
-                        public void updateListener(SqueezeBoxPlayerEventListener listener) {
-                            listener.currentPlaylistRepeatEvent(mac, repeat);
-                        }
-                    });
+                else if ("playlist repeat".equals(entry.key)) {
+                    final int repeat = (int) Double.parseDouble(entry.value);
+                    updatePlayer(listener -> listener.currentPlaylistRepeatEvent(mac, repeat));
                 }
                 // Parameter Playlist Shuffle Mode
-                else if (messagePart.startsWith("playlist%20shuffle%3A")) {
-                    String value = messagePart.substring("playlist%20shuffle%3A".length());
-                    final int shuffle = (int) Double.parseDouble(value);
-                    updatePlayer(new PlayerUpdateEvent() {
-                        @Override
-                        public void updateListener(SqueezeBoxPlayerEventListener listener) {
-                            listener.currentPlaylistShuffleEvent(mac, shuffle);
-                        }
-                    });
+                else if ("playlist shuffle".equals(entry.key)) {
+                    final int shuffle = (int) Double.parseDouble(entry.value);
+                    updatePlayer(listener -> listener.currentPlaylistShuffleEvent(mac, shuffle));
                 }
                 // Parameter Title
-                else if (messagePart.startsWith("title%3A")) {
-                    final String value = messagePart.substring("title%3A".length());
-                    updatePlayer(new PlayerUpdateEvent() {
-                        @Override
-                        public void updateListener(SqueezeBoxPlayerEventListener listener) {
-                            listener.titleChangeEvent(mac, decode(value));
-                        }
-                    });
+                else if ("title".equals(entry.key)) {
+                    updatePlayer(listener -> listener.titleChangeEvent(mac, entry.value));
                 }
                 // Parameter Remote Title (radio)
-                else if (messagePart.startsWith("remote_title%3A")) {
-                    remoteTitle = messagePart.substring("remote_title%3A".length());
+                else if ("remote_title".equals(entry.key)) {
+                    remoteTitle = entry.value;
                 }
                 // Parameter Artist
-                else if (messagePart.startsWith("artist%3A")) {
-                    artist = messagePart.substring("artist%3A".length());
+                else if ("artist".equals(entry.key)) {
+                    artist = entry.value;
                 }
                 // Parameter Album
-                else if (messagePart.startsWith("album%3A")) {
-                    album = messagePart.substring("album%3A".length());
+                else if ("album".equals(entry.key)) {
+                    album = entry.value;
                 }
                 // Parameter Genre
-                else if (messagePart.startsWith("genre%3A")) {
-                    genre = messagePart.substring("genre%3A".length());
+                else if ("genre".equals(entry.key)) {
+                    genre = entry.value;
                 }
                 // Parameter Year
-                else if (messagePart.startsWith("year%3A")) {
-                    year = messagePart.substring("year%3A".length());
+                else if ("year".equals(entry.key)) {
+                    year = entry.value;
                 }
                 // Parameter artwork_url contains url to cover art
-                else if (messagePart.startsWith("artwork_url%3A")) {
-                    artworkUrl = messagePart.substring("artwork_url%3A".length());
+                else if ("artwork_url".equals(entry.key)) {
+                    artworkUrl = entry.value;
                 }
                 // When coverart is "1" coverid will contain a unique coverart id
-                else if (messagePart.startsWith("coverart%3A")) {
-                    coverart = "1".matches(messagePart.substring("coverart%3A".length()));
+                else if ("coverart".equals(entry.key)) {
+                    coverart = "1".equals(entry.value);
                 }
                 // Id for covert art (only valid when coverart is "1")
-                else if (messagePart.startsWith("coverid%3A")) {
-                    coverid = messagePart.substring("coverid%3A".length());
+                else if ("coverid".equals(entry.key)) {
+                    coverid = entry.value;
                 } else {
                     // Added to be able to see additional status message types
-                    logger.trace("Unhandled status message type '{}'", messagePart);
+                    logger.trace("Unhandled status message type '{}' (value '{}')", entry.key, entry.value);
                 }
             }
 
@@ -855,16 +786,13 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
             final String finalGenre = genre;
             final String finalYear = year;
 
-            updatePlayer(new PlayerUpdateEvent() {
-                @Override
-                public void updateListener(SqueezeBoxPlayerEventListener listener) {
-                    listener.coverArtChangeEvent(mac, finalUrl);
-                    listener.remoteTitleChangeEvent(mac, decode(finalRemoteTitle));
-                    listener.artistChangeEvent(mac, decode(finalArtist));
-                    listener.albumChangeEvent(mac, decode(finalAlbum));
-                    listener.genreChangeEvent(mac, decode(finalGenre));
-                    listener.yearChangeEvent(mac, decode(finalYear));
-                }
+            updatePlayer(listener -> {
+                listener.coverArtChangeEvent(mac, finalUrl);
+                listener.remoteTitleChangeEvent(mac, finalRemoteTitle);
+                listener.artistChangeEvent(mac, finalArtist);
+                listener.albumChangeEvent(mac, finalAlbum);
+                listener.genreChangeEvent(mac, finalGenre);
+                listener.yearChangeEvent(mac, finalYear);
             });
         }
 
@@ -888,13 +816,13 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
             } else if (artwork_url != null) {
                 if (artwork_url.startsWith("http")) {
                     // Typically indicates that cover art is not local to LMS
-                    url = decode(artwork_url);
-                } else if (artwork_url.startsWith("%2F")) {
+                    url = artwork_url;
+                } else if (artwork_url.startsWith("/")) {
                     // Typically used for default coverart for plugins (e.g. Pandora, etc.)
-                    url = hostAndPort + decode(artwork_url);
+                    url = hostAndPort + artwork_url;
                 } else {
                     // Another variation of default coverart for plugins (e.g. Pandora, etc.)
-                    url = hostAndPort + "/" + decode(artwork_url);
+                    url = hostAndPort + "/" + artwork_url;
                 }
             }
             return url;
@@ -911,12 +839,7 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
                 // Execute in separate thread to avoid delaying listener
                 scheduler.execute(() -> updateCustomButtons(mac));
                 // Set the track duration to 0
-                updatePlayer(new PlayerUpdateEvent() {
-                    @Override
-                    public void updateListener(SqueezeBoxPlayerEventListener listener) {
-                        listener.durationEvent(mac, 0);
-                    }
-                });
+                updatePlayer(listener -> listener.durationEvent(mac, 0));
             } else if (action.equals("pause")) {
                 if (messageParts.length < 4) {
                     return;
@@ -935,22 +858,12 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
                 return;
             }
             final String value = mode;
-            updatePlayer(new PlayerUpdateEvent() {
-                @Override
-                public void updateListener(SqueezeBoxPlayerEventListener listener) {
-                    listener.modeChangeEvent(mac, value);
-                }
-            });
+            updatePlayer(listener -> listener.modeChangeEvent(mac, value));
         }
 
         private void handleSourceChangeMessage(String mac, String rawSource) {
             String source = URLDecoder.decode(rawSource);
-            updatePlayer(new PlayerUpdateEvent() {
-                @Override
-                public void updateListener(SqueezeBoxPlayerEventListener listener) {
-                    listener.sourceChangeEvent(mac, source);
-                }
-            });
+            updatePlayer(listener -> listener.sourceChangeEvent(mac, source));
         }
 
         private void handlePrefsetMessage(final String mac, String[] messageParts) {
@@ -963,20 +876,10 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
                 String value = messageParts[4];
                 if (function.equals("power")) {
                     final boolean power = value.equals("1");
-                    updatePlayer(new PlayerUpdateEvent() {
-                        @Override
-                        public void updateListener(SqueezeBoxPlayerEventListener listener) {
-                            listener.powerChangeEvent(mac, power);
-                        }
-                    });
+                    updatePlayer(listener -> listener.powerChangeEvent(mac, power));
                 } else if (function.equals("volume")) {
                     final int volume = (int) Double.parseDouble(value);
-                    updatePlayer(new PlayerUpdateEvent() {
-                        @Override
-                        public void updateListener(SqueezeBoxPlayerEventListener listener) {
-                            listener.absoluteVolumeChangeEvent(mac, volume);
-                        }
-                    });
+                    updatePlayer(listener -> listener.absoluteVolumeChangeEvent(mac, volume));
                 }
             }
         }
@@ -996,26 +899,22 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
             List<Favorite> favorites = new ArrayList<>();
             Favorite f = null;
             boolean isTypePlaylist = false;
-            for (String part : messageParts) {
+            for (KeyValue entry : decodeKeyValueResponse(messageParts)) {
                 // Favorite ID (in form xxxxxxxxx.n)
-                if (part.startsWith("id%3A")) {
-                    String id = part.substring("id%3A".length());
-                    f = new Favorite(id);
+                if ("id".equals(entry.key)) {
+                    f = new Favorite(entry.value);
                     favorites.add(f);
                     isTypePlaylist = false;
                 }
                 // Favorite name
-                else if (part.startsWith("name%3A")) {
-                    String name = decode(part.substring("name%3A".length()));
-                    if (f != null) {
-                        f.name = name;
-                    }
-                } else if (part.equals("type%3Aplaylist")) {
+                else if ("name".equals(entry.key)) {
+                    f.name = entry.value;
+                } else if ("type".equals(entry.key) && "playlist".equals(entry.value)) {
                     isTypePlaylist = true;
                 }
                 // When "1", favorite is a submenu with additional favorites
-                else if (part.startsWith("hasitems%3A")) {
-                    boolean hasitems = "1".matches(part.substring("hasitems%3A".length()));
+                else if ("hasitems".equals(entry.key)) {
+                    boolean hasitems = "1".equals(entry.value);
                     if (f != null) {
                         // Except for some favorites (e.g. Spotify) use hasitems:1 and type:playlist
                         if (hasitems && isTypePlaylist == false) {
@@ -1026,19 +925,10 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
                     }
                 }
             }
-            updatePlayersFavoritesList(favorites);
+            updatePlayer(listener -> listener.updateFavoritesListEvent(favorites));
             updateChannelFavoritesList(favorites);
         }
 
-        private void updatePlayersFavoritesList(List<Favorite> favorites) {
-            updatePlayer(new PlayerUpdateEvent() {
-                @Override
-                public void updateListener(SqueezeBoxPlayerEventListener listener) {
-                    listener.updateFavoritesListEvent(favorites);
-                }
-            });
-        }
-
         private void updateChannelFavoritesList(List<Favorite> favorites) {
             final Channel channel = getThing().getChannel(CHANNEL_FAVORITES_LIST);
             if (channel == null) {
@@ -1098,12 +988,7 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
                 }
                 final String like = likeCommand;
                 final String unlike = unlikeCommand;
-                updatePlayer(new PlayerUpdateEvent() {
-                    @Override
-                    public void updateListener(SqueezeBoxPlayerEventListener listener) {
-                        listener.buttonsChangeEvent(mac, like, unlike);
-                    }
-                });
+                updatePlayer(listener -> listener.buttonsChangeEvent(mac, like, unlike));
             }
         }