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