2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.squeezebox.internal;
15 import java.io.IOException;
16 import java.io.InputStream;
17 import java.util.Locale;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.squeezebox.internal.handler.SqueezeBoxPlayerHandler;
22 import org.openhab.core.audio.AudioFormat;
23 import org.openhab.core.audio.AudioHTTPServer;
24 import org.openhab.core.audio.AudioSink;
25 import org.openhab.core.audio.AudioSinkSync;
26 import org.openhab.core.audio.AudioStream;
27 import org.openhab.core.audio.FileAudioStream;
28 import org.openhab.core.audio.StreamServed;
29 import org.openhab.core.audio.URLAudioStream;
30 import org.openhab.core.audio.UnsupportedAudioFormatException;
31 import org.openhab.core.audio.UnsupportedAudioStreamException;
32 import org.openhab.core.audio.utils.AudioStreamUtils;
33 import org.openhab.core.library.types.PercentType;
34 import org.openhab.core.library.types.StringType;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
39 * This makes a SqueezeBox Player serve as an {@link AudioSink}-
41 * @author Mark Hilbush - Initial contribution
42 * @author Mark Hilbush - Add callbackUrl
44 public class SqueezeBoxAudioSink extends AudioSinkSync {
45 private final Logger logger = LoggerFactory.getLogger(SqueezeBoxAudioSink.class);
47 private static final Set<AudioFormat> SUPPORTED_FORMATS = Set.of(AudioFormat.WAV, AudioFormat.MP3);
48 private static final Set<Class<? extends AudioStream>> SUPPORTED_STREAMS = Set.of(AudioStream.class);
50 // Needed because Squeezebox does multiple requests for the stream
51 private static final int STREAM_TIMEOUT = 10;
53 private String callbackUrl;
55 private AudioHTTPServer audioHTTPServer;
56 private SqueezeBoxPlayerHandler playerHandler;
58 public SqueezeBoxAudioSink(SqueezeBoxPlayerHandler playerHandler, AudioHTTPServer audioHTTPServer,
60 this.playerHandler = playerHandler;
61 this.audioHTTPServer = audioHTTPServer;
62 this.callbackUrl = callbackUrl;
63 if (callbackUrl != null && !callbackUrl.isEmpty()) {
64 logger.debug("SqueezeBox AudioSink created with callback URL: {}", callbackUrl);
69 public String getId() {
70 return playerHandler.getThing().getUID().toString();
74 public String getLabel(Locale locale) {
75 return playerHandler.getThing().getLabel();
79 public void processSynchronously(AudioStream audioStream)
80 throws UnsupportedAudioFormatException, UnsupportedAudioStreamException {
81 if (audioStream == null) {
84 AudioFormat format = audioStream.getFormat();
85 if (!AudioFormat.WAV.isCompatible(format) && !AudioFormat.MP3.isCompatible(format)) {
86 tryClose(audioStream);
87 throw new UnsupportedAudioFormatException("Currently only MP3 and WAV formats are supported: ", format);
91 if (audioStream instanceof URLAudioStream urlAudioStream) {
92 url = urlAudioStream.getURL();
93 tryClose(audioStream);
96 // Since Squeezebox will make multiple requests for the stream, set multiple to true
97 StreamServed streamServed = audioHTTPServer.serve(audioStream, STREAM_TIMEOUT, true);
98 url = streamServed.url();
100 if (AudioFormat.WAV.isCompatible(format)) {
101 url += AudioStreamUtils.EXTENSION_SEPARATOR + FileAudioStream.WAV_EXTENSION;
102 } else if (AudioFormat.MP3.isCompatible(format)) {
103 url += AudioStreamUtils.EXTENSION_SEPARATOR + FileAudioStream.MP3_EXTENSION;
106 // Form the URL for streaming the notification from the OH web server
107 // Use the callback URL if it is set in the binding configuration
108 String host = callbackUrl == null || callbackUrl.isEmpty() ? playerHandler.getHostAndPort()
111 logger.warn("Unable to get host/port from which to stream notification");
112 tryClose(audioStream);
116 } catch (IOException e) {
117 tryClose(audioStream);
118 throw new UnsupportedAudioStreamException(
119 "Squeezebox binding was not able to handle the audio stream (cache on disk failed)",
120 audioStream.getClass(), e);
124 logger.debug("Processing audioStream {} of format {}", url, format);
125 playerHandler.playNotificationSoundURI(new StringType(url));
128 private void tryClose(@Nullable InputStream is) {
132 } catch (IOException ignored) {
138 public Set<AudioFormat> getSupportedFormats() {
139 return SUPPORTED_FORMATS;
143 public Set<Class<? extends AudioStream>> getSupportedStreams() {
144 return SUPPORTED_STREAMS;
148 public PercentType getVolume() {
149 return playerHandler.getNotificationSoundVolume();
153 public void setVolume(PercentType volume) {
154 playerHandler.setNotificationSoundVolume(volume);