]> git.basschouten.com Git - openhab-addons.git/blob
9b8b998f41693039a6c054f7cf86c00a1031c186
[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.onkyo.internal.handler;
14
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.Locale;
18 import java.util.Map;
19 import java.util.Set;
20
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.binding.onkyo.internal.OnkyoBindingConstants;
23 import org.openhab.core.audio.AudioFormat;
24 import org.openhab.core.audio.AudioHTTPServer;
25 import org.openhab.core.audio.AudioSink;
26 import org.openhab.core.audio.AudioStream;
27 import org.openhab.core.audio.FixedLengthAudioStream;
28 import org.openhab.core.audio.URLAudioStream;
29 import org.openhab.core.audio.UnsupportedAudioFormatException;
30 import org.openhab.core.audio.UnsupportedAudioStreamException;
31 import org.openhab.core.io.transport.upnp.UpnpIOParticipant;
32 import org.openhab.core.io.transport.upnp.UpnpIOService;
33 import org.openhab.core.library.types.StringType;
34 import org.openhab.core.thing.Thing;
35 import org.openhab.core.thing.binding.BaseThingHandler;
36 import org.openhab.core.types.Command;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 /**
41  * * The {@link UpnpAudioSinkHandler} is a base class for ThingHandlers for devices which support UPnP playback. It
42  * implements the AudioSink interface.
43  * This will allow to register the derived ThingHandler to be registered as an AudioSink in the framework.
44  *
45  * @author Paul Frank - Initial contribution
46  */
47 public abstract class UpnpAudioSinkHandler extends BaseThingHandler implements AudioSink, UpnpIOParticipant {
48
49     private static final Set<AudioFormat> SUPPORTED_FORMATS = new HashSet<>();
50     private static final Set<Class<? extends AudioStream>> SUPPORTED_STREAMS = new HashSet<>();
51
52     static {
53         SUPPORTED_FORMATS.add(AudioFormat.WAV);
54         SUPPORTED_FORMATS.add(AudioFormat.MP3);
55
56         SUPPORTED_STREAMS.add(AudioStream.class);
57     }
58
59     private final Logger logger = LoggerFactory.getLogger(getClass());
60
61     private AudioHTTPServer audioHTTPServer;
62     private String callbackUrl;
63     private UpnpIOService service;
64
65     public UpnpAudioSinkHandler(Thing thing, UpnpIOService upnpIOService, AudioHTTPServer audioHTTPServer,
66             String callbackUrl) {
67         super(thing);
68         this.audioHTTPServer = audioHTTPServer;
69         this.callbackUrl = callbackUrl;
70         if (upnpIOService != null) {
71             this.service = upnpIOService;
72         }
73     }
74
75     protected void handlePlayUri(Command command) {
76         if (command != null && command instanceof StringType) {
77             try {
78                 playMedia(command.toString());
79
80             } catch (IllegalStateException e) {
81                 logger.warn("Cannot play URI ({})", e.getMessage());
82             }
83         }
84     }
85
86     private void playMedia(String url) {
87         stop();
88         removeAllTracksFromQueue();
89
90         if (!url.startsWith("x-") && (!url.startsWith("http"))) {
91             url = "x-file-cifs:" + url;
92         }
93
94         setCurrentURI(url, "");
95
96         play();
97     }
98
99     @Override
100     public Set<AudioFormat> getSupportedFormats() {
101         return SUPPORTED_FORMATS;
102     }
103
104     @Override
105     public Set<Class<? extends AudioStream>> getSupportedStreams() {
106         return SUPPORTED_STREAMS;
107     }
108
109     private void stop() {
110         Map<String, String> inputs = new HashMap<>();
111         inputs.put("InstanceID", "0");
112
113         Map<String, String> result = service.invokeAction(this, "AVTransport", "Stop", inputs);
114
115         for (String variable : result.keySet()) {
116             this.onValueReceived(variable, result.get(variable), "AVTransport");
117         }
118     }
119
120     private void play() {
121         Map<String, String> inputs = new HashMap<>();
122         inputs.put("InstanceID", "0");
123         inputs.put("Speed", "1");
124         Map<String, String> result = service.invokeAction(this, "AVTransport", "Play", inputs);
125
126         for (String variable : result.keySet()) {
127             this.onValueReceived(variable, result.get(variable), "AVTransport");
128         }
129     }
130
131     private void removeAllTracksFromQueue() {
132         Map<String, String> inputs = new HashMap<>();
133         inputs.put("InstanceID", "0");
134
135         Map<String, String> result = service.invokeAction(this, "AVTransport", "RemoveAllTracksFromQueue", inputs);
136
137         for (String variable : result.keySet()) {
138             this.onValueReceived(variable, result.get(variable), "AVTransport");
139         }
140     }
141
142     private void setCurrentURI(String uri, String uriMetaData) {
143         if (uri != null && uriMetaData != null) {
144             Map<String, String> inputs = new HashMap<>();
145
146             try {
147                 inputs.put("InstanceID", "0");
148                 inputs.put("CurrentURI", uri);
149                 inputs.put("CurrentURIMetaData", uriMetaData);
150             } catch (NumberFormatException ex) {
151                 logger.error("Action Invalid Value Format Exception {}", ex.getMessage());
152             }
153
154             Map<String, String> result = service.invokeAction(this, "AVTransport", "SetAVTransportURI", inputs);
155
156             for (String variable : result.keySet()) {
157                 this.onValueReceived(variable, result.get(variable), "AVTransport");
158             }
159         }
160     }
161
162     @Override
163     public String getId() {
164         return getThing().getUID().toString();
165     }
166
167     @Override
168     public String getLabel(Locale locale) {
169         return getThing().getLabel();
170     }
171
172     @Override
173     public void process(@Nullable AudioStream audioStream)
174             throws UnsupportedAudioFormatException, UnsupportedAudioStreamException {
175         if (audioStream == null) {
176             stop();
177             return;
178         }
179
180         String url = null;
181         if (audioStream instanceof URLAudioStream) {
182             // it is an external URL, the speaker can access it itself and play it.
183             URLAudioStream urlAudioStream = (URLAudioStream) audioStream;
184             url = urlAudioStream.getURL();
185         } else {
186             if (callbackUrl != null) {
187                 String relativeUrl;
188                 if (audioStream instanceof FixedLengthAudioStream) {
189                     // we serve it on our own HTTP server
190                     relativeUrl = audioHTTPServer.serve((FixedLengthAudioStream) audioStream, 20);
191                 } else {
192                     relativeUrl = audioHTTPServer.serve(audioStream);
193                 }
194                 url = callbackUrl + relativeUrl;
195             } else {
196                 logger.warn("We do not have any callback url, so onkyo cannot play the audio stream!");
197                 return;
198             }
199         }
200
201         playMedia(url);
202     }
203
204     @Override
205     public String getUDN() {
206         return (String) this.getConfig().get(OnkyoBindingConstants.UDN_PARAMETER);
207     }
208
209     @Override
210     public void onValueReceived(String variable, String value, String service) {
211         logger.debug("received variable {} with value {} from service {}", variable, value, service);
212     }
213
214     @Override
215     public void onServiceSubscribed(String service, boolean succeeded) {
216     }
217
218     @Override
219     public void onStatusChanged(boolean status) {
220     }
221 }