]> git.basschouten.com Git - openhab-addons.git/blob
976c617712c004083b06e09dfd11a08628fb21e2
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.smaenergymeter.internal.packet;
14
15 import java.io.IOException;
16 import java.net.DatagramPacket;
17 import java.net.DatagramSocket;
18 import java.net.InetAddress;
19 import java.net.MulticastSocket;
20 import java.util.List;
21 import java.util.concurrent.CopyOnWriteArrayList;
22 import java.util.concurrent.ScheduledFuture;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.binding.smaenergymeter.internal.handler.EnergyMeter;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 /**
31  * The {@link PacketListener} class is responsible for communication with the SMA devices.
32  * It handles udp/multicast traffic and broadcast received data to subsequent payload handlers.
33  *
34  * @author Ćukasz Dywicki - Initial contribution
35  */
36
37 @NonNullByDefault
38 public class PacketListener {
39
40     private final DefaultPacketListenerRegistry registry;
41     private final List<PayloadHandler> handlers = new CopyOnWriteArrayList<>();
42
43     private String multicastGroup;
44     private int port;
45
46     public static final String DEFAULT_MCAST_GRP = "239.12.255.254";
47     public static final int DEFAULT_MCAST_PORT = 9522;
48
49     private @Nullable MulticastSocket socket;
50     private @Nullable ScheduledFuture<?> future;
51
52     public PacketListener(DefaultPacketListenerRegistry registry, String multicastGroup, int port) {
53         this.registry = registry;
54         this.multicastGroup = multicastGroup;
55         this.port = port;
56     }
57
58     public void addPayloadHandler(PayloadHandler handler) {
59         handlers.add(handler);
60     }
61
62     public void removePayloadHandler(PayloadHandler handler) {
63         handlers.remove(handler);
64
65         if (handlers.isEmpty()) {
66             registry.close(multicastGroup, port);
67         }
68     }
69
70     public boolean isOpen() {
71         MulticastSocket socket = this.socket;
72         return socket != null && socket.isConnected();
73     }
74
75     public void open(int intervalSec) throws IOException {
76         if (isOpen()) {
77             // no need to bind socket second time
78             return;
79         }
80         MulticastSocket socket = new MulticastSocket(port);
81         socket.setSoTimeout(5000);
82         InetAddress address = InetAddress.getByName(multicastGroup);
83         socket.joinGroup(address);
84
85         future = registry.addTask(new ReceivingTask(socket, multicastGroup + ":" + port, handlers), intervalSec);
86         this.socket = socket;
87     }
88
89     void close() throws IOException {
90         ScheduledFuture<?> future = this.future;
91         if (future != null) {
92             future.cancel(true);
93             this.future = null;
94         }
95
96         InetAddress address = InetAddress.getByName(multicastGroup);
97         MulticastSocket socket = this.socket;
98         if (socket != null) {
99             socket.leaveGroup(address);
100             socket.close();
101             this.socket = null;
102         }
103     }
104
105     public void request() {
106         MulticastSocket socket = this.socket;
107         if (socket != null) {
108             registry.execute(new ReceivingTask(socket, multicastGroup + ":" + port, handlers));
109         }
110     }
111
112     static class ReceivingTask implements Runnable {
113         private final Logger logger = LoggerFactory.getLogger(ReceivingTask.class);
114         private final DatagramSocket socket;
115         private final String group;
116         private final List<PayloadHandler> handlers;
117
118         ReceivingTask(DatagramSocket socket, String group, List<PayloadHandler> handlers) {
119             this.socket = socket;
120             this.group = group;
121             this.handlers = handlers;
122         }
123
124         public void run() {
125             try {
126                 byte[] bytes = new byte[608];
127                 DatagramPacket msgPacket = new DatagramPacket(bytes, bytes.length);
128                 DatagramSocket socket = this.socket;
129                 socket.receive(msgPacket);
130
131                 try {
132                     EnergyMeter meter = new EnergyMeter();
133                     meter.parse(bytes);
134
135                     for (PayloadHandler handler : handlers) {
136                         handler.handle(meter);
137                     }
138                 } catch (IOException e) {
139                     logger.debug("Unexpected payload received for group {}", group, e);
140                 }
141             } catch (IOException e) {
142                 logger.warn("Failed to receive data for multicast group {}", group, e);
143             }
144         }
145     }
146 }