/** * Copyright (c) 2010-2016 by the respective copyright holders. * * 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.samsungac.internal; import java.io.IOException; import java.net.DatagramPacket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.InterfaceAddress; import java.net.MulticastSocket; import java.net.NetworkInterface; import java.net.SocketTimeoutException; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Helper class that is able to discover Samsung Airconditioners in the network by a SSDP * broadcast. * * @author Stein Tore Tøsse * @since 1.6.0 */ public class SsdpDiscovery { private static final Logger logger = LoggerFactory.getLogger(SsdpDiscovery.class); static final int PORT = 1900; static final String NEWLINE = "\r\n"; private final static String DISCOVER_MESSAGE = "NOTIFY * HTTP/1.1" + NEWLINE + "HOST: 239.255.255.250:" + PORT + NEWLINE + "CACHE-CONTROL: max-age=20" + NEWLINE + "SERVER: AIR CONDITIONER" + NEWLINE + "SPEC_VER: MSpec-1.00" + NEWLINE + "SERVICE_NAME: ControlServer-MLib" + NEWLINE + "MESSAGE_TYPE: CONTROLLER_START" + NEWLINE; public static void main(String args[]) { Map<String, Map<String, String>> resp = discover(); logger.debug("Got response from possible air conditioner(s):"); for (Map<String, String> ac : resp.values()) { if (ac.get("IP") != null) { logger.debug(ac.toString()); } } } /** * Discovers Samsung Air Conditioners in the network, and returns a Map * with all the details about them. We will use the IP-address and the MAC-address later * * @return A Map of all values from the air conditioner * @throws Exception */ public static Map<String, Map<String, String>> discover() { Map<String, Map<String, String>> response = new HashMap<String, Map<String, String>>(); logger.debug("Sending multibroadcast to all possible network interfaces..."); try { List<InetAddress> ia = getBroadCastAddress(); for (InetAddress i : ia) { try { logger.debug("Broadcasting to {}", i); sendNotify(DISCOVER_MESSAGE, i); Map<String, Map<String, String>> resp = retrieveResponse(); response.putAll(resp); } catch (IOException ioe) { logger.warn("Could not broadcast to {}, moving on to next broadcast address", i); } catch (Exception e) { // No problem, let's try next InetAddress } } } catch (Exception e) { } return response; } private static Map<String, String> parseResponse(String response) { Map<String, String> device = new HashMap<String, String>(); if (response == null) { return device; } for (String element : response.split(NEWLINE)) { if (element.contains(": ")) { device.put(element.split(": ")[0], element.split(": ")[1]); } } if (device.size() > 0) { String location = device.get("LOCATION"); if (location != null && location.length() > 1 && location.contains("//")) { device.put("IP", device.get("LOCATION").split("//")[1].toString()); } else { // If no LOCATION, then no Air Conditioner device.clear(); } } return device; } static Map<String, Map<String, String>> retrieveResponse() throws Exception { String response = null; Map<String, Map<String, String>> result = new HashMap<String, Map<String, String>>(); MulticastSocket recSocket = setUpSocket(); int i = 0; logger.debug("Retrieving response"); while (i < 10) { byte[] buf = new byte[2048]; DatagramPacket input = new DatagramPacket(buf, buf.length); try { recSocket.receive(input); response = new String(input.getData()); Map<String, String> parsedResponse = parseResponse(response); result.put(parsedResponse.get("IP"), parsedResponse); } catch (SocketTimeoutException e) { if (i >= 10) { break; } i++; } } logger.debug("Response retrieved: {}", result); return result; } private static MulticastSocket setUpSocket() throws IOException { MulticastSocket recSocket = new MulticastSocket(null); recSocket.bind(new InetSocketAddress(InetAddress.getByName("0.0.0.0"), PORT)); recSocket.setTimeToLive(10); recSocket.setSoTimeout(1000); recSocket.setBroadcast(true); return recSocket; } private static void sendNotify(String notifyMessage, InetAddress ia) throws Exception { MulticastSocket socket = new MulticastSocket(null); try { socket.bind(new InetSocketAddress(PORT)); socket.setTimeToLive(4); byte[] data = notifyMessage.toString().getBytes(); socket.send(new DatagramPacket(data, data.length, new InetSocketAddress(ia, PORT))); } catch (Exception e) { logger.error("sendNotify", e); throw e; } finally { socket.disconnect(); socket.close(); } } private static List<InetAddress> getBroadCastAddress() throws Exception { Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); List<InetAddress> addresses = new ArrayList<InetAddress>(); addresses.add(InetAddress.getByName("255.255.255.255")); while (interfaces.hasMoreElements()) { NetworkInterface networkInterface = interfaces.nextElement(); if (networkInterface.isLoopback() || !networkInterface.supportsMulticast()) { continue; // Don't want to broadcast to the loopback interface } for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) { InetAddress broadcast = interfaceAddress.getBroadcast(); if (broadcast != null) { addresses.add(broadcast); } } } return addresses; } }