2 * Copyright (c) 2010-2020 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.avmfritz.internal.callmonitor;
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;
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;
35 * This class handles all communication with the call monitor port of the fritzbox.
37 * @author Kai Kreuzer - Initial contribution
40 public class CallMonitor {
42 protected final Logger logger = LoggerFactory.getLogger(CallMonitor.class);
44 // port number to connect to fritzbox
45 private final int MONITOR_PORT = 1012;
47 private @Nullable CallMonitorThread monitorThread;
48 private ScheduledFuture<?> reconnectJob;
51 private BoxHandler handler;
53 public CallMonitor(String ip, BoxHandler handler, ScheduledExecutorService scheduler) {
55 this.handler = handler;
56 reconnectJob = scheduler.scheduleWithFixedDelay(() -> {
59 // Wait before reconnect
62 } catch (InterruptedException e) {
65 // create a new thread for listening to the FritzBox
66 CallMonitorThread thread = new CallMonitorThread();
67 thread.setName("OH-binding-" + handler.getThing().getUID().getAsString());
69 this.monitorThread = thread;
70 }, 0, 2, TimeUnit.HOURS);
74 * Cancel the reconnect job.
76 public void dispose() {
77 reconnectJob.cancel(true);
80 public class CallMonitorThread extends Thread {
83 private @Nullable Socket socket;
85 // Thread control flag
86 private boolean interrupted = false;
88 // time to wait before reconnecting
89 private long reconnectTime = 60000L;
91 public CallMonitorThread() {
96 while (!interrupted) {
97 BufferedReader reader = null;
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);
111 Thread.sleep(reconnectTime);
112 } catch (InterruptedException ex) {
115 // wait another more minute the next time
116 reconnectTime += 60000L;
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) {
123 String line = reader.readLine();
125 logger.debug("Received raw call string from fbox: {}", line);
126 CallEvent ce = new CallEvent(line);
129 } catch (IOException e) {
131 logger.debug("Lost connection to Fritzbox because of an interrupt.");
133 handler.setStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
134 "Lost connection to Fritz!Box: " + e.getMessage());
140 } catch (InterruptedException e) {
149 * Close socket and stop running thread.
152 public void interrupt() {
154 if (socket != null) {
157 logger.debug("Socket to FritzBox closed.");
158 } catch (IOException e) {
159 logger.warn("Failed to close connection to FritzBox.", e);
162 logger.debug("Socket to FritzBox not open, therefore not closing it.");
167 * Handle call event and update item as required.
169 * @param ce call event to process
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);
204 public void stopThread() {
205 logger.debug("Stopping call monitor thread...");
206 if (monitorThread != null) {
207 monitorThread.interrupt();
208 monitorThread = null;
212 public void startThread() {
213 logger.debug("Starting call monitor thread...");
214 if (monitorThread != null) {
215 monitorThread.interrupt();
216 monitorThread = null;
218 // create a new thread for listening to the FritzBox
219 monitorThread = new CallMonitorThread();
220 monitorThread.start();