Skip to content

Commit f562629

Browse files
committed
Moved the single tenant audio client from the deprecated internal repository to the public repository
1 parent bb45fdd commit f562629

File tree

6 files changed

+57
-133
lines changed

6 files changed

+57
-133
lines changed

README.md

Lines changed: 14 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -37,54 +37,38 @@ $ mvn package
3737
## Development and Debugging (ability to use Eclipse IDE)
3838
**For deployment to a device (e.g. Raspberry Pi) the Maven build must be run from the command line (to create a correctly configured JAR)**
3939

40-
# Instructions for Eclipse
41-
These instructions are for Eclipse, but those of you that use other IDE's can probably adapt to these instuctions.
40+
For development and debugging - an `Eclipse project` has been created. Using a dedicated Java IDE like Eclipse can provide significant benefits during development (and especially debugging). The Eclipse project is configured to function as a Maven Project/Build. This means you can edit, build, and debug directly from Eclipse. Remote debugging is also possible. The Maven build is configured with debug information, so if you run the client on the Raspberry Pi in debug mode you can connect to it from Eclipse and debug interactively.
4241

43-
For development and debugging - an `Eclipse project` has been created. Find it as `.project` in the main folder.
42+
An example command to run the client in remote debug mode is:
43+
`sudo java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000 -jar wpa-1.4-SNAPSHOT.jar`
44+
In Eclipse, simply configure a 'Debug Configuration' that specifies the IP address and port of the target and the main class: `Driver`
4445

45-
Using a dedicated Java IDE like Eclipse can provide significant benefits during development (and especially debugging). The Eclipse project is configured to function as a Maven Project/Build. This means you can edit, build, and debug directly from Eclipse. Remote debugging is also possible. The Maven build is configured with debug information, so if you run the client on the Raspberry Pi in debug mode you can connect to it from Eclipse and debug interactively.
46+
The `start` directory contains startup scripts for both debug and non-debug modes.
4647

47-
Create a new workspace and import the project (as an existing Eclipse project).
48+
If you are setting this up for the first time, it means you will need to create an Eclipse Workspace or use an existing Workspace. Import the Eclipse project from this directory into the Workspace.
4849

49-
You can run and debug the client within Eclipse - just set up a 'Run/Debug' configuration with the main class of 'Driver'.
50-
51-
To debug remotely on the Raspberry Pi:
52-
An example command to run the client in remote debug mode is:
53-
`sudo java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000 -jar wpa-1.4-SNAPSHOT.jar`
54-
In Eclipse, simply configure a 'Debug Configuration' that specifies the IP address and port of the target and the main class: `Driver`
55-
56-
The `start` directory contains startup scripts for both debug and non-debug modes. These scripts can be copied to the `watson` directory on the device.
57-
58-
To Run/Debug directly from Eclipse (on the local machine) you will need to set up a local Run/Debug configuration. The main requirement for this is that you will need to indicate that the working directory is the `target/` directory and make sure that the config directory includes a properly configured `configure.properties` file (the Eclipse build does not put one there, so if a 'clean' build is performed there will not be a `configure.properties` file in the directory. If the `configure.properties` file doesn't exist in the `config` directory the client should announce that to you (a good test really...).
50+
To Run/Debug directly from Eclipse (on the local machine) you will need to set up a local Run/Debug configuration. The main requirement for this is that you will need to indicate that the working directory is the `target/` directory and make sure that it includes a properly configured `configure.properties` file (the Eclipse build does not put one there, so if a 'clean' build is performed there will not be a `configure.properties` file in the directory. If the `configure.properties` file doesn't exist the client should announce that to you (a good test really...).
5951

6052
*(as we find and resolve problems with the Eclipse project, we will add to this section - for example, a 'clean' build will delete 'target', so we probably want a build step to copy a `configure.properties` in at the end of the build.)*
6153

6254

6355
## Deploy
64-
Our standard deployment to a device is into the ~/watson directory.
65-
66-
Copy the jar (created using the `maven package` command) from the `target/` directory on your local system to the device `~/watson` directory, e.g.
56+
Copy the jar (created using the `maven package` command) from the `target/` directory to the device, e.g.
6757

6858
```
69-
$ scp target/wpa-1.4-SNAPSHOT.jar pi@192.168.1.101:~/watson
59+
$ scp target/wpa-1.4-SNAPSHOT.jar pi@192.168.1.15:~/watson
7060
```
7161

72-
## Run
73-
Copy `configure.properties.example` to `configure.properties` into the the `~/watson/config` directory of the device. Provide configuration and credential information and options by editing the following properties:
7462

75-
The `start` directory contains startup scripts for both debug and non-debug modes. These scripts can be copied to the `watson` directory on the device.
63+
## Run
64+
Copy `configure.properties.example` to `configure.properties` and copy it to the device `~/watson`. Provide configuration and credential information and options by editing the following properties:
7665

7766
### Required Connection Information
78-
* `host` The URL of the Audio Gateway server (should NOT include the protocol prefix such as "https://")
79-
* `IAMAPIKey` The client IAM API Key associated with the device in the IBM cloud IAM management system
80-
* `skillset` The skillset to be used by this client
67+
* `host` The URL of the server
68+
* `header` The client ID associated with the device in the client database
69+
* `key` The password, as established in the client database
8170

8271
### Optional Settings
83-
84-
#### Speech to text engine
85-
The server supports two STT engines out of the box - Watson STT and Google STT
86-
* `engine` - if you use the value `watson`, the server will use the Watson STT for converting audio to text, if you use `google` the server will use the Google STT. (default=google)
87-
8872
#### Audio Response via URL or Streaming
8973
The server supports returning the response via an audio URL or by streaming the audio to the client.
9074
* `urltts` (if `true` the server will respond with a URL, if `false` the server will stream the audio (using `audio_data` messages) - default=false)
Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,10 @@
11
### Server connection parameters
22
host=<<endpoint-address>>
3-
userID=<<user ID or client-id>>
4-
3+
header=<<client-id>>
4+
key=<<client-password>>
55
# Used to make a non-SSL connection if testing on a server without SSL configured (locally). Default=false
66
#nossl=true
77

8-
# IAM API Key is Used to authenticate the client device - created per IBM ID (or Service ID) using IBM Cloud
9-
IAMAPIKey=
10-
11-
# Choosing which skill set to use (Required parameter)
12-
skillset=
13-
14-
# Client language specific preferences can be passed (Optional parameter with a default value: en-US)
15-
#language=en-US
16-
17-
# Choosing which STT and TTS engine to convert audio to text and text to audio - possible values are : watson, google , (Optional parameter with a default value : watson)
18-
#engine=
19-
208
### Controls playback method. Playback using an audio URL in the response [true], playback by streaming audio from the server [false]
219
urltts=false
2210

pom.xml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -199,10 +199,6 @@
199199
<artifactId>apache-log4j-extras</artifactId>
200200
<version>1.2.17</version>
201201
</dependency>
202-
<dependency>
203-
<groupId>commons-io</groupId>
204-
<artifactId>commons-io</artifactId>
205-
<version>2.5</version>
206-
</dependency>
207202
</dependencies>
203+
208204
</project>

src/main/java/Driver.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,7 @@ private static Properties readProps() {
111111
// Just return a null properties...
112112
} finally {
113113
try {
114-
if (null != file) {
115-
file.close();
116-
}
114+
file.close();
117115
} catch (IOException e) { }
118116
}
119117
return props;

src/main/java/wa/audio/AudioInput.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ private void captureInternal() throws InterruptedException {
183183
e.printStackTrace();
184184
} finally {
185185
client.getIndicator().off();
186-
client.logFinalServerWriteStatus();
186+
client.logFinalServerWriteStatus();
187187
if (this.client.isServerConnectionReady() && writingToServer) {
188188
this.client.writeToServer(ClientHelpers.getClientEndAudioAction().toString());
189189
}
@@ -192,10 +192,6 @@ private void captureInternal() throws InterruptedException {
192192
audioInputDevice.release();
193193
audioInputDevice = null;
194194
}
195-
196-
client.wakeupTriggerIsAllowed();
197-
198-
// TODO is this still needed?
199195
// Enable a client 'trigger wake up safety' in case we never get a response
200196
client.scheduleWakeupTriggerEnable();
201197
}

src/main/java/wa/client/Client.java

Lines changed: 38 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@
1818
import java.io.IOException;
1919
import java.lang.reflect.Constructor;
2020
import java.lang.reflect.InvocationTargetException;
21-
import java.net.HttpURLConnection;
2221
import java.net.InetAddress;
23-
import java.net.URL;
24-
import java.nio.charset.StandardCharsets;
2522
import java.util.ArrayList;
2623
import java.util.Iterator;
2724
import java.util.List;
@@ -38,8 +35,11 @@
3835
import org.json.JSONException;
3936
import org.json.JSONObject;
4037

38+
import okhttp3.Credentials;
39+
import okhttp3.MediaType;
4140
import okhttp3.OkHttpClient;
4241
import okhttp3.Request;
42+
import okhttp3.RequestBody;
4343
import okhttp3.Response;
4444
import okhttp3.WebSocket;
4545
import okhttp3.WebSocketListener;
@@ -81,21 +81,15 @@ public class Client extends WebSocketListener implements ThreadManager, Runnable
8181

8282
private WebSocket webSocket;
8383

84-
private String IAMAccessToken = null;
85-
86-
private String skillset = null;
87-
88-
private String language = null;
89-
90-
private String engine = null;
91-
9284
private String watsonHost = null;
9385

9486
private String watsonPort = null;
9587

9688
private Boolean watsonSsl = true;
9789

98-
private String userID = null;
90+
private String watsonHeader = null;
91+
92+
private String watsonKey = null;
9993

10094
private Debouncer<String> debouncer;
10195

@@ -303,9 +297,6 @@ public void run() {
303297
LOG.error("Server connection is not ready and microphone is open - Problem closing the microphone.", e);
304298
}
305299
if (ServerConnectionStatus.NOTCONNECTED == getServerConnectionStatus()) {
306-
// IAMAccessToken should be retrieved from the IAM service by providing it with your cloud API Key (based on your IBM ID)
307-
IAMAccessToken = getIAMAccessToken(IAMAPIKey);
308-
309300
// We need to connect...
310301
connect();
311302
Thread.sleep(1500);
@@ -790,10 +781,7 @@ else if (null != currentAudioId && null != previousAudioId && !currentAudioId.eq
790781
LOG.debug(String.format(" Server sent a different response without a CARD - treat as STOP and play new response. Current-ID: %s Previous-ID: %s", currentAudioId, previousAudioId));
791782
// Clear current audioId
792783
previousAudioId = null;
793-
794-
// commented the next line as it was causing a bug - after 2-3 utterances the voice playbacks stops
795-
//this.audioOutput.stop();
796-
784+
this.audioOutput.stop();
797785
getSocketCommandProcessor().sendPlaybackStop();
798786
// Don't enable the wake up trigger. End of playback will do it.
799787
allowWakeUpTriggerAfterProcessing = false;
@@ -929,7 +917,6 @@ private void continueConversation(String id, JSONObject complexPromptSttOptions)
929917

930918
private int writeToServerCount;
931919
private int writeToServerBytes;
932-
private String IAMAPIKey;
933920

934921
public synchronized void clearServerWriteLogging() {
935922
writeToServerCount = 0;
@@ -1041,21 +1028,12 @@ private void joinThreads() {
10411028
}
10421029

10431030
private void initialize(Properties props) {
1044-
// Required parameter
1045-
IAMAPIKey = props.getProperty("IAMAPIKey");
1046-
skillset = props.getProperty("skillset");
1047-
1048-
// Optional parameters
1049-
language = props.getProperty("language");
1050-
engine = props.getProperty("engine");
1051-
1052-
10531031
watsonHost = props.getProperty("host");
10541032
watsonPort = props.getProperty("port");
10551033
String noSsl = props.getProperty("nossl", "false");
10561034
watsonSsl = noSsl.equalsIgnoreCase("false");
1057-
// the userId
1058-
userID = props.getProperty("userID");
1035+
watsonHeader = props.getProperty("header");
1036+
watsonKey = props.getProperty("key");
10591037
watsonVoice = props.getProperty("voice");
10601038
commandSocketPort = Integer.parseInt(props.getProperty("cmdSocketPort", "10010"));
10611039
audioSocketPort = Integer.parseInt(props.getProperty("audioSocketPort", "10011"));
@@ -1069,15 +1047,13 @@ private void initialize(Properties props) {
10691047
java.util.logging.Logger.getLogger(OkHttpClient.class.getName()).setLevel(Level.FINE);
10701048
}
10711049

1072-
LOG.info("USER ID (Optional): " + userID);
1073-
LOG.info("Language (Optional): " + language);
1074-
LOG.info("Engine (Optional): " + engine);
10751050
LOG.info("WATSON HOST: " + watsonHost);
10761051
LOG.info("WATSON PORT: " + watsonPort);
1077-
LOG.info("SKILLSET: " + skillset);
1078-
LOG.info("WATSON IAM API Key: *****");
1052+
LOG.info("WATSON HEADER: " + watsonHeader);
1053+
LOG.info("WATSON KEY: *****");
1054+
LOG.debug(" (" + watsonKey + ")");
10791055

1080-
if (watsonHost == null || skillset == null || IAMAPIKey == null) {
1056+
if (watsonHost == null || watsonHeader == null || watsonKey == null) {
10811057
LocalAudio.play(LocalAudio.ERROR_CONFIG);
10821058
throw new Error("Missing authentication information. Aborting.");
10831059
}
@@ -1129,43 +1105,7 @@ private void initialize(Properties props) {
11291105

11301106
}
11311107

1132-
private String getIAMAccessToken(String apikey) {
1133-
String IAMAccessToken = "";
1134-
1135-
String urlParameters = "apikey="+apikey+"&grant_type=urn%3Aibm%3Aparams%3Aoauth%3Agrant-type%3Aapikey";
1136-
byte[] postData = urlParameters.getBytes( StandardCharsets.UTF_8 );
1137-
int postDataLength = postData.length;
1138-
String request = "https://iam.bluemix.net/oidc/token";
1139-
URL url;
1140-
try {
1141-
url = new URL( request );
1142-
1143-
HttpURLConnection conn= (HttpURLConnection) url.openConnection();
1144-
conn.setDoOutput( true );
1145-
conn.setInstanceFollowRedirects( false );
1146-
conn.setRequestMethod( "POST" );
1147-
conn.setRequestProperty( "Content-Type", "application/x-www-form-urlencoded");
1148-
conn.setRequestProperty( "cache-control", "no-cache");
1149-
conn.setRequestProperty( "accept","application/json");
1150-
// conn.setRequestProperty( "charset", "utf-8");
1151-
// conn.setRequestProperty( "Content-Length", Integer.toString( postDataLength ));
1152-
conn.setUseCaches( false );
1153-
conn.getOutputStream().write(postData);
1154-
1155-
String response = org.apache.commons.io.IOUtils.toString(conn.getInputStream(), StandardCharsets.UTF_8);
1156-
JSONObject jsonObj = new JSONObject(response);
1157-
IAMAccessToken = jsonObj.getString("access_token");
1158-
1159-
} catch (IOException e) {
1160-
LOG.error(String.format("Error - Could not convert IAM API Key to IAM Access token : %s",
1161-
e.getMessage()),e);
1162-
}
1163-
1164-
return IAMAccessToken;
1165-
}
1166-
1167-
1168-
/**
1108+
/**
11691109
* Connect the client to the Watson (Websocket) Server.
11701110
* If the client is currently connected, it will first disconnect and then attempt a new connection.
11711111
*
@@ -1192,13 +1132,35 @@ private synchronized void connect() throws InterruptedException {
11921132
.build();
11931133

11941134
try {
1135+
String tokenUrl = (watsonSsl ? "https" : "http")
1136+
+ "://" + watsonHost
1137+
+ (watsonPort == null ? "" : ":" + watsonPort) + "/v1/api/tokens";
1138+
Request tokenRequest = new Request.Builder()
1139+
.url(tokenUrl)
1140+
.header("Authorization", Credentials.basic(watsonHeader, watsonKey))
1141+
.post(RequestBody.create(MediaType.parse("text/plain"), ""))
1142+
.build();
1143+
1144+
Response response = httpClient.newCall(tokenRequest).execute();
1145+
if (!response.isSuccessful()) {
1146+
int code = response.code();
1147+
response.body().close();
1148+
if (code == 401 || code == 403) {
1149+
throw new AuthException("Authentication failure: " + response.message());
1150+
}
1151+
}
1152+
1153+
String tokenJSON = response.body().string();
1154+
JSONObject tokenObject = new JSONObject(tokenJSON);
1155+
String token = tokenObject.getString("token");
1156+
LOG.info("Token received: " + token);
1157+
11951158
// Build request
11961159
String webSocketUrl = (watsonSsl ? "wss" : "ws")
11971160
+ "://" + watsonHost
1198-
+ (watsonPort == null ? "" : ":" + watsonPort) + "?skillset=" + skillset+"&userID=" + userID + "&language=" + language + "&engine=" + engine;
1161+
+ (watsonPort == null ? "" : ":" + watsonPort) + "?token=" + token;
11991162
Request request = new Request.Builder()
12001163
.url(webSocketUrl)
1201-
.addHeader("Authorization", "Bearer " + IAMAccessToken)
12021164
.build();
12031165

12041166
// initialize the watch dog

0 commit comments

Comments
 (0)