]> git.basschouten.com Git - openhab-addons.git/blob
cdf67aee96ac51be2e51de693cdede5b10e0d96b
[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.amplipi.internal.audio;
14
15 import java.io.IOException;
16 import java.util.Locale;
17 import java.util.Set;
18
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.amplipi.internal.AmpliPiHandler;
22 import org.openhab.core.audio.AudioFormat;
23 import org.openhab.core.audio.AudioSink;
24 import org.openhab.core.audio.AudioStream;
25 import org.openhab.core.audio.FixedLengthAudioStream;
26 import org.openhab.core.audio.URLAudioStream;
27 import org.openhab.core.audio.UnsupportedAudioFormatException;
28 import org.openhab.core.audio.UnsupportedAudioStreamException;
29 import org.openhab.core.library.types.PercentType;
30 import org.openhab.core.thing.binding.ThingHandler;
31 import org.openhab.core.thing.binding.ThingHandlerService;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 /**
36  * This is an audio sink that allows to do public announcements on the AmpliPi.
37  *
38  * @author Kai Kreuzer - Initial contribution
39  *
40  */
41 @NonNullByDefault
42 public class PAAudioSink implements AudioSink, ThingHandlerService {
43
44     private final Logger logger = LoggerFactory.getLogger(PAAudioSink.class);
45
46     private static final Set<AudioFormat> SUPPORTED_AUDIO_FORMATS = Set.of(AudioFormat.MP3, AudioFormat.WAV);
47     private static final Set<Class<? extends AudioStream>> SUPPORTED_AUDIO_STREAMS = Set
48             .of(FixedLengthAudioStream.class, URLAudioStream.class);
49
50     private @Nullable AmpliPiHandler handler;
51
52     private @Nullable PercentType volume;
53
54     @Override
55     public void process(@Nullable AudioStream audioStream)
56             throws UnsupportedAudioFormatException, UnsupportedAudioStreamException {
57         if (audioStream == null) {
58             // in case the audioStream is null, this should be interpreted as a request to end any currently playing
59             // stream.
60             logger.debug("Web Audio sink does not support stopping the currently playing stream.");
61             return;
62         }
63         AmpliPiHandler localHandler = this.handler;
64         if (localHandler != null) {
65             try (AudioStream stream = audioStream) {
66                 logger.debug("Received audio stream of format {}", audioStream.getFormat());
67                 String audioUrl;
68                 if (audioStream instanceof URLAudioStream) {
69                     // it is an external URL, so we can directly pass this on.
70                     URLAudioStream urlAudioStream = (URLAudioStream) audioStream;
71                     audioUrl = urlAudioStream.getURL();
72                 } else if (audioStream instanceof FixedLengthAudioStream) {
73                     String callbackUrl = localHandler.getCallbackUrl();
74                     if (callbackUrl == null) {
75                         throw new UnsupportedAudioStreamException(
76                                 "Cannot play audio since no callback url is available.", audioStream.getClass());
77                     } else {
78                         // we need to serve it for a while, hence only
79                         // FixedLengthAudioStreams are supported.
80                         String relativeUrl = localHandler.getAudioHTTPServer()
81                                 .serve((FixedLengthAudioStream) audioStream, 10).toString();
82                         audioUrl = callbackUrl + relativeUrl;
83                     }
84                 } else {
85                     throw new UnsupportedAudioStreamException(
86                             "Web audio sink can only handle FixedLengthAudioStreams and URLAudioStreams.",
87                             audioStream.getClass());
88                 }
89                 localHandler.playPA(audioUrl, volume);
90                 // we reset the volume value again, so that a next invocation without a volume will again use the zones
91                 // defaults.
92                 volume = null;
93             } catch (IOException e) {
94                 logger.debug("Error while closing the audio stream: {}", e.getMessage(), e);
95             }
96         }
97     }
98
99     @Override
100     public Set<AudioFormat> getSupportedFormats() {
101         return SUPPORTED_AUDIO_FORMATS;
102     }
103
104     @Override
105     public Set<Class<? extends AudioStream>> getSupportedStreams() {
106         return SUPPORTED_AUDIO_STREAMS;
107     }
108
109     @Override
110     public String getId() {
111         if (handler != null) {
112             return handler.getThing().getUID().toString();
113         } else {
114             throw new IllegalStateException();
115         }
116     }
117
118     @Override
119     public @Nullable String getLabel(@Nullable Locale locale) {
120         if (handler != null) {
121             return handler.getThing().getLabel();
122         } else {
123             return null;
124         }
125     }
126
127     @Override
128     public PercentType getVolume() throws IOException {
129         PercentType vol = volume;
130         if (vol != null) {
131             return vol;
132         } else {
133             throw new IOException("Audio sink does not support reporting the volume.");
134         }
135     }
136
137     @Override
138     public void setVolume(final PercentType volume) throws IOException {
139         this.volume = volume;
140     }
141
142     @Override
143     public void setThingHandler(ThingHandler handler) {
144         this.handler = (AmpliPiHandler) handler;
145     }
146
147     @Override
148     public @Nullable ThingHandler getThingHandler() {
149         return handler;
150     }
151 }