2 * Copyright (c) 2010-2023 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.echonetlite.internal;
15 import java.io.IOException;
16 import java.net.Inet4Address;
17 import java.net.InetSocketAddress;
18 import java.net.NetworkInterface;
19 import java.net.SocketAddress;
20 import java.net.StandardProtocolFamily;
21 import java.nio.ByteBuffer;
22 import java.nio.channels.DatagramChannel;
23 import java.nio.channels.SelectionKey;
24 import java.nio.channels.Selector;
25 import java.util.Enumeration;
26 import java.util.function.BiConsumer;
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
33 * Wraps a Datagram channel for sending/receiving data to/from echonet lite devices.
35 * @author Michael Barker - Initial contribution
38 public class EchonetChannel {
40 private final Logger logger = LoggerFactory.getLogger(EchonetChannel.class);
42 private final DatagramChannel channel;
43 private final Selector selector = Selector.open();
45 private short tid = 0;
47 public EchonetChannel(InetSocketAddress discoveryAddress) throws IOException {
48 channel = DatagramChannel.open(StandardProtocolFamily.INET);
49 channel.bind(new InetSocketAddress("0.0.0.0", discoveryAddress.getPort()));
50 final Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
51 while (networkInterfaces.hasMoreElements()) {
52 final NetworkInterface networkInterface = (NetworkInterface) networkInterfaces.nextElement();
53 if (networkInterface.supportsMulticast() && hasIpV4Address(networkInterface)) {
54 channel.join(discoveryAddress.getAddress(), networkInterface);
57 channel.configureBlocking(false);
58 channel.register(selector, SelectionKey.OP_READ);
61 private boolean hasIpV4Address(final NetworkInterface networkInterface) {
62 return networkInterface.inetAddresses().anyMatch(ia -> ia instanceof Inet4Address);
67 logger.debug("closing selector");
69 logger.debug("closing channel");
71 } catch (IOException ignore) {
79 public void sendMessage(EchonetMessageBuilder messageBuilder) throws IOException {
80 messageBuilder.buffer().flip();
81 channel.send(messageBuilder.buffer(), messageBuilder.address());
84 public void pollMessages(EchonetMessage echonetMessage, BiConsumer<EchonetMessage, SocketAddress> consumer,
85 final long timeout) throws IOException {
86 selector.select(selectionKey -> {
87 final DatagramChannel channel = (DatagramChannel) selectionKey.channel();
89 final ByteBuffer buffer = echonetMessage.bufferForRead();
90 final SocketAddress address = channel.receive(buffer);
92 echonetMessage.sourceAddress(address);
94 long t0 = System.currentTimeMillis();
95 consumer.accept(echonetMessage, address);
96 long t1 = System.currentTimeMillis();
97 final long processingTimeMs = t1 - t0;
98 if (500 < processingTimeMs) {
99 logger.debug("Message took {}ms to process", processingTimeMs);
101 } catch (IOException e) {
102 logger.warn("Failed to receive on channel", e);