]> git.basschouten.com Git - openhab-addons.git/blob
f9f5b6130e9034599272152b05cf1ec90fac6384
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.kaleidescape.internal.discovery;
14
15 import static org.openhab.binding.kaleidescape.internal.KaleidescapeBindingConstants.*;
16
17 import java.io.BufferedReader;
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.io.InputStreamReader;
21 import java.io.OutputStream;
22 import java.io.PrintWriter;
23 import java.net.InetAddress;
24 import java.net.InetSocketAddress;
25 import java.net.Socket;
26 import java.net.UnknownHostException;
27 import java.util.Arrays;
28 import java.util.HashSet;
29 import java.util.Set;
30
31 import org.eclipse.jdt.annotation.NonNullByDefault;
32 import org.openhab.core.thing.ThingTypeUID;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 /**
37  * The {@link KaleidescapeDiscoveryJob} class allow manual discovery of
38  * Kaleidescape components for a single IP address. This is used
39  * for threading to make discovery faster.
40  *
41  * @author Chris Graham - Initial contribution
42  * @author Michael Lobstein - Adapted for the Kaleidescape binding
43  * 
44  */
45 @NonNullByDefault
46 public class KaleidescapeDiscoveryJob implements Runnable {
47     private final Logger logger = LoggerFactory.getLogger(KaleidescapeDiscoveryJob.class);
48
49     // Component Types
50     private static final String PLAYER = "Player";
51     private static final String CINEMA_ONE = "Cinema One";
52     private static final String ALTO = "Alto";
53     private static final String STRATO = "Strato";
54     private static final String STRATO_S = "Strato S";
55     private static final String DISC_VAULT = "Disc Vault";
56
57     private static final Set<String> ALLOWED_DEVICES = new HashSet<String>(
58             Arrays.asList(PLAYER, CINEMA_ONE, ALTO, STRATO, STRATO_S, DISC_VAULT));
59
60     private KaleidescapeDiscoveryService discoveryClass;
61
62     private ThingTypeUID thingTypeUid = THING_TYPE_PLAYER;
63     private String ipAddress = EMPTY;
64     private String friendlyName = EMPTY;
65     private String serialNumber = EMPTY;
66
67     public KaleidescapeDiscoveryJob(KaleidescapeDiscoveryService service, String ip) {
68         this.discoveryClass = service;
69         this.ipAddress = ip;
70     }
71
72     @Override
73     public void run() {
74         if (hasKaleidescapeDevice(this.ipAddress)) {
75             discoveryClass.submitDiscoveryResults(this.thingTypeUid, this.ipAddress, this.friendlyName,
76                     this.serialNumber);
77         }
78     }
79
80     /**
81      * Determines if a Kaleidescape component with a movie player zone is available at a given IP address.
82      *
83      * @param ip IP address of the Kaleidescape component as a string.
84      * @return True if a component is found, false if not.
85      */
86     private boolean hasKaleidescapeDevice(String ip) {
87         try {
88             InetAddress address = InetAddress.getByName(ip);
89
90             if (isKaleidescapeDevice(address, DEFAULT_API_PORT)) {
91                 return true;
92             } else {
93                 logger.debug("No Kaleidescape component found at IP address ({})", ip);
94                 return false;
95             }
96         } catch (UnknownHostException e) {
97             logger.debug("Unknown host: {} - {}", ip, e.getMessage());
98             return false;
99         }
100     }
101
102     /**
103      * Tries to establish a connection to a hostname and port and then interrogate the component
104      *
105      * @param host Hostname or IP address to connect to.
106      * @param port Port to attempt to connect to.
107      * @return True if the component found is one the binding supports
108      */
109     private boolean isKaleidescapeDevice(InetAddress host, int port) {
110         try (Socket socket = new Socket()) {
111             socket.connect(new InetSocketAddress(host, port), DISCOVERY_DEFAULT_IP_TIMEOUT_RATE_MS);
112
113             OutputStream output = socket.getOutputStream();
114             PrintWriter writer = new PrintWriter(output, true);
115
116             // query the component to see if it has video zones, the device type, friendly name, and serial number
117             writer.println("01/1/GET_NUM_ZONES:");
118             writer.println("01/1/GET_DEVICE_TYPE_NAME:");
119             writer.println("01/1/GET_FRIENDLY_NAME:");
120             writer.println("01/1/GET_DEVICE_INFO:");
121
122             InputStream input = socket.getInputStream();
123
124             BufferedReader reader = new BufferedReader(new InputStreamReader(input));
125
126             String componentType = EMPTY;
127             String line;
128             String videoZone = null;
129             String audioZone = null;
130             int lineCount = 0;
131
132             while ((line = reader.readLine()) != null) {
133                 String[] strArr = line.split(":");
134
135                 if (strArr.length >= 4) {
136                     switch (strArr[1]) {
137                         case "NUM_ZONES":
138                             videoZone = strArr[2];
139                             audioZone = strArr[3];
140                             break;
141                         case "DEVICE_TYPE_NAME":
142                             componentType = strArr[2];
143                             break;
144                         case "FRIENDLY_NAME":
145                             friendlyName = strArr[2];
146                             break;
147                         case "DEVICE_INFO":
148                             serialNumber = strArr[3].trim(); // take off leading zeros
149                             break;
150                     }
151                 } else {
152                     logger.debug("isKaleidescapeDevice() - Unable to process line: {}", line);
153                 }
154
155                 lineCount++;
156
157                 // stop after reading four lines
158                 if (lineCount > 3) {
159                     break;
160                 }
161             }
162
163             // see if we have a video zone
164             if ("01".equals(videoZone)) {
165                 // now check if we are one of the allowed types
166                 if (ALLOWED_DEVICES.contains(componentType)) {
167                     if (STRATO_S.equals(componentType) || STRATO.equals(componentType)) {
168                         thingTypeUid = THING_TYPE_STRATO;
169                         return true;
170                     }
171
172                     // A 'Player' without an audio zone is really a Strato C
173                     // does not work yet, Strato C erroneously reports "01" for audio zones
174                     // so we are unable to differentiate a Strato C from a Premiere player
175                     if ("00".equals(audioZone) && PLAYER.equals(componentType)) {
176                         thingTypeUid = THING_TYPE_STRATO;
177                         return true;
178                     }
179
180                     // Alto
181                     if (ALTO.equals(componentType)) {
182                         thingTypeUid = THING_TYPE_ALTO;
183                         return true;
184                     }
185
186                     // Cinema One
187                     if (CINEMA_ONE.equals(componentType)) {
188                         thingTypeUid = THING_TYPE_CINEMA_ONE;
189                         return true;
190                     }
191
192                     // A Disc Vault with a video zone (the M700 vault), just call it a THING_TYPE_PLAYER
193                     if (DISC_VAULT.equals(componentType)) {
194                         thingTypeUid = THING_TYPE_PLAYER;
195                         return true;
196                     }
197
198                     // default returns THING_TYPE_PLAYER
199                     return true;
200                 }
201             }
202         } catch (IOException e) {
203             logger.debug("isKaleidescapeDevice() IOException: {}", e.getMessage());
204             return false;
205         }
206
207         return false;
208     }
209 }