]> git.basschouten.com Git - openhab-addons.git/blob
dd58c6ed511339ef0d0ef47417830517956a650f
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.danfossairunit.internal.discovery;
14
15 import static org.openhab.binding.danfossairunit.internal.DanfossAirUnitBindingConstants.*;
16
17 import java.io.IOException;
18 import java.net.DatagramPacket;
19 import java.net.DatagramSocket;
20 import java.net.InetAddress;
21 import java.net.InterfaceAddress;
22 import java.net.NetworkInterface;
23 import java.net.SocketTimeoutException;
24 import java.util.*;
25
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.openhab.core.config.discovery.AbstractDiscoveryService;
28 import org.openhab.core.config.discovery.DiscoveryResult;
29 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
30 import org.openhab.core.config.discovery.DiscoveryService;
31 import org.openhab.core.thing.ThingTypeUID;
32 import org.openhab.core.thing.ThingUID;
33 import org.osgi.service.component.annotations.Component;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 /**
38  * The Discovery service implementation to scan for available air units in the network via broadcast.
39  *
40  * @author Ralf Duckstein - Initial contribution
41  * @author Robert Bach - heavy refactorings
42  */
43 @Component(service = DiscoveryService.class, immediate = true)
44 @NonNullByDefault
45 public class DanfossAirUnitDiscoveryService extends AbstractDiscoveryService {
46
47     private static final int BROADCAST_PORT = 30045;
48     private static final byte[] DISCOVER_SEND = { 0x0c, 0x00, 0x30, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13 };
49     private static final byte[] DISCOVER_RECEIVE = { 0x0d, 0x00, 0x07, 0x00, 0x02, 0x02, 0x00 };
50
51     private final Logger logger = LoggerFactory.getLogger(DanfossAirUnitDiscoveryService.class);
52
53     public DanfossAirUnitDiscoveryService() {
54         super(SUPPORTED_THING_TYPES_UIDS, 15, true);
55     }
56
57     @Override
58     public Set<ThingTypeUID> getSupportedThingTypes() {
59         return SUPPORTED_THING_TYPES_UIDS;
60     }
61
62     @Override
63     protected void startBackgroundDiscovery() {
64         logger.debug("Start Danfoss Air CCM background discovery");
65         scheduler.execute(this::discover);
66     }
67
68     @Override
69     public void startScan() {
70         logger.debug("Start Danfoss Air CCM scan");
71         discover();
72     }
73
74     private synchronized void discover() {
75         logger.debug("Try to discover all Danfoss Air CCM devices");
76
77         try (DatagramSocket socket = new DatagramSocket()) {
78
79             Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
80             while (interfaces.hasMoreElements()) {
81                 NetworkInterface networkInterface = interfaces.nextElement();
82                 if (networkInterface.isLoopback() || !networkInterface.isUp()) {
83                     continue;
84                 }
85                 for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) {
86                     if (interfaceAddress.getBroadcast() == null) {
87                         continue;
88                     }
89                     logger.debug("Sending broadcast on interface {} to discover Danfoss Air CCM device...",
90                             interfaceAddress.getAddress());
91                     sendBroadcastToDiscoverThing(socket, interfaceAddress.getBroadcast());
92                 }
93             }
94
95         } catch (IOException e) {
96             logger.debug("No Danfoss Air CCM device found. Diagnostic: {}", e.getMessage());
97         }
98     }
99
100     private void sendBroadcastToDiscoverThing(DatagramSocket socket, InetAddress broadcastAddress) throws IOException {
101         socket.setBroadcast(true);
102         socket.setSoTimeout(500);
103         // send discover
104         byte[] sendBuffer = DISCOVER_SEND;
105         DatagramPacket sendPacket = new DatagramPacket(sendBuffer, sendBuffer.length, broadcastAddress, BROADCAST_PORT);
106         socket.send(sendPacket);
107         logger.debug("Discover message sent");
108
109         // wait for responses
110         while (true) {
111             byte[] receiveBuffer = new byte[7];
112             DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, receiveBuffer.length);
113             try {
114                 socket.receive(receivePacket);
115             } catch (SocketTimeoutException e) {
116                 break; // leave the endless loop
117             }
118
119             byte[] data = receivePacket.getData();
120             if (Arrays.equals(data, DISCOVER_RECEIVE)) {
121                 logger.debug("Discover received correct response");
122
123                 String host = receivePacket.getAddress().getHostName();
124                 Map<String, Object> properties = new HashMap<>();
125                 properties.put("host", host);
126
127                 logger.debug("Adding a new Danfoss Air Unit CCM '{}' to inbox", host);
128
129                 ThingUID uid = new ThingUID(THING_TYPE_AIRUNIT, String.valueOf(receivePacket.getAddress().hashCode()));
130
131                 DiscoveryResult result = DiscoveryResultBuilder.create(uid).withRepresentationProperty("host")
132                         .withProperties(properties).withLabel("Danfoss HRV").build();
133                 thingDiscovered(result);
134
135                 logger.debug("Thing discovered '{}'", result);
136             }
137         }
138     }
139 }