org.eclipse.smarthome.binding.wemo.discovery.WemoDiscoveryService.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.smarthome.binding.wemo.discovery.WemoDiscoveryService.java

Source

/**
 * Copyright (c) 2014-2015 openHAB UG (haftungsbeschraenkt) 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.eclipse.smarthome.binding.wemo.discovery;

import static org.eclipse.smarthome.binding.wemo.WemoBindingConstants.LOCATION;
import static org.eclipse.smarthome.binding.wemo.WemoBindingConstants.UDN;
import static org.eclipse.smarthome.binding.wemo.WemoBindingConstants.WEMO_INSIGHT_TYPE_UID;
import static org.eclipse.smarthome.binding.wemo.WemoBindingConstants.WEMO_LIGHTSWITCH_TYPE_UID;
import static org.eclipse.smarthome.binding.wemo.WemoBindingConstants.WEMO_MOTION_TYPE_UID;
import static org.eclipse.smarthome.binding.wemo.WemoBindingConstants.WEMO_SOCKET_TYPE_UID;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MulticastSocket;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang.StringUtils;
import org.eclipse.smarthome.binding.wemo.handler.WemoHandler;
import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService;
import org.eclipse.smarthome.config.discovery.DiscoveryResult;
import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder;
import org.eclipse.smarthome.core.thing.ThingTypeUID;
import org.eclipse.smarthome.core.thing.ThingUID;
import org.eclipse.smarthome.io.net.http.HttpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The {@link WemoDiscoveryService} is responsible for discovering new and
 * removed Wemo devices.
 *
 * @author Hans-Jrg Merk - Initial contribution
 *
 */
public class WemoDiscoveryService extends AbstractDiscoveryService {

    private Logger logger = LoggerFactory.getLogger(WemoDiscoveryService.class);

    /**
     * Connection timeout
     */
    private static int TIMEOUT = 5000;
    /**
     * Port to send discovery message to
     */
    private static final int SSDP_PORT = 1900;
    /**
     * Port to use for sending discovery message
     */
    private static final int SSDP_SEARCH_PORT = 1901;
    /**
     * Broadcast address to use for sending discovery message
     */
    private static final String SSDP_IP = "239.255.255.250";

    public InetAddress address;

    /**
     * When true we will keep sending out packets
     */

    static boolean discoveryRunning = false;

    /**
     * The refresh interval for discovering WeMo devices
     */
    private long refreshInterval = 600;
    private ScheduledFuture<?> wemoDiscoveryJob;
    private Runnable wemoDiscoveryRunnable = new Runnable() {
        @Override
        public void run() {
            discoverWemo();
        }
    };

    public WemoDiscoveryService() {
        super(WemoHandler.SUPPORTED_THING_TYPES, 15, true);
    }

    public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
        return WemoHandler.SUPPORTED_THING_TYPES;
    }

    protected void startScan() {
        logger.debug("Starting WeMo Device discovery");
        discoverWemo();
    }

    @Override
    protected void startBackgroundDiscovery() {
        logger.trace("Start WeMo device background discovery");
        if (wemoDiscoveryJob == null || wemoDiscoveryJob.isCancelled()) {
            wemoDiscoveryJob = scheduler.scheduleAtFixedRate(wemoDiscoveryRunnable, 0, refreshInterval,
                    TimeUnit.SECONDS);
        }
    }

    @Override
    protected void stopBackgroundDiscovery() {
        logger.debug("Stop WeMo device background discovery");
        if (wemoDiscoveryJob != null && !wemoDiscoveryJob.isCancelled()) {
            wemoDiscoveryJob.cancel(true);
            wemoDiscoveryJob = null;
        }
    }

    /**
     * Scans for WeMo devices
     */
    private synchronized void discoverWemo() {
        logger.debug("Run Wemo device discovery");
        sendWemoDiscoveryMessage();
        logger.trace("Done sending WeMo broadcast discovery message");
        //receiveWemoDiscoveryMessage();
        logger.trace("Done receiving WeMo broadcast discovery message");
    }

    public void sendWemoDiscoveryMessage() {
        logger.debug("wemoDiscovery() is called!");
        try {

            InetAddress localhost = InetAddress.getLocalHost();
            InetSocketAddress srcAddress = new InetSocketAddress(localhost, SSDP_SEARCH_PORT);

            InetSocketAddress dstAddress = new InetSocketAddress(InetAddress.getByName(SSDP_IP), SSDP_PORT);

            // Request-Packet-Constructor
            StringBuffer discoveryMessage = new StringBuffer();
            discoveryMessage.append("M-SEARCH * HTTP/1.1\r\n");
            discoveryMessage.append("HOST: " + SSDP_IP + ":" + SSDP_PORT + "\r\n");
            discoveryMessage.append("ST: upnp:rootdevice\r\n");
            discoveryMessage.append("MAN: \"ssdp:discover\"\r\n");
            discoveryMessage.append("MX: 5\r\n");
            discoveryMessage.append("\r\n");
            logger.trace("Request: {}", discoveryMessage.toString());
            byte[] discoveryMessageBytes = discoveryMessage.toString().getBytes();
            DatagramPacket discoveryPacket = new DatagramPacket(discoveryMessageBytes, discoveryMessageBytes.length,
                    dstAddress);

            // Send multi-cast packet
            MulticastSocket multicast = null;
            try {
                multicast = new MulticastSocket(null);
                multicast.bind(srcAddress);
                logger.trace("Source-Address = '{}'", srcAddress);
                multicast.setTimeToLive(4);
                logger.debug("Send multicast request.");
                multicast.send(discoveryPacket);
            } finally {
                logger.trace("Multicast ends. Close connection.");
                multicast.disconnect();
                multicast.close();
            }

            // Response-Listener
            DatagramSocket wemoReceiveSocket = null;
            DatagramPacket receivePacket = null;
            try {
                wemoReceiveSocket = new DatagramSocket(SSDP_SEARCH_PORT);
                wemoReceiveSocket.setSoTimeout(TIMEOUT);
                logger.debug("Send datagram packet.");
                wemoReceiveSocket.send(discoveryPacket);

                while (true) {
                    try {
                        receivePacket = new DatagramPacket(new byte[1536], 1536);
                        wemoReceiveSocket.receive(receivePacket);
                        final String message = new String(receivePacket.getData());
                        logger.trace("Received message: {}", message);

                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                String label = "WeMo Device";
                                String wemoUDN = null;
                                String wemoLocation = null;
                                String wemoFriendlyName = null;
                                String wemoModelName = null;
                                ThingUID uid = null;

                                if (message != null) {
                                    if (message.contains("Socket-1_0") || message.contains("Insight-1_0")
                                            || message.contains("Motion-1_0")
                                            || message.contains("Lightswitch-1_0")) {
                                        wemoUDN = StringUtils.substringBetween(message, "USN: uuid:",
                                                "::upnp:rootdevice");
                                        logger.debug("Wemo device with UDN '{}' found", wemoUDN);
                                        wemoLocation = StringUtils.substringBetween(message, "LOCATION: ",
                                                "/setup.xml");
                                        if (wemoLocation != null) {
                                            try {
                                                int timeout = 5000;
                                                String response = HttpUtil.executeUrl("GET",
                                                        wemoLocation + "/setup.xml", timeout);
                                                wemoFriendlyName = StringUtils.substringBetween(response,
                                                        "<friendlyName>", "</friendlyName>");
                                                logger.debug("Wemo device '{}' found at '{}'", wemoFriendlyName,
                                                        wemoLocation);
                                                wemoModelName = StringUtils.substringBetween(response,
                                                        "<modelName>", "</modelName>");
                                                logger.trace("Wemo device '{}' has model name '{}'",
                                                        wemoFriendlyName, wemoModelName);
                                                label = "Wemo" + wemoModelName;

                                                switch (wemoModelName) {
                                                case "Socket":
                                                    logger.debug(
                                                            "Creating ThingUID for device model '{}' with UDN '{}'",
                                                            wemoModelName, wemoUDN);
                                                    uid = new ThingUID(WEMO_SOCKET_TYPE_UID, wemoUDN);

                                                    break;
                                                case "Insight":
                                                    logger.trace(
                                                            "Creating ThingUID for device model '{}' with UDN '{}'",
                                                            wemoModelName, wemoUDN);
                                                    uid = new ThingUID(WEMO_INSIGHT_TYPE_UID, wemoUDN);
                                                    break;
                                                case "LightSwitch":
                                                    logger.trace(
                                                            "Creating ThingUID for device model '{}' with UDN '{}'",
                                                            wemoModelName, wemoUDN);
                                                    uid = new ThingUID(WEMO_LIGHTSWITCH_TYPE_UID, wemoUDN);
                                                    break;
                                                case "Motion":
                                                    logger.trace(
                                                            "Creating ThingUID for device model '{}' with UDN '{}'",
                                                            wemoModelName, wemoUDN);
                                                    uid = new ThingUID(WEMO_MOTION_TYPE_UID, wemoUDN);
                                                    break;
                                                }
                                                Map<String, Object> properties = new HashMap<>(4);
                                                properties.put(UDN, wemoUDN);
                                                properties.put(LOCATION, wemoLocation);

                                                DiscoveryResult result = DiscoveryResultBuilder.create(uid)
                                                        .withProperties(properties).withLabel(label).build();
                                                thingDiscovered(result);

                                            } catch (Exception te) {
                                                logger.error("Could not discover WeMo device", te);
                                            }
                                        }
                                    }
                                }
                            }
                        }).start();

                    } catch (SocketTimeoutException e) {
                        logger.debug("Message receive timed out.");
                        break;
                    }
                }
            } finally {
                if (wemoReceiveSocket != null) {
                    wemoReceiveSocket.disconnect();
                    wemoReceiveSocket.close();
                }
            }

        } catch (Exception e) {
            logger.error("Could not send Wemo device discovery", e);
        }

    }
}