diff --git a/bundles/core/org.eclipse.smarthome.core.audio/OSGI-INF/AudioManager.xml b/bundles/core/org.eclipse.smarthome.core.audio/OSGI-INF/AudioManager.xml
index 0ff29a9c375..006068cfa65 100644
--- a/bundles/core/org.eclipse.smarthome.core.audio/OSGI-INF/AudioManager.xml
+++ b/bundles/core/org.eclipse.smarthome.core.audio/OSGI-INF/AudioManager.xml
@@ -16,7 +16,7 @@
-
+
diff --git a/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/AudioHTTPServer.java b/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/AudioHTTPServer.java
index 249ef0793a7..57ce45f8fec 100644
--- a/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/AudioHTTPServer.java
+++ b/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/AudioHTTPServer.java
@@ -7,8 +7,6 @@
*/
package org.eclipse.smarthome.core.audio;
-import java.net.URL;
-
import org.eclipse.smarthome.core.audio.internal.AudioServlet;
/**
@@ -22,28 +20,28 @@
public interface AudioHTTPServer {
/**
- * Creates a url for a given {@link AudioStream} where it can be requested a single time.
+ * Creates a relative url for a given {@link AudioStream} where it can be requested a single time.
* Note that the HTTP header only contains "Content-length", if the passed stream is an instance of
* {@link FixedLengthAudioStream}.
* If the client that requests the url expects this header field to be present, make sure to pass such an instance.
* Streams are closed after having been served.
*
* @param stream the stream to serve on HTTP
- * @return the absolute URL to access the stream (using the primary network interface)
+ * @return the relative URL to access the stream starting with a '/'
*/
- URL serve(AudioStream stream);
+ String serve(AudioStream stream);
/**
- * Creates a url for a given {@link AudioStream} where it can be requested multiple times within the given time
- * frame.
+ * Creates a relative url for a given {@link AudioStream} where it can be requested multiple times within the given
+ * time frame.
* This method only accepts {@link FixedLengthAudioStream}s, since it needs to be able to create multiple concurrent
* streams from it, which isn't possible with a regular {@link AudioStream}.
* Streams are closed, once they expire.
*
* @param stream the stream to serve on HTTP
* @param seconds number of seconds for which the stream is available through HTTP
- * @return the absolute URL to access the stream (using the primary network interface)
+ * @return the relative URL to access the stream starting with a '/'
*/
- URL serve(FixedLengthAudioStream stream, int seconds);
+ String serve(FixedLengthAudioStream stream, int seconds);
}
diff --git a/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/internal/AudioServlet.java b/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/internal/AudioServlet.java
index 5116f1ae056..e4e9a9046cb 100644
--- a/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/internal/AudioServlet.java
+++ b/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/internal/AudioServlet.java
@@ -9,8 +9,6 @@
import java.io.IOException;
import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
import java.util.Hashtable;
import java.util.Map;
import java.util.UUID;
@@ -29,9 +27,6 @@
import org.eclipse.smarthome.core.audio.AudioHTTPServer;
import org.eclipse.smarthome.core.audio.AudioStream;
import org.eclipse.smarthome.core.audio.FixedLengthAudioStream;
-import org.eclipse.smarthome.core.net.HttpServiceUtil;
-import org.eclipse.smarthome.core.net.NetUtil;
-import org.osgi.framework.BundleContext;
import org.osgi.service.http.HttpContext;
import org.osgi.service.http.HttpService;
import org.osgi.service.http.NamespaceException;
@@ -57,15 +52,6 @@ public class AudioServlet extends HttpServlet implements AudioHTTPServer {
private Map streamTimeouts = new ConcurrentHashMap<>();
protected HttpService httpService;
- private BundleContext bundleContext;
-
- protected void activate(BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- }
-
- protected void deactivate(BundleContext bundleContext) {
- this.bundleContext = null;
- }
protected void setHttpService(HttpService httpService) {
this.httpService = httpService;
@@ -179,40 +165,22 @@ private synchronized void removeTimedOutStreams() {
}
@Override
- public URL serve(AudioStream stream) {
+ public String serve(AudioStream stream) {
String streamId = UUID.randomUUID().toString();
oneTimeStreams.put(streamId, stream);
- return getURL(streamId);
+ return getRelativeURL(streamId);
}
@Override
- public URL serve(FixedLengthAudioStream stream, int seconds) {
+ public String serve(FixedLengthAudioStream stream, int seconds) {
String streamId = UUID.randomUUID().toString();
multiTimeStreams.put(streamId, stream);
streamTimeouts.put(streamId, System.nanoTime() + TimeUnit.SECONDS.toNanos(seconds));
- return getURL(streamId);
+ return getRelativeURL(streamId);
}
- private URL getURL(String streamId) {
- try {
- final String ipAddress = NetUtil.getLocalIpv4HostAddress();
- if (ipAddress == null) {
- logger.warn("No network interface could be found.");
- return null;
- }
-
- // we do not use SSL as it can cause certificate validation issues.
- final int port = HttpServiceUtil.getHttpServicePort(bundleContext);
- if (port == -1) {
- logger.warn("Cannot find port of the http service.");
- return null;
- }
-
- return new URL("http://" + ipAddress + ":" + port + SERVLET_NAME + "/" + streamId);
- } catch (final MalformedURLException e) {
- logger.error("Failed to construct audio stream URL: {}", e.getMessage(), e);
- return null;
- }
+ private String getRelativeURL(String streamId) {
+ return SERVLET_NAME + "/" + streamId;
}
}
\ No newline at end of file
diff --git a/bundles/core/org.eclipse.smarthome.core.voice/OSGI-INF/VoiceManager.xml b/bundles/core/org.eclipse.smarthome.core.voice/OSGI-INF/VoiceManager.xml
index 1983752bdb5..5650006e703 100644
--- a/bundles/core/org.eclipse.smarthome.core.voice/OSGI-INF/VoiceManager.xml
+++ b/bundles/core/org.eclipse.smarthome.core.voice/OSGI-INF/VoiceManager.xml
@@ -20,7 +20,7 @@
-
+
diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/binding/binding.xml b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/binding/binding.xml
index fada3b4ca16..4ef1774e302 100644
--- a/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/binding/binding.xml
+++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/ESH-INF/binding/binding.xml
@@ -13,7 +13,11 @@
URL for the OPML/tunein.com service
false
-
+
+
+ url to use for playing notification sounds, e.g. http://192.168.0.2:8080
+ false
+
diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.sonos/META-INF/MANIFEST.MF
index b8f66c912b4..0b2b0d0e8e0 100644
--- a/extensions/binding/org.eclipse.smarthome.binding.sonos/META-INF/MANIFEST.MF
+++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/META-INF/MANIFEST.MF
@@ -15,6 +15,7 @@ Import-Package: com.google.common.collect,
org.eclipse.smarthome.core.audio,
org.eclipse.smarthome.core.common.registry,
org.eclipse.smarthome.core.library.types,
+ org.eclipse.smarthome.core.net,
org.eclipse.smarthome.core.thing,
org.eclipse.smarthome.core.thing.binding,
org.eclipse.smarthome.core.types,
diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/OSGI-INF/SonosHandlerFactory.xml b/extensions/binding/org.eclipse.smarthome.binding.sonos/OSGI-INF/SonosHandlerFactory.xml
index 51c7092b9b4..efd0f407481 100644
--- a/extensions/binding/org.eclipse.smarthome.binding.sonos/OSGI-INF/SonosHandlerFactory.xml
+++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/OSGI-INF/SonosHandlerFactory.xml
@@ -8,7 +8,7 @@
http://www.eclipse.org/legal/epl-v10.html
-->
-
+
diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/handler/ZonePlayerHandler.java b/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/handler/ZonePlayerHandler.java
index 1ade866e233..37204dc5a85 100644
--- a/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/handler/ZonePlayerHandler.java
+++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/handler/ZonePlayerHandler.java
@@ -2230,7 +2230,9 @@ private void handleNotificationSound(Command notificationURL, ZonePlayerHandler
coordinator.setPositionTrack(notificationPosition);
coordinator.play();
waitForFinishedNotification();
- setVolumeForGroup(DecimalType.valueOf(originalVolume));
+ if (originalVolume != null) {
+ setVolumeForGroup(DecimalType.valueOf(originalVolume));
+ }
coordinator.removeRangeOfTracksFromQueue(new StringType(Integer.toString(notificationPosition) + ",1"));
}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/internal/SonosAudioSink.java b/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/internal/SonosAudioSink.java
index 6057a27d929..f4715073181 100644
--- a/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/internal/SonosAudioSink.java
+++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/internal/SonosAudioSink.java
@@ -22,6 +22,12 @@
import org.eclipse.smarthome.core.audio.UnsupportedAudioFormatException;
import org.eclipse.smarthome.core.library.types.PercentType;
import org.eclipse.smarthome.core.library.types.StringType;
+import org.eclipse.smarthome.core.net.HttpServiceUtil;
+import org.eclipse.smarthome.core.net.NetUtil;
+import org.eclipse.smarthome.core.thing.ThingStatus;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* This makes a Sonos speaker to serve as an {@link AudioSink}-
@@ -31,6 +37,8 @@
*/
public class SonosAudioSink implements AudioSink {
+ private final Logger logger = LoggerFactory.getLogger(SonosAudioSink.class);
+
private static HashSet supportedFormats = new HashSet<>();
static {
@@ -40,10 +48,15 @@ public class SonosAudioSink implements AudioSink {
private AudioHTTPServer audioHTTPServer;
private ZonePlayerHandler handler;
+ private BundleContext context;
+ private String callbackUrl;
- public SonosAudioSink(ZonePlayerHandler handler, AudioHTTPServer audioHTTPServer) {
+ public SonosAudioSink(BundleContext context, ZonePlayerHandler handler, AudioHTTPServer audioHTTPServer,
+ String callbackUrl) {
+ this.context = context;
this.handler = handler;
this.audioHTTPServer = audioHTTPServer;
+ this.callbackUrl = callbackUrl;
}
@Override
@@ -76,12 +89,19 @@ public void process(AudioStream audioStream) throws UnsupportedAudioFormatExcept
// FixedLengthAudioStream, but this might be dangerous as we have no clue, how much data to expect from
// the stream.
} else {
- String url = audioHTTPServer.serve((FixedLengthAudioStream) audioStream, 10).toString();
+ String relativeUrl = audioHTTPServer.serve((FixedLengthAudioStream) audioStream, 10).toString();
+ String url = createAbsoluteUrl(relativeUrl);
+
AudioFormat format = audioStream.getFormat();
if (AudioFormat.WAV.isCompatible(format)) {
handler.playNotificationSoundURI(new StringType(url + ".wav"));
} else if (AudioFormat.MP3.isCompatible(format)) {
- handler.playNotificationSoundURI(new StringType(url + ".mp3"));
+ if (handler.getThing().getStatus() == ThingStatus.ONLINE) {
+ handler.playNotificationSoundURI(new StringType(url + ".mp3"));
+ } else {
+ logger.warn("Sonos speaker '{}' is not online - status is {}", handler.getThing().getUID(),
+ handler.getThing().getStatus());
+ }
} else {
throw new UnsupportedAudioFormatException("Sonos only supports MP3 or WAV.", format);
}
@@ -89,6 +109,27 @@ public void process(AudioStream audioStream) throws UnsupportedAudioFormatExcept
}
}
+ private String createAbsoluteUrl(String relativeUrl) {
+ if (callbackUrl != null) {
+ return callbackUrl + relativeUrl;
+ } else {
+ final String ipAddress = NetUtil.getLocalIpv4HostAddress();
+ if (ipAddress == null) {
+ logger.warn("No network interface could be found.");
+ return null;
+ }
+
+ // we do not use SSL as it can cause certificate validation issues.
+ final int port = HttpServiceUtil.getHttpServicePort(context);
+ if (port == -1) {
+ logger.warn("Cannot find port of the http service.");
+ return null;
+ }
+
+ return "http://" + ipAddress + ":" + port + relativeUrl;
+ }
+ }
+
@Override
public Set getSupportedFormats() {
return supportedFormats;
diff --git a/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/internal/SonosHandlerFactory.java b/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/internal/SonosHandlerFactory.java
index ad27cd63ea1..3b6252943a8 100644
--- a/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/internal/SonosHandlerFactory.java
+++ b/extensions/binding/org.eclipse.smarthome.binding.sonos/src/main/java/org/eclipse/smarthome/binding/sonos/internal/SonosHandlerFactory.java
@@ -26,6 +26,7 @@
import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory;
import org.eclipse.smarthome.core.thing.binding.ThingHandler;
import org.eclipse.smarthome.io.transport.upnp.UpnpIOService;
+import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
@@ -44,17 +45,23 @@ public class SonosHandlerFactory extends BaseThingHandlerFactory {
private UpnpIOService upnpIOService;
private DiscoveryServiceRegistry discoveryServiceRegistry;
private AudioHTTPServer audioHTTPServer;
+ private BundleContext context;
private Map> audioSinkRegistrations = new ConcurrentHashMap<>();
// optional OPML URL that can be configured through configuration admin
private String opmlUrl = null;
+ // url (scheme+server+port) to use for playing notification sounds
+ private String callbackUrl = null;
+
@Override
protected void activate(ComponentContext componentContext) {
super.activate(componentContext);
+ this.context = componentContext.getBundleContext();
Dictionary properties = componentContext.getProperties();
opmlUrl = (String) properties.get("opmlUrl");
+ callbackUrl = (String) properties.get("callbackUrl");
};
@Override
@@ -88,7 +95,7 @@ protected ThingHandler createHandler(Thing thing) {
ZonePlayerHandler handler = new ZonePlayerHandler(thing, upnpIOService, discoveryServiceRegistry, opmlUrl);
// register the speaker as an audio sink
- SonosAudioSink audioSink = new SonosAudioSink(handler, audioHTTPServer);
+ SonosAudioSink audioSink = new SonosAudioSink(context, handler, audioHTTPServer, callbackUrl);
@SuppressWarnings("unchecked")
ServiceRegistration reg = (ServiceRegistration) bundleContext
.registerService(AudioSink.class.getName(), audioSink, new Hashtable());