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.magentatv.internal.network;
15 import static org.openhab.binding.magentatv.internal.MagentaTVBindingConstants.*;
16 import static org.openhab.binding.magentatv.internal.MagentaTVUtil.*;
18 import java.io.IOException;
19 import java.net.DatagramPacket;
20 import java.net.InetAddress;
21 import java.net.MulticastSocket;
22 import java.net.NetworkInterface;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.binding.magentatv.internal.MagentaTVHandlerFactory;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
31 * The {@link MagentaTVPoweroffListener} implements a UPnP listener to detect
32 * power-off of the receiver
34 * @author Markus Michels - Initial contribution
37 public class MagentaTVPoweroffListener extends Thread {
38 private final Logger logger = LoggerFactory.getLogger(MagentaTVPoweroffListener.class);
40 private final MagentaTVHandlerFactory handlerFactory;
42 public static final String UPNP_MULTICAST_ADDRESS = "239.255.255.250";
43 public static final int UPNP_PORT = 1900;
44 public static final String UPNP_BYEBYE_MESSAGE = "ssdp:byebye";
46 protected final MulticastSocket socket;
47 protected @Nullable NetworkInterface networkInterface;
48 protected byte[] buf = new byte[256];
49 private boolean started = false;
51 public MagentaTVPoweroffListener(MagentaTVHandlerFactory handlerFactory,
52 @Nullable NetworkInterface networkInterface) throws IOException {
53 setName("OH-Binding-magentatv-upnp-listener");
56 this.handlerFactory = handlerFactory;
57 this.networkInterface = networkInterface;
58 socket = new MulticastSocket(UPNP_PORT);
64 logger.debug("Listening to SSDP shutdown messages");
71 * Listening thread. Receive SSDP multicast packets and filter for byebye If
72 * such a packet is received the handlerFactory is called, which then dispatches
73 * the event to the thing handler.
78 logger.debug("SSDP listener started");
79 socket.setReceiveBufferSize(1024);
80 socket.setReuseAddress(true);
82 // Join the Multicast group on the selected network interface
83 socket.setNetworkInterface(networkInterface);
84 InetAddress group = InetAddress.getByName(UPNP_MULTICAST_ADDRESS);
85 socket.joinGroup(group);
87 // read the SSDP messages
88 while (!socket.isClosed()) {
89 DatagramPacket packet = new DatagramPacket(buf, buf.length);
90 socket.receive(packet);
91 String message = new String(packet.getData(), 0, packet.getLength());
93 String ipAddress = substringAfter(packet.getAddress().toString(), "/");
94 if (message.contains("NTS: ")) {
95 String ssdpMsg = substringBetween(message, "NTS: ", "\r");
96 if (ssdpMsg != null) {
97 if (message.contains(MR400_DEF_DESCRIPTION_URL)
98 || message.contains(MR401B_DEF_DESCRIPTION_URL)) {
99 if (ssdpMsg.contains(UPNP_BYEBYE_MESSAGE)) {
100 handlerFactory.onPowerOff(ipAddress);
105 } catch (RuntimeException e) {
106 logger.debug("Unable to process SSDP message: {}", message);
109 } catch (IOException | RuntimeException e) {
110 logger.debug("Poweroff listener failure: {}", e.getMessage());
116 public boolean isStarted() {
121 * Make sure the socket gets closed
123 public void close() {
124 if (started) { // if (isStarted()) {
125 logger.debug("No longer listening to SSDP messages");
126 if (!socket.isClosed()) {
134 * Make sure the socket gets closed
136 public void dispose() {
137 logger.debug("SSDP listener terminated");