/**
* Copyright (c) 2014-2017 by the respective copyright holders.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.smarthome.binding.sonos.internal;
import java.io.IOException;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import org.eclipse.smarthome.binding.sonos.handler.ZonePlayerHandler;
import org.eclipse.smarthome.core.audio.AudioFormat;
import org.eclipse.smarthome.core.audio.AudioHTTPServer;
import org.eclipse.smarthome.core.audio.AudioSink;
import org.eclipse.smarthome.core.audio.AudioStream;
import org.eclipse.smarthome.core.audio.FixedLengthAudioStream;
import org.eclipse.smarthome.core.audio.URLAudioStream;
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.thing.util.ThingHandlerHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This makes a Sonos speaker to serve as an {@link AudioSink}-
*
* @author Kai Kreuzer - Initial contribution and API
*
*/
public class SonosAudioSink implements AudioSink {
private final Logger logger = LoggerFactory.getLogger(SonosAudioSink.class);
private static HashSet<AudioFormat> supportedFormats = new HashSet<>();
static {
supportedFormats.add(AudioFormat.WAV);
supportedFormats.add(AudioFormat.MP3);
}
private AudioHTTPServer audioHTTPServer;
private ZonePlayerHandler handler;
private String callbackUrl;
public SonosAudioSink(ZonePlayerHandler handler, AudioHTTPServer audioHTTPServer, String callbackUrl) {
this.handler = handler;
this.audioHTTPServer = audioHTTPServer;
this.callbackUrl = callbackUrl;
}
@Override
public String getId() {
return handler.getThing().getUID().toString();
}
@Override
public String getLabel(Locale locale) {
return handler.getThing().getLabel();
}
@Override
public void process(AudioStream audioStream) throws UnsupportedAudioFormatException {
if (audioStream instanceof URLAudioStream) {
// it is an external URL, the speaker can access it itself and play it.
URLAudioStream urlAudioStream = (URLAudioStream) audioStream;
handler.playURI(new StringType(urlAudioStream.getURL()));
try {
audioStream.close();
} catch (IOException e) {
}
} else {
// we serve it on our own HTTP server and treat it as a notification
if (!(audioStream instanceof FixedLengthAudioStream)) {
// Note that we have to pass a FixedLengthAudioStream, since Sonos does multiple concurrent requests to
// the AudioServlet, so a one time serving won't work.
throw new UnsupportedAudioFormatException("Sonos can only handle FixedLengthAudioStreams.", null);
// TODO: Instead of throwing an exception, we could ourselves try to wrap it into a
// FixedLengthAudioStream, but this might be dangerous as we have no clue, how much data to expect from
// the stream.
} else {
if (callbackUrl != null) {
String relativeUrl = audioHTTPServer.serve((FixedLengthAudioStream) audioStream, 10).toString();
String url = callbackUrl + relativeUrl;
AudioFormat format = audioStream.getFormat();
if (!ThingHandlerHelper.isHandlerInitialized(handler)) {
logger.warn("Sonos speaker '{}' is not initialized - status is {}", handler.getThing().getUID(),
handler.getThing().getStatus());
} else if (AudioFormat.WAV.isCompatible(format)) {
handler.playNotificationSoundURI(new StringType(url + ".wav"));
} else if (AudioFormat.MP3.isCompatible(format)) {
handler.playNotificationSoundURI(new StringType(url + ".mp3"));
} else {
throw new UnsupportedAudioFormatException("Sonos only supports MP3 or WAV.", format);
}
} else {
logger.warn("We do not have any callback url, so Sonos cannot play the audio stream!");
}
}
}
}
@Override
public Set<AudioFormat> getSupportedFormats() {
return supportedFormats;
}
@Override
public PercentType getVolume() {
return handler.getNotificationSoundVolume();
}
@Override
public void setVolume(PercentType volume) {
handler.setNotificationSoundVolume(volume);
}
}