]> git.basschouten.com Git - openhab-addons.git/blob
5abc20935de10a83d38311bd7c5d721b80834b49
[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.epsonprojector.internal.discovery;
14
15 import java.io.IOException;
16 import java.net.DatagramPacket;
17 import java.net.InetAddress;
18 import java.net.MulticastSocket;
19 import java.net.NetworkInterface;
20 import java.net.SocketException;
21 import java.net.SocketTimeoutException;
22 import java.nio.charset.StandardCharsets;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28 /**
29  * The {@link MulticastListener} class is responsible for listening for the Epson projector device announcement
30  * beacons on the multicast address, and then extracting the data fields out of the received datagram.
31  *
32  * @author Mark Hilbush - Initial contribution
33  * @author Michael Lobstein - Adapted for the Epson Projector binding
34  */
35 @NonNullByDefault
36 public class MulticastListener {
37     private final Logger logger = LoggerFactory.getLogger(MulticastListener.class);
38
39     private MulticastSocket socket;
40
41     // Epson-specific properties defined in this binding
42     private String uid = "";
43     private String ipAddress = "";
44
45     // Epson projector devices announce themselves on a multicast port
46     private static final String EPSON_MULTICAST_GROUP = "239.255.250.250";
47     private static final int EPSON_MULTICAST_PORT = 9131;
48
49     // How long to wait in milliseconds for a discovery beacon
50     public static final int DEFAULT_SOCKET_TIMEOUT_SEC = 3000;
51
52     /*
53      * Constructor joins the multicast group, throws IOException on failure.
54      */
55     public MulticastListener(String ipv4Address) throws IOException, SocketException {
56         InetAddress ifAddress = InetAddress.getByName(ipv4Address);
57         logger.debug("Discovery job using address {} on network interface {}", ifAddress.getHostAddress(),
58                 NetworkInterface.getByInetAddress(ifAddress).getName());
59         socket = new MulticastSocket(EPSON_MULTICAST_PORT);
60         socket.setInterface(ifAddress);
61         socket.setSoTimeout(DEFAULT_SOCKET_TIMEOUT_SEC);
62         InetAddress mcastAddress = InetAddress.getByName(EPSON_MULTICAST_GROUP);
63         socket.joinGroup(mcastAddress);
64         logger.debug("Multicast listener joined multicast group {}:{}", EPSON_MULTICAST_GROUP, EPSON_MULTICAST_PORT);
65     }
66
67     public void shutdown() {
68         logger.debug("Multicast listener closing down multicast socket");
69         socket.close();
70     }
71
72     /*
73      * Wait on the multicast socket for an announcement beacon. Return false on socket timeout or error.
74      * Otherwise, parse the beacon for information about the device.
75      */
76     public boolean waitForBeacon() throws IOException {
77         byte[] bytes = new byte[600];
78         boolean beaconFound;
79
80         // Wait for a device to announce itself
81         logger.trace("Multicast listener waiting for datagram on multicast port");
82         DatagramPacket msgPacket = new DatagramPacket(bytes, bytes.length);
83         try {
84             socket.receive(msgPacket);
85             beaconFound = true;
86             logger.trace("Multicast listener got datagram of length {} from multicast port: {}", msgPacket.getLength(),
87                     msgPacket.toString());
88         } catch (SocketTimeoutException e) {
89             beaconFound = false;
90         }
91
92         if (beaconFound) {
93             // Get the device properties from the announcement beacon
94             parseAnnouncementBeacon(msgPacket);
95         }
96
97         return beaconFound;
98     }
99
100     /*
101      * Parse the announcement beacon into the elements needed to create the thing.
102      *
103      * Example Epson beacon:
104      * AMXB<-UUID=000048746B33><-SDKClass=VideoProjector><-GUID=EPSON_EMP001><-Revision=1.0.0>
105      */
106     private void parseAnnouncementBeacon(DatagramPacket packet) {
107         String beacon = (new String(packet.getData(), StandardCharsets.UTF_8)).trim();
108
109         logger.trace("Multicast listener parsing announcement packet: {}", beacon);
110
111         clearProperties();
112
113         if (beacon.toUpperCase().contains("EPSON") && beacon.toUpperCase().contains("VIDEOPROJECTOR")) {
114             ipAddress = packet.getAddress().getHostAddress();
115             parseEpsonAnnouncementBeacon(beacon);
116         } else {
117             logger.debug("Multicast listener doesn't know how to parse beacon: {}", beacon);
118         }
119     }
120
121     private void parseEpsonAnnouncementBeacon(String beacon) {
122         String[] parameterList = beacon.split("<-");
123
124         for (String parameter : parameterList) {
125             String[] keyValue = parameter.split("=");
126
127             if (keyValue.length != 2) {
128                 continue;
129             }
130
131             if (keyValue[0].contains("UUID")) {
132                 uid = keyValue[1].substring(0, keyValue[1].length() - 1);
133             }
134         }
135     }
136
137     private void clearProperties() {
138         uid = "";
139         ipAddress = "";
140     }
141
142     public String getUID() {
143         return uid;
144     }
145
146     public String getIPAddress() {
147         return ipAddress;
148     }
149 }