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.yamahamusiccast.internal;
15 import java.io.IOException;
16 import java.net.DatagramPacket;
17 import java.net.DatagramSocket;
18 import java.net.InetSocketAddress;
19 import java.net.SocketException;
20 import java.net.SocketTimeoutException;
21 import java.util.UUID;
22 import java.util.concurrent.ExecutorService;
23 import java.util.concurrent.Executors;
24 import java.util.concurrent.Future;
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.openhab.binding.yamahamusiccast.internal.dto.UdpMessage;
29 import org.openhab.core.common.NamedThreadFactory;
30 import org.openhab.core.thing.Bridge;
31 import org.openhab.core.thing.ChannelUID;
32 import org.openhab.core.thing.Thing;
33 import org.openhab.core.thing.ThingStatus;
34 import org.openhab.core.thing.ThingStatusInfo;
35 import org.openhab.core.thing.binding.BaseBridgeHandler;
36 import org.openhab.core.types.Command;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
40 import com.google.gson.Gson;
43 * The {@link YamahaMusiccastBridgeHandler} is responsible for dispatching UDP events to linked Things.
45 * @author Lennert Coopman - Initial contribution
48 public class YamahaMusiccastBridgeHandler extends BaseBridgeHandler {
49 private Gson gson = new Gson();
50 private final Logger logger = LoggerFactory.getLogger(YamahaMusiccastBridgeHandler.class);
51 private String threadname = getThing().getUID().getAsString();
52 private @Nullable ExecutorService executor;
53 private @Nullable Future<?> eventListenerJob;
54 private static final int UDP_PORT = 41100;
55 private static final int SOCKET_TIMEOUT_MILLISECONDS = 3000;
56 private static final int BUFFER_SIZE = 5120;
57 private @Nullable DatagramSocket socket;
59 private void receivePackets() {
61 DatagramSocket s = new DatagramSocket(null);
62 s.setSoTimeout(SOCKET_TIMEOUT_MILLISECONDS);
63 s.setReuseAddress(true);
64 InetSocketAddress address = new InetSocketAddress(UDP_PORT);
67 logger.trace("UDP Listener got socket on port {} with timeout {}", UDP_PORT, SOCKET_TIMEOUT_MILLISECONDS);
68 } catch (SocketException e) {
69 logger.trace("UDP Listener got SocketException: {}", e.getMessage(), e);
74 DatagramPacket packet = new DatagramPacket(new byte[BUFFER_SIZE], BUFFER_SIZE);
75 DatagramSocket localSocket = socket;
76 while (localSocket != null) {
78 localSocket.receive(packet);
79 String received = new String(packet.getData(), 0, packet.getLength());
80 String trackingID = UUID.randomUUID().toString().replace("-", "").substring(0, 32);
81 logger.trace("Received packet: {} (Tracking: {})", received, trackingID);
82 handleUDPEvent(received, trackingID);
83 } catch (SocketTimeoutException e) {
84 // Nothing to do on socket timeout
85 } catch (IOException e) {
86 logger.trace("UDP Listener got IOException waiting for datagram: {}", e.getMessage());
90 logger.trace("UDP Listener exiting");
93 public YamahaMusiccastBridgeHandler(Bridge bridge) {
98 public void handleCommand(ChannelUID channelUID, Command command) {
102 public void initialize() {
103 updateStatus(ThingStatus.ONLINE);
104 executor = Executors.newSingleThreadExecutor(new NamedThreadFactory(threadname));
105 Future<?> localEventListenerJob = eventListenerJob;
106 ExecutorService localExecutor = executor;
107 if (localEventListenerJob == null || localEventListenerJob.isCancelled()) {
108 if (localExecutor != null) {
109 localEventListenerJob = localExecutor.submit(this::receivePackets);
115 public void dispose() {
117 Future<?> localEventListenerJob = eventListenerJob;
118 ExecutorService localExecutor = executor;
119 if (localEventListenerJob != null) {
120 localEventListenerJob.cancel(true);
121 localEventListenerJob = null;
123 if (localExecutor != null) {
124 localExecutor.shutdownNow();
125 localExecutor = null;
129 public void handleUDPEvent(String json, String trackingID) {
130 String udpDeviceId = "";
131 Bridge bridge = (Bridge) thing;
132 for (Thing thing : bridge.getThings()) {
133 ThingStatusInfo statusInfo = thing.getStatusInfo();
134 switch (statusInfo.getStatus()) {
136 logger.trace("Thing Status: ONLINE - {}", thing.getLabel());
137 YamahaMusiccastHandler handler = (YamahaMusiccastHandler) thing.getHandler();
138 if (handler != null) {
139 logger.trace("UDP: {} - {} ({} - Tracking: {})", json, handler.getDeviceId(), thing.getLabel(),
143 UdpMessage targetObject = gson.fromJson(json, UdpMessage.class);
144 if (targetObject != null) {
145 udpDeviceId = targetObject.getDeviceId();
146 if (udpDeviceId.equals(handler.getDeviceId())) {
147 handler.processUDPEvent(json, trackingID);
153 logger.trace("Thing Status: NOT ONLINE - {} (Tracking: {})", thing.getLabel(), trackingID);