2 * Copyright (c) 2010-2022 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.epsonprojector.internal.discovery;
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;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
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.
32 * @author Mark Hilbush - Initial contribution
33 * @author Michael Lobstein - Adapted for the Epson Projector binding
36 public class MulticastListener {
37 private final Logger logger = LoggerFactory.getLogger(MulticastListener.class);
39 private MulticastSocket socket;
41 // Epson-specific properties defined in this binding
42 private String uid = "";
43 private String ipAddress = "";
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;
49 // How long to wait in milliseconds for a discovery beacon
50 public static final int DEFAULT_SOCKET_TIMEOUT_SEC = 3000;
53 * Constructor joins the multicast group, throws IOException on failure.
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);
67 public void shutdown() {
68 logger.debug("Multicast listener closing down multicast socket");
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.
76 public boolean waitForBeacon() throws IOException {
77 byte[] bytes = new byte[600];
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);
84 socket.receive(msgPacket);
86 logger.trace("Multicast listener got datagram of length {} from multicast port: {}", msgPacket.getLength(),
87 msgPacket.toString());
88 } catch (SocketTimeoutException e) {
93 // Get the device properties from the announcement beacon
94 parseAnnouncementBeacon(msgPacket);
101 * Parse the announcement beacon into the elements needed to create the thing.
103 * Example Epson beacon:
104 * AMXB<-UUID=000048746B33><-SDKClass=VideoProjector><-GUID=EPSON_EMP001><-Revision=1.0.0>
106 private void parseAnnouncementBeacon(DatagramPacket packet) {
107 String beacon = (new String(packet.getData(), StandardCharsets.UTF_8)).trim();
109 logger.trace("Multicast listener parsing announcement packet: {}", beacon);
113 if (beacon.toUpperCase().contains("EPSON") && beacon.toUpperCase().contains("VIDEOPROJECTOR")) {
114 ipAddress = packet.getAddress().getHostAddress();
115 parseEpsonAnnouncementBeacon(beacon);
117 logger.debug("Multicast listener doesn't know how to parse beacon: {}", beacon);
121 private void parseEpsonAnnouncementBeacon(String beacon) {
122 String[] parameterList = beacon.split("<-");
124 for (String parameter : parameterList) {
125 String[] keyValue = parameter.split("=");
127 if (keyValue.length != 2) {
131 if (keyValue[0].contains("UUID")) {
132 uid = keyValue[1].substring(0, keyValue[1].length() - 1);
137 private void clearProperties() {
142 public String getUID() {
146 public String getIPAddress() {