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.heos.internal.api;
15 import java.io.IOException;
16 import java.util.HashSet;
17 import java.util.Locale;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.binding.heos.internal.handler.HeosThingBaseHandler;
23 import org.openhab.binding.heos.internal.resources.Telnet.ReadException;
24 import org.openhab.core.audio.AudioFormat;
25 import org.openhab.core.audio.AudioHTTPServer;
26 import org.openhab.core.audio.AudioSink;
27 import org.openhab.core.audio.AudioStream;
28 import org.openhab.core.audio.FileAudioStream;
29 import org.openhab.core.audio.FixedLengthAudioStream;
30 import org.openhab.core.audio.URLAudioStream;
31 import org.openhab.core.audio.UnsupportedAudioFormatException;
32 import org.openhab.core.audio.utils.AudioStreamUtils;
33 import org.openhab.core.library.types.PercentType;
34 import org.openhab.core.thing.util.ThingHandlerHelper;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
39 * This makes HEOS to serve as an {@link AudioSink}.
41 * @author Johannes Einig - Initial contribution
44 public class HeosAudioSink implements AudioSink {
45 private final Logger logger = LoggerFactory.getLogger(HeosAudioSink.class);
47 private static final Set<AudioFormat> SUPPORTED_AUDIO_FORMATS = new HashSet<>();
48 private static final Set<Class<? extends AudioStream>> SUPPORTED_AUDIO_STREAMS = new HashSet<>();
51 SUPPORTED_AUDIO_FORMATS.add(AudioFormat.WAV);
52 SUPPORTED_AUDIO_FORMATS.add(AudioFormat.MP3);
53 SUPPORTED_AUDIO_FORMATS.add(AudioFormat.AAC);
55 SUPPORTED_AUDIO_STREAMS.add(URLAudioStream.class);
56 SUPPORTED_AUDIO_STREAMS.add(FixedLengthAudioStream.class);
59 private final HeosThingBaseHandler handler;
60 private final AudioHTTPServer audioHTTPServer;
61 private @Nullable final String callbackUrl;
63 public HeosAudioSink(HeosThingBaseHandler handler, AudioHTTPServer audioHTTPServer, @Nullable String callbackUrl) {
64 this.handler = handler;
65 this.audioHTTPServer = audioHTTPServer;
66 this.callbackUrl = callbackUrl;
70 public String getId() {
71 return handler.getThing().getUID().toString();
75 public @Nullable String getLabel(@Nullable Locale locale) {
76 return handler.getThing().getLabel();
80 public void process(@Nullable AudioStream audioStream) throws UnsupportedAudioFormatException {
82 if (audioStream instanceof URLAudioStream) {
83 // it is an external URL, the speaker can access it itself and play it.
84 URLAudioStream urlAudioStream = (URLAudioStream) audioStream;
85 handler.playURL(urlAudioStream.getURL());
86 } else if (audioStream instanceof FixedLengthAudioStream) {
87 if (callbackUrl != null) {
88 // we serve it on our own HTTP server for 30 seconds as HEOS requests the stream several times
89 String relativeUrl = audioHTTPServer.serve((FixedLengthAudioStream) audioStream, 30);
90 String url = callbackUrl + relativeUrl + AudioStreamUtils.EXTENSION_SEPARATOR;
91 AudioFormat audioFormat = audioStream.getFormat();
92 if (!ThingHandlerHelper.isHandlerInitialized(handler)) {
93 logger.debug("HEOS speaker '{}' is not initialized - status is {}", handler.getThing().getUID(),
94 handler.getThing().getStatus());
95 } else if (AudioFormat.MP3.isCompatible(audioFormat)) {
96 handler.playURL(url + FileAudioStream.MP3_EXTENSION);
97 } else if (AudioFormat.WAV.isCompatible(audioFormat)) {
98 handler.playURL(url + FileAudioStream.WAV_EXTENSION);
99 } else if (AudioFormat.AAC.isCompatible(audioFormat)) {
100 handler.playURL(url + FileAudioStream.AAC_EXTENSION);
102 throw new UnsupportedAudioFormatException("HEOS only supports MP3, WAV and AAC.", audioFormat);
105 logger.warn("We do not have any callback url, so HEOS cannot play the audio stream!");
108 throw new UnsupportedAudioFormatException(
109 "HEOS can only handle FixedLengthAudioStreams & URLAudioStream.", null);
111 } catch (IOException | ReadException e) {
112 logger.warn("Failed to play audio stream: {}", e.getMessage());
117 public Set<AudioFormat> getSupportedFormats() {
118 return SUPPORTED_AUDIO_FORMATS;
122 public Set<Class<? extends AudioStream>> getSupportedStreams() {
123 return SUPPORTED_AUDIO_STREAMS;
127 public PercentType getVolume() {
128 return handler.getNotificationSoundVolume();
132 public void setVolume(PercentType volume) {
133 handler.setNotificationSoundVolume(volume);