]> git.basschouten.com Git - openhab-addons.git/blob
d65930e2ca380bfbc6e934ee77d9156a4135ebfb
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.pulseaudio.internal;
14
15 import java.io.IOException;
16 import java.net.Socket;
17 import java.util.Locale;
18 import java.util.concurrent.ScheduledExecutorService;
19 import java.util.concurrent.ScheduledFuture;
20 import java.util.concurrent.TimeUnit;
21 import java.util.concurrent.locks.ReentrantLock;
22
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.binding.pulseaudio.internal.handler.PulseaudioHandler;
26 import org.openhab.core.library.types.PercentType;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 /**
31  * A connection to a pulseaudio Simple TCP Protocol
32  *
33  * @author Gwendal Roulleau - Initial contribution
34  * @author Miguel Álvarez - Refactor some code from PulseAudioAudioSink here
35  *
36  */
37 @NonNullByDefault
38 public abstract class PulseaudioSimpleProtocolStream {
39
40     private final Logger logger = LoggerFactory.getLogger(PulseaudioSimpleProtocolStream.class);
41
42     protected PulseaudioHandler pulseaudioHandler;
43     protected ScheduledExecutorService scheduler;
44
45     protected @Nullable Socket clientSocket;
46
47     private ReentrantLock countClientLock = new ReentrantLock();
48     private Integer countClient = 0;
49
50     private @Nullable ScheduledFuture<?> scheduledDisconnection;
51
52     public PulseaudioSimpleProtocolStream(PulseaudioHandler pulseaudioHandler, ScheduledExecutorService scheduler) {
53         this.pulseaudioHandler = pulseaudioHandler;
54         this.scheduler = scheduler;
55     }
56
57     /**
58      * Connect to pulseaudio with the simple protocol
59      * Will schedule an attempt for disconnection after timeout
60      *
61      * @throws IOException
62      * @throws InterruptedException when interrupted during the loading module wait
63      */
64     public void connectIfNeeded() throws IOException, InterruptedException {
65         Socket clientSocketLocal = clientSocket;
66         if (clientSocketLocal == null || !clientSocketLocal.isConnected() || clientSocketLocal.isClosed()) {
67             logger.debug("Simple TCP Stream connecting for {}", getLabel(null));
68             String host = pulseaudioHandler.getHost();
69             int port = pulseaudioHandler.getSimpleTcpPortAndLoadModuleIfNecessary();
70             var clientSocketFinal = new Socket(host, port);
71             clientSocketFinal.setSoTimeout(pulseaudioHandler.getBasicProtocolSOTimeout());
72             clientSocket = clientSocketFinal;
73             scheduleDisconnectIfNoClient();
74         }
75     }
76
77     /**
78      * Disconnect the socket to pulseaudio simple protocol
79      */
80     public void disconnect() {
81         final Socket clientSocketLocal = clientSocket;
82         if (clientSocketLocal != null) {
83             logger.debug("Simple TCP Stream disconnecting for {}", getLabel(null));
84             try {
85                 clientSocketLocal.close();
86             } catch (IOException ignored) {
87             }
88         } else {
89             logger.debug("Stream still running or socket not open");
90         }
91     }
92
93     private void scheduleDisconnectIfNoClient() {
94         countClientLock.lock();
95         try {
96             if (countClient <= 0) {
97                 var scheduledDisconnectionFinal = scheduledDisconnection;
98                 if (scheduledDisconnectionFinal != null) {
99                     logger.debug("Aborting next disconnect");
100                     scheduledDisconnectionFinal.cancel(true);
101                 }
102                 int idleTimeout = pulseaudioHandler.getIdleTimeout();
103                 if (idleTimeout > -1) {
104                     if (idleTimeout == 0) {
105                         this.disconnect();
106                     } else {
107                         logger.debug("Scheduling next disconnect");
108                         scheduledDisconnection = scheduler.schedule(this::disconnect, idleTimeout,
109                                 TimeUnit.MILLISECONDS);
110                     }
111                 }
112             }
113         } finally {
114             countClientLock.unlock();
115         }
116     }
117
118     public PercentType getVolume() {
119         return new PercentType(pulseaudioHandler.getLastVolume());
120     }
121
122     public void setVolume(PercentType volume) {
123         pulseaudioHandler.setVolume(volume.intValue());
124     }
125
126     public String getId() {
127         return pulseaudioHandler.getThing().getUID().toString();
128     }
129
130     public String getLabel(@Nullable Locale locale) {
131         var label = pulseaudioHandler.getThing().getLabel();
132         return label != null ? label : pulseaudioHandler.getThing().getUID().getId();
133     }
134
135     protected void addClientCount() {
136         countClientLock.lock();
137         try {
138             countClient += 1;
139             logger.debug("Adding new client for pulseaudio sink/source {}. Current count: {}", getLabel(null),
140                     countClient);
141             if (countClient <= 0) { // safe against misuse
142                 countClient = 1;
143             }
144             var scheduledDisconnectionFinal = scheduledDisconnection;
145             if (scheduledDisconnectionFinal != null) {
146                 logger.debug("Aborting next disconnect");
147                 scheduledDisconnectionFinal.cancel(true);
148             }
149         } finally {
150             countClientLock.unlock();
151         }
152     }
153
154     protected void minusClientCount() {
155         countClientLock.lock();
156         countClient -= 1;
157         logger.debug("Removing client for pulseaudio sink/source {}. Current count: {}", getLabel(null), countClient);
158         if (countClient < 0) { // safe against misuse
159             countClient = 0;
160         }
161         countClientLock.unlock();
162         if (countClient <= 0) {
163             scheduleDisconnectIfNoClient();
164         }
165     }
166 }