org.openhab.binding.hue.internal.tools.SsdpDiscovery.java Source code

Java tutorial

Introduction

Here is the source code for org.openhab.binding.hue.internal.tools.SsdpDiscovery.java

Source

/**
 * Copyright (c) 2010-2015, openHAB.org and others.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package org.openhab.binding.hue.internal.tools;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Helper class that is able to discover services in the network by a SSDP
 * broadcast.
 * 
 * @author Roman Hartmann
 * @since 1.2.0
 */
public class SsdpDiscovery {

    static final Logger logger = LoggerFactory.getLogger(SsdpDiscovery.class);

    private final static String DISCOVER_MESSAGE = "M-SEARCH * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n"
            + "MAN: \"ssdp:discover\"\r\n" + "MX: 120\r\n" + "ST: ssdp:all\r\n";

    /**
     * Determines the IP address of a service provided in the network. The
     * service is detected by one or more keywords that have to appear in the
     * answer message of the service to the SSDP discover broadcast. The
     * keywords are not case sensitive. The timeout of the discovery is 120
     * seconds.
     * 
     * @param keywords
     *            The keywords that have to be found case insensitive in the
     *            response of the service to be searched.
     * @return The IP if a service with the keywords has been found, null
     *         otherwise.
     * @throws IOException
     */
    public String findIpForResponseKeywords(String... keywords) throws IOException {
        try {
            logger.debug("Sending SSDP discover.");
            MulticastSocket socket = sendDiscoveryBroacast();
            logger.debug("Waiting for response.");
            return scanResposesForKeywords(socket, keywords);
        } catch (SocketTimeoutException e) {
            logger.debug("Timeout of request...");
        }
        return null;
    }

    /**
     * Broadcasts a SSDP discovery message into the network to find provided
     * services.
     * 
     * @return The Socket the answers will arrive at.
     * @throws UnknownHostException
     * @throws IOException
     * @throws SocketException
     * @throws UnsupportedEncodingException
     */
    private MulticastSocket sendDiscoveryBroacast()
            throws UnknownHostException, IOException, SocketException, UnsupportedEncodingException {
        InetAddress multicastAddress = InetAddress.getByName("239.255.255.250");
        final int port = 1900;
        MulticastSocket socket = new MulticastSocket(port);
        socket.setReuseAddress(true);
        socket.setSoTimeout(130000);
        socket.joinGroup(multicastAddress);
        byte[] requestMessage = DISCOVER_MESSAGE.getBytes("UTF-8");
        DatagramPacket datagramPacket = new DatagramPacket(requestMessage, requestMessage.length, multicastAddress,
                port);
        socket.send(datagramPacket);
        return socket;
    }

    /**
     * Scans all messages that arrive on the socket and scans them for the
     * search keywords. The search is not case sensitive.
     * 
     * @param socket
     *            The socket where the answers arrive.
     * @param keywords
     *            The keywords to be searched for.
     * @return
     * @throws IOException
     */
    private String scanResposesForKeywords(MulticastSocket socket, String... keywords) throws IOException {
        // In the worst case a SocketTimeoutException raises
        socket.setSoTimeout(2000);
        do {
            logger.debug("Got an answer message.");
            byte[] rxbuf = new byte[8192];
            DatagramPacket packet = new DatagramPacket(rxbuf, rxbuf.length);
            socket.receive(packet);
            String foundIp = analyzePacket(packet, keywords);
            if (foundIp != null) {
                return foundIp;
            }
        } while (true);
    }

    /**
     * Checks whether a packet does contain all given keywords case insensitive.
     * If all keywords are contained the IP address of the packet sender will be
     * returned.
     * 
     * @param packet
     *            The data packet to be analyzed.
     * @param keywords
     *            The keywords to be searched for.
     * @return The IP of the sender if all keywords have been found, null
     *         otherwise.
     * 
     * @throws IOException
     */
    private String analyzePacket(DatagramPacket packet, String... keywords) throws IOException {

        logger.debug("Analyzing answer message.");

        InetAddress addr = packet.getAddress();
        ByteArrayInputStream in = new ByteArrayInputStream(packet.getData(), 0, packet.getLength());
        String response = IOUtils.toString(in);

        boolean foundAllKeywords = true;

        for (String keyword : keywords) {
            foundAllKeywords &= response.toUpperCase().contains(keyword.toUpperCase());
        }

        if (foundAllKeywords) {
            logger.debug("Found matching answer.");
            return addr.getHostAddress();
        }

        logger.debug("Answer did not match.");
        return null;
    }

}