]> git.basschouten.com Git - openhab-addons.git/blob
e518de92c1807b69ee43268c78018d93cd8539be
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.heos.internal.api;
14
15 import java.io.IOException;
16 import java.util.HashSet;
17 import java.util.Locale;
18 import java.util.Set;
19
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;
37
38 /**
39  * This makes HEOS to serve as an {@link AudioSink}.
40  *
41  * @author Johannes Einig - Initial contribution
42  */
43 @NonNullByDefault
44 public class HeosAudioSink implements AudioSink {
45     private final Logger logger = LoggerFactory.getLogger(HeosAudioSink.class);
46
47     private static final Set<AudioFormat> SUPPORTED_AUDIO_FORMATS = new HashSet<>();
48     private static final Set<Class<? extends AudioStream>> SUPPORTED_AUDIO_STREAMS = new HashSet<>();
49
50     static {
51         SUPPORTED_AUDIO_FORMATS.add(AudioFormat.WAV);
52         SUPPORTED_AUDIO_FORMATS.add(AudioFormat.MP3);
53         SUPPORTED_AUDIO_FORMATS.add(AudioFormat.AAC);
54
55         SUPPORTED_AUDIO_STREAMS.add(URLAudioStream.class);
56         SUPPORTED_AUDIO_STREAMS.add(FixedLengthAudioStream.class);
57     }
58
59     private final HeosThingBaseHandler handler;
60     private final AudioHTTPServer audioHTTPServer;
61     private @Nullable final String callbackUrl;
62
63     public HeosAudioSink(HeosThingBaseHandler handler, AudioHTTPServer audioHTTPServer, @Nullable String callbackUrl) {
64         this.handler = handler;
65         this.audioHTTPServer = audioHTTPServer;
66         this.callbackUrl = callbackUrl;
67     }
68
69     @Override
70     public String getId() {
71         return handler.getThing().getUID().toString();
72     }
73
74     @Override
75     public @Nullable String getLabel(@Nullable Locale locale) {
76         return handler.getThing().getLabel();
77     }
78
79     @Override
80     public void process(@Nullable AudioStream audioStream) throws UnsupportedAudioFormatException {
81         try {
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);
101                     } else {
102                         throw new UnsupportedAudioFormatException("HEOS only supports MP3, WAV and AAC.", audioFormat);
103                     }
104                 } else {
105                     logger.warn("We do not have any callback url, so HEOS cannot play the audio stream!");
106                 }
107             } else {
108                 throw new UnsupportedAudioFormatException(
109                         "HEOS can only handle FixedLengthAudioStreams & URLAudioStream.", null);
110             }
111         } catch (IOException | ReadException e) {
112             logger.warn("Failed to play audio stream: {}", e.getMessage());
113         }
114     }
115
116     @Override
117     public Set<AudioFormat> getSupportedFormats() {
118         return SUPPORTED_AUDIO_FORMATS;
119     }
120
121     @Override
122     public Set<Class<? extends AudioStream>> getSupportedStreams() {
123         return SUPPORTED_AUDIO_STREAMS;
124     }
125
126     @Override
127     public PercentType getVolume() {
128         return handler.getNotificationSoundVolume();
129     }
130
131     @Override
132     public void setVolume(PercentType volume) {
133         handler.setNotificationSoundVolume(volume);
134     }
135 }