]> git.basschouten.com Git - openhab-addons.git/blob
25b7163608512bb9dad6671a89f1ff6dd030a7af
[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.insteon.internal.handler;
14
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.concurrent.ConcurrentHashMap;
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.insteon.internal.InsteonBinding;
26 import org.openhab.binding.insteon.internal.config.InsteonNetworkConfiguration;
27 import org.openhab.binding.insteon.internal.discovery.InsteonDeviceDiscoveryService;
28 import org.openhab.core.io.console.Console;
29 import org.openhab.core.io.transport.serial.SerialPortManager;
30 import org.openhab.core.thing.Bridge;
31 import org.openhab.core.thing.ChannelUID;
32 import org.openhab.core.thing.ThingStatus;
33 import org.openhab.core.thing.ThingStatusDetail;
34 import org.openhab.core.thing.ThingUID;
35 import org.openhab.core.thing.binding.BaseBridgeHandler;
36 import org.openhab.core.types.Command;
37 import org.openhab.core.types.State;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 /**
42  * The {@link InsteonNetworkHandler} is responsible for handling commands, which are
43  * sent to one of the channels.
44  *
45  * @author Rob Nielsen - Initial contribution
46  */
47 @NonNullByDefault
48 @SuppressWarnings("null")
49 public class InsteonNetworkHandler extends BaseBridgeHandler {
50     private static final int LOG_DEVICE_STATISTICS_DELAY_IN_SECONDS = 600;
51     private static final int RETRY_DELAY_IN_SECONDS = 30;
52     private static final int SETTLE_TIME_IN_SECONDS = 5;
53
54     private final Logger logger = LoggerFactory.getLogger(InsteonNetworkHandler.class);
55
56     private @Nullable InsteonNetworkConfiguration config;
57     private @Nullable InsteonBinding insteonBinding;
58     private @Nullable InsteonDeviceDiscoveryService insteonDeviceDiscoveryService;
59     private @Nullable ScheduledFuture<?> pollingJob = null;
60     private @Nullable ScheduledFuture<?> reconnectJob = null;
61     private @Nullable ScheduledFuture<?> settleJob = null;
62     private long lastInsteonDeviceCreatedTimestamp = 0;
63     private @Nullable SerialPortManager serialPortManager;
64     private Map<String, String> deviceInfo = new ConcurrentHashMap<>();
65     private Map<String, String> channelInfo = new ConcurrentHashMap<>();
66
67     public static ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
68
69     public InsteonNetworkHandler(Bridge bridge, @Nullable SerialPortManager serialPortManager) {
70         super(bridge);
71         this.serialPortManager = serialPortManager;
72     }
73
74     @Override
75     public void handleCommand(ChannelUID channelUID, Command command) {
76     }
77
78     @Override
79     public void initialize() {
80         logger.debug("Starting Insteon bridge");
81         config = getConfigAs(InsteonNetworkConfiguration.class);
82         updateStatus(ThingStatus.UNKNOWN);
83
84         scheduler.execute(() -> {
85             insteonBinding = new InsteonBinding(this, config, serialPortManager, scheduler);
86
87             // hold off on starting to poll until devices that already are defined as things are added.
88             // wait SETTLE_TIME_IN_SECONDS to start then check every second afterwards until it has been at
89             // least SETTLE_TIME_IN_SECONDS since last device was created.
90             settleJob = scheduler.scheduleWithFixedDelay(() -> {
91                 // check to see if it has been at least SETTLE_TIME_IN_SECONDS since last device was created
92                 if (System.currentTimeMillis() - lastInsteonDeviceCreatedTimestamp > SETTLE_TIME_IN_SECONDS * 1000) {
93                     // settle time has expired start polling
94                     if (insteonBinding.startPolling()) {
95                         pollingJob = scheduler.scheduleWithFixedDelay(() -> {
96                             insteonBinding.logDeviceStatistics();
97                         }, 0, LOG_DEVICE_STATISTICS_DELAY_IN_SECONDS, TimeUnit.SECONDS);
98
99                         insteonBinding.setIsActive(true);
100
101                         updateStatus(ThingStatus.ONLINE);
102                     } else {
103                         String msg = "Initialization failed, unable to start the Insteon bridge with the port '"
104                                 + config.getPort() + "'.";
105                         logger.warn(msg);
106
107                         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, msg);
108                     }
109
110                     settleJob.cancel(false);
111                     settleJob = null;
112                 }
113             }, SETTLE_TIME_IN_SECONDS, 1, TimeUnit.SECONDS);
114         });
115     }
116
117     @Override
118     public void dispose() {
119         logger.debug("Shutting down Insteon bridge");
120
121         if (pollingJob != null) {
122             pollingJob.cancel(true);
123             pollingJob = null;
124         }
125
126         if (reconnectJob != null) {
127             reconnectJob.cancel(true);
128             reconnectJob = null;
129         }
130
131         if (settleJob != null) {
132             settleJob.cancel(true);
133             settleJob = null;
134         }
135
136         if (insteonBinding != null) {
137             insteonBinding.shutdown();
138             insteonBinding = null;
139         }
140
141         deviceInfo.clear();
142         channelInfo.clear();
143
144         super.dispose();
145     }
146
147     @Override
148     public void updateState(ChannelUID channelUID, State state) {
149         super.updateState(channelUID, state);
150     }
151
152     public void bindingDisconnected() {
153         reconnectJob = scheduler.scheduleWithFixedDelay(() -> {
154             if (insteonBinding.reconnect()) {
155                 updateStatus(ThingStatus.ONLINE);
156                 reconnectJob.cancel(false);
157                 reconnectJob = null;
158             } else {
159                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Port disconnected.");
160             }
161         }, 0, RETRY_DELAY_IN_SECONDS, TimeUnit.SECONDS);
162     }
163
164     public void insteonDeviceWasCreated() {
165         lastInsteonDeviceCreatedTimestamp = System.currentTimeMillis();
166     }
167
168     public @Nullable InsteonBinding getInsteonBinding() {
169         return insteonBinding;
170     }
171
172     public void setInsteonDeviceDiscoveryService(InsteonDeviceDiscoveryService insteonDeviceDiscoveryService) {
173         this.insteonDeviceDiscoveryService = insteonDeviceDiscoveryService;
174     }
175
176     public void addMissingDevices(List<String> missing) {
177         scheduler.execute(() -> {
178             insteonDeviceDiscoveryService.addInsteonDevices(missing, getThing().getUID());
179         });
180     }
181
182     public void displayDevices(Console console) {
183         display(console, deviceInfo);
184     }
185
186     public void displayChannels(Console console) {
187         display(console, channelInfo);
188     }
189
190     public void displayLocalDatabase(Console console) {
191         Map<String, String> databaseInfo = insteonBinding.getDatabaseInfo();
192         console.println("local database contains " + databaseInfo.size() + " entries");
193         display(console, databaseInfo);
194     }
195
196     public void initialized(ThingUID uid, String msg) {
197         deviceInfo.put(uid.getAsString(), msg);
198     }
199
200     public void disposed(ThingUID uid) {
201         deviceInfo.remove(uid.getAsString());
202     }
203
204     public boolean isChannelLinked(ChannelUID uid) {
205         return channelInfo.containsKey(uid.getAsString());
206     }
207
208     public void linked(ChannelUID uid, String msg) {
209         channelInfo.put(uid.getAsString(), msg);
210     }
211
212     public void unlinked(ChannelUID uid) {
213         channelInfo.remove(uid.getAsString());
214     }
215
216     private void display(Console console, Map<String, String> info) {
217         ArrayList<String> ids = new ArrayList<>(info.keySet());
218         Collections.sort(ids);
219         for (String id : ids) {
220             console.println(info.get(id));
221         }
222     }
223 }