]> git.basschouten.com Git - openhab-addons.git/blob
55707339f188412bf484261e12f8891bf33a07d5
[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.avmfritz.internal.callmonitor;
14
15 import java.io.BufferedReader;
16 import java.io.IOException;
17 import java.io.InputStreamReader;
18 import java.net.Socket;
19 import java.util.concurrent.ScheduledExecutorService;
20 import java.util.concurrent.ScheduledFuture;
21 import java.util.concurrent.TimeUnit;
22
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.binding.avmfritz.internal.AVMFritzBindingConstants;
26 import org.openhab.binding.avmfritz.internal.handler.BoxHandler;
27 import org.openhab.core.library.types.StringListType;
28 import org.openhab.core.thing.ThingStatus;
29 import org.openhab.core.thing.ThingStatusDetail;
30 import org.openhab.core.types.UnDefType;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 /**
35  * This class handles all communication with the call monitor port of the fritzbox.
36  *
37  * @author Kai Kreuzer - Initial contribution
38  */
39 @NonNullByDefault
40 public class CallMonitor {
41
42     protected final Logger logger = LoggerFactory.getLogger(CallMonitor.class);
43
44     // port number to connect to fritzbox
45     private final int MONITOR_PORT = 1012;
46
47     private @Nullable CallMonitorThread monitorThread;
48     private ScheduledFuture<?> reconnectJob;
49
50     private String ip;
51     private BoxHandler handler;
52
53     public CallMonitor(String ip, BoxHandler handler, ScheduledExecutorService scheduler) {
54         this.ip = ip;
55         this.handler = handler;
56         reconnectJob = scheduler.scheduleWithFixedDelay(() -> {
57             stopThread();
58
59             // Wait before reconnect
60             try {
61                 Thread.sleep(5000L);
62             } catch (InterruptedException e) {
63             }
64
65             // create a new thread for listening to the FritzBox
66             CallMonitorThread thread = new CallMonitorThread();
67             thread.setName("OH-binding-" + handler.getThing().getUID().getAsString());
68             thread.start();
69             this.monitorThread = thread;
70         }, 0, 2, TimeUnit.HOURS);
71     }
72
73     /**
74      * Cancel the reconnect job.
75      */
76     public void dispose() {
77         reconnectJob.cancel(true);
78     }
79
80     public class CallMonitorThread extends Thread {
81
82         // Socket to connect
83         private @Nullable Socket socket;
84
85         // Thread control flag
86         private boolean interrupted = false;
87
88         // time to wait before reconnecting
89         private long reconnectTime = 60000L;
90
91         public CallMonitorThread() {
92         }
93
94         @Override
95         public void run() {
96             while (!interrupted) {
97                 BufferedReader reader = null;
98                 try {
99                     logger.debug("Callmonitor thread [{}] attempting connection to FritzBox on {}:{}.",
100                             Thread.currentThread().getId(), ip, MONITOR_PORT);
101                     socket = new Socket(ip, MONITOR_PORT);
102                     reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
103                     // reset the retry interval
104                     reconnectTime = 60000L;
105                 } catch (Exception e) {
106                     handler.setStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
107                             "Cannot connect to Fritz!Box call monitor - make sure to enable it by dialing '#96*5'!");
108                     logger.debug("Error attempting to connect to FritzBox. Retrying in {} seconds",
109                             reconnectTime / 1000L, e);
110                     try {
111                         Thread.sleep(reconnectTime);
112                     } catch (InterruptedException ex) {
113                         interrupted = true;
114                     }
115                     // wait another more minute the next time
116                     reconnectTime += 60000L;
117                 }
118                 if (reader != null) {
119                     logger.debug("Connected to FritzBox call monitor at {}:{}.", ip, MONITOR_PORT);
120                     handler.setStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, null);
121                     while (!interrupted) {
122                         try {
123                             String line = reader.readLine();
124                             if (line != null) {
125                                 logger.debug("Received raw call string from fbox: {}", line);
126                                 CallEvent ce = new CallEvent(line);
127                                 handleCallEvent(ce);
128                             }
129                         } catch (IOException e) {
130                             if (interrupted) {
131                                 logger.debug("Lost connection to Fritzbox because of an interrupt.");
132                             } else {
133                                 handler.setStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
134                                         "Lost connection to Fritz!Box: " + e.getMessage());
135                             }
136                             break;
137                         } finally {
138                             try {
139                                 sleep(1000L);
140                             } catch (InterruptedException e) {
141                             }
142                         }
143                     }
144                 }
145             }
146         }
147
148         /**
149          * Close socket and stop running thread.
150          */
151         @Override
152         public void interrupt() {
153             interrupted = true;
154             if (socket != null) {
155                 try {
156                     socket.close();
157                     logger.debug("Socket to FritzBox closed.");
158                 } catch (IOException e) {
159                     logger.warn("Failed to close connection to FritzBox.", e);
160                 }
161             } else {
162                 logger.debug("Socket to FritzBox not open, therefore not closing it.");
163             }
164         }
165
166         /**
167          * Handle call event and update item as required.
168          *
169          * @param ce call event to process
170          */
171         private void handleCallEvent(CallEvent ce) {
172             if (ce.getCallType().equals("DISCONNECT")) {
173                 // reset states of call monitor channels
174                 handler.updateState(AVMFritzBindingConstants.CHANNEL_CALL_INCOMING, UnDefType.UNDEF);
175                 handler.updateState(AVMFritzBindingConstants.CHANNEL_CALL_OUTGOING, UnDefType.UNDEF);
176                 handler.updateState(AVMFritzBindingConstants.CHANNEL_CALL_ACTIVE, UnDefType.UNDEF);
177                 handler.updateState(AVMFritzBindingConstants.CHANNEL_CALL_STATE,
178                         AVMFritzBindingConstants.CALL_STATE_IDLE);
179             } else if (ce.getCallType().equals("RING")) { // first event when call is incoming
180                 StringListType state = new StringListType(ce.getInternalNo(), ce.getExternalNo());
181                 handler.updateState(AVMFritzBindingConstants.CHANNEL_CALL_INCOMING, state);
182                 handler.updateState(AVMFritzBindingConstants.CHANNEL_CALL_OUTGOING, UnDefType.UNDEF);
183                 handler.updateState(AVMFritzBindingConstants.CHANNEL_CALL_ACTIVE, UnDefType.UNDEF);
184                 handler.updateState(AVMFritzBindingConstants.CHANNEL_CALL_STATE,
185                         AVMFritzBindingConstants.CALL_STATE_RINGING);
186             } else if (ce.getCallType().equals("CONNECT")) { // when call is answered/running
187                 StringListType state = new StringListType(ce.getExternalNo(), "");
188                 handler.updateState(AVMFritzBindingConstants.CHANNEL_CALL_ACTIVE, state);
189                 handler.updateState(AVMFritzBindingConstants.CHANNEL_CALL_INCOMING, UnDefType.UNDEF);
190                 handler.updateState(AVMFritzBindingConstants.CHANNEL_CALL_OUTGOING, UnDefType.UNDEF);
191                 handler.updateState(AVMFritzBindingConstants.CHANNEL_CALL_STATE,
192                         AVMFritzBindingConstants.CALL_STATE_ACTIVE);
193             } else if (ce.getCallType().equals("CALL")) { // outgoing call
194                 StringListType state = new StringListType(ce.getExternalNo(), ce.getInternalNo());
195                 handler.updateState(AVMFritzBindingConstants.CHANNEL_CALL_INCOMING, UnDefType.UNDEF);
196                 handler.updateState(AVMFritzBindingConstants.CHANNEL_CALL_OUTGOING, state);
197                 handler.updateState(AVMFritzBindingConstants.CHANNEL_CALL_ACTIVE, UnDefType.UNDEF);
198                 handler.updateState(AVMFritzBindingConstants.CHANNEL_CALL_STATE,
199                         AVMFritzBindingConstants.CALL_STATE_DIALING);
200             }
201         }
202     }
203
204     public void stopThread() {
205         logger.debug("Stopping call monitor thread...");
206         if (monitorThread != null) {
207             monitorThread.interrupt();
208             monitorThread = null;
209         }
210     }
211
212     public void startThread() {
213         logger.debug("Starting call monitor thread...");
214         if (monitorThread != null) {
215             monitorThread.interrupt();
216             monitorThread = null;
217         }
218         // create a new thread for listening to the FritzBox
219         monitorThread = new CallMonitorThread();
220         monitorThread.start();
221     }
222 }