2 * Copyright (c) 2010-2020 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.kaleidescape.internal.discovery;
15 import static org.openhab.binding.kaleidescape.internal.KaleidescapeBindingConstants.*;
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;
31 import org.eclipse.jdt.annotation.NonNullByDefault;
32 import org.openhab.core.thing.ThingTypeUID;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
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.
41 * @author Chris Graham - Initial contribution
42 * @author Michael Lobstein - Adapted for the Kaleidescape binding
46 public class KaleidescapeDiscoveryJob implements Runnable {
47 private final Logger logger = LoggerFactory.getLogger(KaleidescapeDiscoveryJob.class);
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";
57 private static final Set<String> ALLOWED_DEVICES = new HashSet<String>(
58 Arrays.asList(PLAYER, CINEMA_ONE, ALTO, STRATO, STRATO_S, DISC_VAULT));
60 private KaleidescapeDiscoveryService discoveryClass;
62 private ThingTypeUID thingTypeUid = THING_TYPE_PLAYER;
63 private String ipAddress = EMPTY;
64 private String friendlyName = EMPTY;
65 private String serialNumber = EMPTY;
67 public KaleidescapeDiscoveryJob(KaleidescapeDiscoveryService service, String ip) {
68 this.discoveryClass = service;
74 if (hasKaleidescapeDevice(this.ipAddress)) {
75 discoveryClass.submitDiscoveryResults(this.thingTypeUid, this.ipAddress, this.friendlyName,
81 * Determines if a Kaleidescape component with a movie player zone is available at a given IP address.
83 * @param ip IP address of the Kaleidescape component as a string.
84 * @return True if a component is found, false if not.
86 private boolean hasKaleidescapeDevice(String ip) {
88 InetAddress address = InetAddress.getByName(ip);
90 if (isKaleidescapeDevice(address, DEFAULT_API_PORT)) {
93 logger.debug("No Kaleidescape component found at IP address ({})", ip);
96 } catch (UnknownHostException e) {
97 logger.debug("Unknown host: {} - {}", ip, e.getMessage());
103 * Tries to establish a connection to a hostname and port and then interrogate the component
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
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);
113 OutputStream output = socket.getOutputStream();
114 PrintWriter writer = new PrintWriter(output, true);
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:");
122 InputStream input = socket.getInputStream();
124 BufferedReader reader = new BufferedReader(new InputStreamReader(input));
126 String componentType = EMPTY;
128 String videoZone = null;
129 String audioZone = null;
132 while ((line = reader.readLine()) != null) {
133 String[] strArr = line.split(":");
135 if (strArr.length >= 4) {
138 videoZone = strArr[2];
139 audioZone = strArr[3];
141 case "DEVICE_TYPE_NAME":
142 componentType = strArr[2];
144 case "FRIENDLY_NAME":
145 friendlyName = strArr[2];
148 serialNumber = strArr[3].trim(); // take off leading zeros
152 logger.debug("isKaleidescapeDevice() - Unable to process line: {}", line);
157 // stop after reading four lines
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;
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;
181 if (ALTO.equals(componentType)) {
182 thingTypeUid = THING_TYPE_ALTO;
187 if (CINEMA_ONE.equals(componentType)) {
188 thingTypeUid = THING_TYPE_CINEMA_ONE;
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;
198 // default returns THING_TYPE_PLAYER
202 } catch (IOException e) {
203 logger.debug("isKaleidescapeDevice() IOException: {}", e.getMessage());