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.volumio.internal;
15 import java.net.InetAddress;
17 import java.net.URISyntaxException;
18 import java.net.UnknownHostException;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.json.JSONException;
22 import org.json.JSONObject;
23 import org.openhab.binding.volumio.internal.mapping.VolumioCommands;
24 import org.openhab.core.library.types.PercentType;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
28 import io.socket.client.IO;
29 import io.socket.client.Socket;
30 import io.socket.emitter.Emitter;
33 * @author Patrick Sernetz - Initial Contribution
34 * @author Chris Wohlbrecht - Adaption for openHAB 3
35 * @author Michael Loercher - Adaption for openHAB 3
38 public class VolumioService {
40 private final Logger logger = LoggerFactory.getLogger(VolumioService.class);
42 private final Socket socket;
44 private boolean connected;
46 public VolumioService(String protocol, String hostname, int port, int timeout)
47 throws URISyntaxException, UnknownHostException {
48 String uriString = String.format("%s://%s:%d", protocol, hostname, port);
50 URI destUri = new URI(uriString);
52 IO.Options opts = new IO.Options();
53 opts.reconnection = true;
54 opts.reconnectionDelay = 1000 * 30;
55 opts.reconnectionDelayMax = 1000 * 60;
56 opts.timeout = timeout;
58 // Connection to mdns endpoint is only available after fetching ip.
59 InetAddress ipaddress = InetAddress.getByName(hostname);
60 logger.debug("Resolving {} to IP {}", hostname, ipaddress.getHostAddress());
62 socket = IO.socket(destUri, opts);
64 bindDefaultEvents(hostname);
67 private void bindDefaultEvents(String hostname) {
68 socket.on(Socket.EVENT_CONNECTING, arg0 -> logger.debug("Trying to connect to Volumio on {}", hostname));
70 socket.on(Socket.EVENT_RECONNECTING, arg0 -> logger.debug("Trying to reconnect to Volumio on {}", hostname));
72 socket.on(Socket.EVENT_CONNECT_ERROR, arg0 -> logger.error("Could not connect to Volumio on {}", hostname));
74 socket.on(Socket.EVENT_CONNECT_TIMEOUT,
75 arg0 -> logger.error("Timedout while conntecting to Volumio on {}", hostname));
77 socket.on(Socket.EVENT_CONNECT, arg0 -> {
78 logger.info("Connected to Volumio on {}", hostname);
81 }).on(Socket.EVENT_DISCONNECT, arg0 -> {
82 logger.warn("Disconnected from Volumio on {}", hostname);
87 public void connect() throws InterruptedException {
91 public void disconnect() {
100 public void on(String eventName, Emitter.Listener listener) {
101 socket.on(eventName, listener);
104 public void once(String eventName, Emitter.Listener listener) {
105 socket.once(eventName, listener);
108 public void getState() {
109 socket.emit(VolumioCommands.GET_STATE);
113 socket.emit(VolumioCommands.PLAY);
116 public void pause() {
117 socket.emit(VolumioCommands.PAUSE);
121 socket.emit(VolumioCommands.STOP);
124 public void play(Integer index) {
125 socket.emit(VolumioCommands.PLAY, index);
129 socket.emit(VolumioCommands.NEXT);
132 public void previous() {
133 socket.emit(VolumioCommands.PREVIOUS);
136 public void setVolume(PercentType level) {
137 socket.emit(VolumioCommands.VOLUME, level.intValue());
140 public void shutdown() {
141 socket.emit(VolumioCommands.SHUTDOWN);
144 public void reboot() {
145 socket.emit(VolumioCommands.REBOOT);
148 public void playPlaylist(String playlistName) {
149 JSONObject item = new JSONObject();
152 item.put("name", playlistName);
154 socket.emit(VolumioCommands.PLAY_PLAYLIST, item);
155 } catch (JSONException e) {
156 logger.error("The following error occurred {}", e.getMessage());
160 public void clearQueue() {
161 socket.emit(VolumioCommands.CLEAR_QUEUE);
164 public void setRandom(boolean val) {
165 JSONObject item = new JSONObject();
168 item.put("value", val);
170 socket.emit(VolumioCommands.RANDOM, item);
171 } catch (JSONException e) {
172 logger.error("The following error occurred {}", e.getMessage());
176 public void setRepeat(boolean val) {
177 JSONObject item = new JSONObject();
180 item.put("value", val);
182 socket.emit(VolumioCommands.REPEAT, item);
183 } catch (JSONException e) {
184 logger.error("The following error occurred {}", e.getMessage());
188 public void playFavorites(String favoriteName) {
189 JSONObject item = new JSONObject();
192 item.put("name", favoriteName);
194 socket.emit(VolumioCommands.PLAY_FAVOURITES, item);
195 } catch (JSONException e) {
196 logger.error("The following error occurred {}", e.getMessage());
201 * Play a radio station from volumio´s Radio Favourites identifed by
204 public void playRadioFavourite(final Integer index) {
205 logger.debug("socket.emit({})", VolumioCommands.PLAY_RADIO_FAVOURITES);
207 socket.once("pushPlayRadioFavourites", arg -> play(index));
209 socket.emit(VolumioCommands.PLAY_RADIO_FAVOURITES);
212 public void playURI(String uri) {
213 JSONObject item = new JSONObject();
214 logger.debug("PlayURI: {}", uri);
216 item.put("uri", uri);
218 socket.emit(VolumioCommands.PLAY, uri);
219 } catch (JSONException e) {
220 logger.error("The following error occurred {}", e.getMessage());
224 public void addPlay(String uri, String title, String serviceType) {
225 JSONObject item = new JSONObject();
228 item.put("uri", uri);
229 item.put("title", title);
230 item.put("service", serviceType);
232 socket.emit(VolumioCommands.ADD_PLAY, item);
233 } catch (JSONException e) {
234 logger.error("The following error occurred {}", e.getMessage());
238 public void replacePlay(String uri, String title, String serviceType) {
239 JSONObject item = new JSONObject();
242 item.put("uri", uri);
243 item.put("title", title);
244 item.put("service", serviceType);
246 socket.emit(VolumioCommands.REPLACE_AND_PLAY, item);
247 } catch (JSONException e) {
248 logger.error("The following error occurred {}", e.getMessage());
252 public boolean isConnected() {
253 return this.connected;
256 public void setConnected(boolean status) {
257 this.connected = status;
260 public void sendSystemCommand(String string) {
261 logger.warn("Jukebox Command: {}", string);
263 case VolumioCommands.SHUTDOWN:
266 case VolumioCommands.REBOOT: