diff --git a/bundles/org.openhab.binding.bosesoundtouch/src/main/java/org/openhab/binding/bosesoundtouch/internal/CommandExecutor.java b/bundles/org.openhab.binding.bosesoundtouch/src/main/java/org/openhab/binding/bosesoundtouch/internal/CommandExecutor.java
index 7a01af606..65a1ad4d4 100644
--- a/bundles/org.openhab.binding.bosesoundtouch/src/main/java/org/openhab/binding/bosesoundtouch/internal/CommandExecutor.java
+++ b/bundles/org.openhab.binding.bosesoundtouch/src/main/java/org/openhab/binding/bosesoundtouch/internal/CommandExecutor.java
@@ -155,7 +155,6 @@ public class CommandExecutor implements AvailableSources {
contentItem.setPresetID(presetID);
currentContentItem = contentItem;
-
}
updateOperatingValues();
}
diff --git a/bundles/org.openhab.binding.bosesoundtouch/src/main/java/org/openhab/binding/bosesoundtouch/internal/XMLResponseHandler.java b/bundles/org.openhab.binding.bosesoundtouch/src/main/java/org/openhab/binding/bosesoundtouch/internal/XMLResponseHandler.java
index 502680e58..56b390fad 100644
--- a/bundles/org.openhab.binding.bosesoundtouch/src/main/java/org/openhab/binding/bosesoundtouch/internal/XMLResponseHandler.java
+++ b/bundles/org.openhab.binding.bosesoundtouch/src/main/java/org/openhab/binding/bosesoundtouch/internal/XMLResponseHandler.java
@@ -97,11 +97,6 @@ public class XMLResponseHandler extends DefaultHandler {
// showing a
// warning for unhandled states
- XMLHandlerState localState = null;
- if (stateMap != null) {
- localState = stateMap.get(localName);
- }
-
switch (curState) {
case INIT:
if ("updates".equals(localName)) {
@@ -112,10 +107,13 @@ public class XMLResponseHandler extends DefaultHandler {
state = XMLHandlerState.Unprocessed;
}
} else {
+ XMLHandlerState localState = stateMap.get(localName);
if (localState == null) {
- logger.debug("{}: Unhandled XML entity during {}: '{}", handler.getDeviceName(), curState,
+ logger.warn("{}: Unhandled XML entity during {}: '{}", handler.getDeviceName(), curState,
localName);
state = XMLHandlerState.Unprocessed;
+ } else {
+ state = localState;
}
}
break;
@@ -196,9 +194,11 @@ public class XMLResponseHandler extends DefaultHandler {
state = XMLHandlerState.Presets;
} else if ("group".equals(localName)) {
this.masterDeviceId = new BoseSoundTouchConfiguration();
+ state = stateMap.get(localName);
} else {
- if (localState == null) {
- logger.debug("{}: Unhandled XML entity during {}: '{}", handler.getDeviceName(), curState,
+ state = stateMap.get(localName);
+ if (state == null) {
+ logger.warn("{}: Unhandled XML entity during {}: '{}", handler.getDeviceName(), curState,
localName);
state = XMLHandlerState.Unprocessed;
@@ -366,16 +366,12 @@ public class XMLResponseHandler extends DefaultHandler {
if (contentItem == null) {
contentItem = new ContentItem();
}
- String source = "";
- String location = "";
- String sourceAccount = "";
- Boolean isPresetable = false;
if (attributes != null) {
- source = attributes.getValue("source");
- sourceAccount = attributes.getValue("sourceAccount");
- location = attributes.getValue("location");
- isPresetable = Boolean.parseBoolean(attributes.getValue("isPresetable"));
+ String source = attributes.getValue("source");
+ String location = attributes.getValue("location");
+ String sourceAccount = attributes.getValue("sourceAccount");
+ Boolean isPresetable = Boolean.parseBoolean(attributes.getValue("isPresetable"));
if (source != null) {
contentItem.setSource(source);
diff --git a/bundles/org.openhab.binding.bosesoundtouch/src/main/java/org/openhab/binding/bosesoundtouch/internal/XMLResponseProcessor.java b/bundles/org.openhab.binding.bosesoundtouch/src/main/java/org/openhab/binding/bosesoundtouch/internal/XMLResponseProcessor.java
index 533e53821..bd180faaf 100644
--- a/bundles/org.openhab.binding.bosesoundtouch/src/main/java/org/openhab/binding/bosesoundtouch/internal/XMLResponseProcessor.java
+++ b/bundles/org.openhab.binding.bosesoundtouch/src/main/java/org/openhab/binding/bosesoundtouch/internal/XMLResponseProcessor.java
@@ -47,6 +47,7 @@ public class XMLResponseProcessor {
public void handleMessage(String msg) throws SAXException, IOException, ParserConfigurationException {
SAXParserFactory parserFactory = SAXParserFactory.newInstance();
+ parserFactory.setNamespaceAware(true);
SAXParser parser = parserFactory.newSAXParser();
XMLReader reader = parser.getXMLReader();
reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
diff --git a/bundles/org.openhab.binding.bosesoundtouch/src/main/java/org/openhab/binding/bosesoundtouch/internal/handler/BoseSoundTouchHandler.java b/bundles/org.openhab.binding.bosesoundtouch/src/main/java/org/openhab/binding/bosesoundtouch/internal/handler/BoseSoundTouchHandler.java
index c5a1e8360..c69288c80 100644
--- a/bundles/org.openhab.binding.bosesoundtouch/src/main/java/org/openhab/binding/bosesoundtouch/internal/handler/BoseSoundTouchHandler.java
+++ b/bundles/org.openhab.binding.bosesoundtouch/src/main/java/org/openhab/binding/bosesoundtouch/internal/handler/BoseSoundTouchHandler.java
@@ -306,6 +306,14 @@ public class BoseSoundTouchHandler extends BaseThingHandler implements WebSocket
return commandExecutor;
}
+ /**
+ * Sets the CommandExecutor of this handler
+ *
+ */
+ public void setCommandExecutor(@Nullable CommandExecutor commandExecutor) {
+ this.commandExecutor = commandExecutor;
+ }
+
/**
* Returns the Session this handler has opened
*
diff --git a/bundles/org.openhab.binding.bosesoundtouch/src/test/java/org/openhab/binding/bosesoundtouch/internal/SoundTouch20Tests.java b/bundles/org.openhab.binding.bosesoundtouch/src/test/java/org/openhab/binding/bosesoundtouch/internal/SoundTouch20Tests.java
new file mode 100644
index 000000000..f7a7d954d
--- /dev/null
+++ b/bundles/org.openhab.binding.bosesoundtouch/src/test/java/org/openhab/binding/bosesoundtouch/internal/SoundTouch20Tests.java
@@ -0,0 +1,136 @@
+/**
+ * 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.bosesoundtouch.internal;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
+
+import java.text.MessageFormat;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.openhab.binding.bosesoundtouch.internal.handler.BoseSoundTouchHandler;
+import org.openhab.binding.bosesoundtouch.internal.handler.InMemmoryContentStorage;
+import org.openhab.core.config.core.Configuration;
+import org.openhab.core.library.types.PercentType;
+import org.openhab.core.library.types.StringListType;
+import org.openhab.core.storage.Storage;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingUID;
+import org.openhab.core.thing.binding.ThingHandlerCallback;
+import org.openhab.core.thing.binding.builder.ChannelBuilder;
+import org.openhab.core.thing.binding.builder.ThingBuilder;
+
+/**
+ *
+ * @author Leo Siepel - Initial contribution
+ *
+ */
+@ExtendWith(MockitoExtension.class)
+@NonNullByDefault
+public class SoundTouch20Tests {
+
+ private @Mock @NonNullByDefault({}) ThingHandlerCallback thingHandlerCallback;
+ private @NonNullByDefault({}) Thing soundTouchThing;
+ private @NonNullByDefault({}) BoseSoundTouchHandler thingHandler;
+ private @NonNullByDefault({}) XMLResponseProcessor processor;
+ private ThingUID thingUID = new ThingUID(BoseSoundTouchBindingConstants.BINDING_ID, "soundtouch20");
+ private ChannelUID volumeChannelUID = new ChannelUID(thingUID, BoseSoundTouchBindingConstants.CHANNEL_VOLUME);
+ private ChannelUID presetChannelUID = new ChannelUID(thingUID, BoseSoundTouchBindingConstants.CHANNEL_PRESET);
+ private Storage<@NonNull ContentItem> storage = new InMemmoryContentStorage();
+ private @Mock @NonNullByDefault({}) BoseStateDescriptionOptionProvider stateDescriptionProvider;
+
+ @BeforeEach
+ public void initialize() {
+ // arrange
+ Configuration config = new Configuration();
+ config.put(BoseSoundTouchConfiguration.MAC_ADDRESS, "B0D5CC1AAAA1");
+
+ soundTouchThing = ThingBuilder.create(BoseSoundTouchBindingConstants.BST_20_THING_TYPE_UID, thingUID)
+ .withConfiguration(config).withChannel(ChannelBuilder.create(volumeChannelUID, "Number").build())
+ .withChannel(ChannelBuilder.create(presetChannelUID, "Number").build()).build();
+
+ PresetContainer container = new PresetContainer(storage);
+ thingHandler = new BoseSoundTouchHandler(soundTouchThing, container, stateDescriptionProvider);
+ processor = new XMLResponseProcessor(thingHandler);
+ }
+
+ private void processIncomingMessage(String mesage) {
+ try {
+ processor.handleMessage(mesage);
+ } catch (Exception e) {
+ assert false : MessageFormat.format("handleMessage throws an exception: {0} Stacktrace: {1}",
+ e.getMessage(), e.getStackTrace());
+ }
+ }
+
+ @Test
+ public void configurationPropertyUpdated() {
+ // arange
+ CommandExecutor executor = new CommandExecutor(thingHandler);
+ thingHandler.setCommandExecutor(executor);
+ String message = "livingroomSoundTouch 203504027SCM27.0.6.46330.5043500 epdbuild.trunk.hepdswbld04.2022-08-04T11:20:29U6148010803720048000100PackagedProduct069430P5227013812https://streaming.bose.comB0D5CC1AAAA1192.168.1.15CF821E2FD76192.168.1.1sm2spottynormalGBGB";
+
+ // act
+ processIncomingMessage(message);
+
+ // assert
+ assertEquals("27.0.6.46330.5043500",
+ soundTouchThing.getProperties().get(org.openhab.core.thing.Thing.PROPERTY_FIRMWARE_VERSION));
+ }
+
+ @Test
+ public void channelVolumeUpdated() {
+ // arrange
+ CommandExecutor executor = new CommandExecutor(thingHandler);
+
+ thingHandler.setCommandExecutor(executor);
+ Mockito.when(thingHandlerCallback.isChannelLinked((ChannelUID) notNull())).thenReturn(true);
+ thingHandler.setCallback(thingHandlerCallback);
+ String message = "27";
+
+ // act
+ processIncomingMessage(message);
+
+ // assert
+ Mockito.verify(thingHandlerCallback).stateUpdated(eq(volumeChannelUID), eq(new PercentType("27")));
+ }
+
+ @Test
+ @Disabled
+ public void channelPresetUpdated() {
+ // arrange
+ CommandExecutor executor = new CommandExecutor(thingHandler);
+
+ thingHandler.setCommandExecutor(executor);
+ Mockito.when(thingHandlerCallback.isChannelLinked((ChannelUID) notNull())).thenReturn(true);
+ thingHandler.setCallback(thingHandlerCallback);
+ String message = "Radio FM1http://cdn-profiles.tunein.com/s25077/images/logoq.jpgMedicine At MidnightConcrete & GoldSWR3http://radiotime-logos.s3.amazonaws.com/s24896q.pngSRF 3http://radiotime-logos.s3.amazonaws.com/s24862q.pngSonic Highways";
+
+ // act
+ processIncomingMessage(message);
+
+ // assert
+ // TODO: check if preset channels have changed
+ Mockito.verify(thingHandlerCallback).stateUpdated(eq(presetChannelUID), eq(new StringListType("27")));
+ }
+}
diff --git a/bundles/org.openhab.binding.bosesoundtouch/src/test/java/org/openhab/binding/bosesoundtouch/internal/handler/InMemmoryContentStorage.java b/bundles/org.openhab.binding.bosesoundtouch/src/test/java/org/openhab/binding/bosesoundtouch/internal/handler/InMemmoryContentStorage.java
new file mode 100644
index 000000000..2b7a3c042
--- /dev/null
+++ b/bundles/org.openhab.binding.bosesoundtouch/src/test/java/org/openhab/binding/bosesoundtouch/internal/handler/InMemmoryContentStorage.java
@@ -0,0 +1,64 @@
+/**
+ * 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.bosesoundtouch.internal.handler;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.bosesoundtouch.internal.ContentItem;
+import org.openhab.core.storage.Storage;
+
+/**
+ * @author Leo Siepel - Initial contribution
+ */
+@NonNullByDefault
+public class InMemmoryContentStorage implements Storage {
+ Map items = new TreeMap<>();
+
+ public InMemmoryContentStorage() {
+ }
+
+ @Override
+ public @Nullable ContentItem put(String key, @Nullable ContentItem value) {
+ return items.put(key, value);
+ }
+
+ @Override
+ public @Nullable ContentItem remove(String key) {
+ return items.remove(key);
+ }
+
+ @Override
+ public boolean containsKey(String key) {
+ return items.containsKey(key);
+ }
+
+ @Override
+ public @Nullable ContentItem get(String key) {
+ return items.get(key);
+ }
+
+ @Override
+ public Collection<@NonNull String> getKeys() {
+ return items.keySet();
+ }
+
+ @Override
+ public Collection<@Nullable ContentItem> getValues() {
+ return items.values();
+ }
+}