2 * Copyright (c) 2010-2022 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.dominoswiss.internal;
15 import static org.openhab.binding.dominoswiss.internal.DominoswissBindingConstants.*;
17 import java.io.BufferedReader;
18 import java.io.BufferedWriter;
19 import java.io.IOException;
20 import java.io.InputStreamReader;
21 import java.io.OutputStreamWriter;
22 import java.net.InetSocketAddress;
23 import java.net.Socket;
24 import java.util.HashMap;
25 import java.util.List;
27 import java.util.concurrent.Future;
28 import java.util.concurrent.ScheduledFuture;
29 import java.util.concurrent.TimeUnit;
31 import org.eclipse.jdt.annotation.NonNullByDefault;
32 import org.eclipse.jdt.annotation.Nullable;
33 import org.openhab.core.thing.Bridge;
34 import org.openhab.core.thing.ChannelUID;
35 import org.openhab.core.thing.Thing;
36 import org.openhab.core.thing.ThingStatus;
37 import org.openhab.core.thing.ThingStatusDetail;
38 import org.openhab.core.thing.ThingUID;
39 import org.openhab.core.thing.binding.BaseBridgeHandler;
40 import org.openhab.core.types.Command;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
45 * The {@link EgateHandler} is responsible for handling commands, which are
46 * sent to one of the channels.
48 * @author Frieso Aeschbacher - Initial contribution
51 public class EGateHandler extends BaseBridgeHandler {
53 private final Logger logger = LoggerFactory.getLogger(EGateHandler.class);
54 private @Nullable Socket egateSocket;
57 private @Nullable String host;
58 private static final int SOCKET_TIMEOUT_SEC = 250;
59 private final Object lock = new Object();
60 private @Nullable BufferedWriter writer;
61 private @Nullable BufferedReader reader;
62 private @Nullable Future<?> refreshJob;
63 private Map<String, ThingUID> registeredBlinds;
64 private @Nullable ScheduledFuture<?> pollingJob;
66 public EGateHandler(Bridge thing) {
68 registeredBlinds = new HashMap<String, ThingUID>();
72 public void handleCommand(ChannelUID channelUID, Command command) {
73 if (channelUID.getId().equals(GETCONFIG)) {
74 sendCommand("EthernetGet;\r");
79 public void initialize() {
80 DominoswissConfiguration config;
81 config = this.getConfigAs(DominoswissConfiguration.class);
82 host = config.ipAddress;
85 if (host != null && port > 0) {
86 // Create a socket to eGate
87 try (Socket localEgateSocket = new Socket(host, port)) {
88 writer = new BufferedWriter(new OutputStreamWriter(localEgateSocket.getOutputStream()));
89 egateSocket = localEgateSocket;
90 } catch (IOException e) {
91 logger.debug("IOException in initialize: {} host {} port {}", e.toString(), host, port);
92 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.toString());
95 pollingJob = scheduler.scheduleWithFixedDelay(this::pollingConfig, 0, 30, TimeUnit.SECONDS);
97 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
98 "Cannot connect to dominoswiss eGate gateway. host IP address or port are not set.");
103 public void dispose() {
105 Socket localEgateSocket = egateSocket;
106 if (localEgateSocket != null) {
107 localEgateSocket.close();
109 Future<?> localRefreshJob = refreshJob;
110 if (localRefreshJob != null) {
111 localRefreshJob.cancel(true);
113 BufferedReader localReader = reader;
114 if (localReader != null) {
118 BufferedWriter localWriter = writer;
119 if (localWriter != null) {
122 ScheduledFuture<?> localPollingJob = pollingJob;
123 if (localPollingJob != null) {
124 localPollingJob.cancel(true);
125 localPollingJob = null;
127 logger.debug("EGate Handler connection closed, disposing");
128 } catch (IOException e) {
129 logger.debug("EGate Handler Error on dispose: {} ", e.toString());
133 public synchronized boolean isConnected() {
134 Socket localEGateSocket = egateSocket;
135 if (localEGateSocket == null) {
139 // NOTE: isConnected() returns true once a connection is made and will
140 // always return true even after the socket is closed
141 // http://stackoverflow.com/questions/10163358/
142 return localEGateSocket.isConnected() && !localEGateSocket.isClosed();
146 * Possible Instructions are:
147 * FssTransmit 1 Kommandoabsetzung (Controller > eGate > Dominoswiss)
148 * FssReceive 2 Empfangenes Funkpaket (Dominoswiss > eGate > Controller)
150 * @throws InterruptedException
154 public void tiltUp(String id) throws InterruptedException {
155 for (int i = 0; i < 3; i++) {
157 Thread.sleep(150); // sleep to not confuse the blinds
162 public void tiltDown(String id) throws InterruptedException {
163 for (int i = 0; i < 3; i++) {
165 Thread.sleep(150);// sleep to not confuse the blinds
169 public void pulseUp(String id) {
170 sendCommand("Instruction=1;ID=" + id + ";Command=1;Priority=1;CheckNr=3415347;" + CR);
173 public void pulseDown(String id) {
174 sendCommand("Instruction=1;ID=" + id + ";Command=2;Priority=1;CheckNr=2764516;" + CR);
177 public void continuousUp(String id) {
178 sendCommand("Instruction=1;ID=" + id + ";Command=3;Priority=1;CheckNr=2867016;" + CR, 20000);
181 public void continuousDown(String id) {
182 sendCommand("Instruction=1;ID=" + id + ";Command=4;Priority=1;CheckNr=973898;" + CR, 20000);
185 public void stop(String id) {
186 sendCommand("Instruction=1;ID=" + id + ";Command=5;Priority=1;CheckNr=5408219;" + CR);
189 public void registerBlind(String id, ThingUID uid) {
190 logger.debug("Registring Blind id {} with thingUID {}", id, uid);
191 registeredBlinds.put(id, uid);
195 * Send a command to the eGate Server.
198 private void sendCommand(String command) {
199 sendCommand(command, SOCKET_TIMEOUT_SEC);
202 private synchronized void sendCommand(String command, int timeout) {
203 logger.debug("EGate got command: {}", command);
204 Socket localEGateSocket = egateSocket;
205 BufferedWriter localWriter = writer;
206 if (localEGateSocket == null || localWriter == null) {
209 if (!isConnected()) {
210 logger.debug("no connection to Dominoswiss eGate server when trying to send command, returning...");
214 // Send plain string to eGate Server,
216 localEGateSocket.setSoTimeout(SOCKET_TIMEOUT_SEC);
217 localWriter.write(command);
219 } catch (IOException e) {
220 logger.debug("Error while sending command {} to Dominoswiss eGate Server {} ", command, e.toString());
224 private void pollingConfig() {
225 if (!isConnected()) {
226 Socket localEGateSocket = egateSocket;
227 BufferedWriter localWriter = writer;
228 if (localEGateSocket == null || localWriter == null) {
231 synchronized (lock) {
233 localEGateSocket.connect(new InetSocketAddress(host, port));
234 localEGateSocket.setSoTimeout(SOCKET_TIMEOUT_SEC);
235 localWriter.write("SilenceModeSet;Value=0;" + CR);
237 } catch (IOException e) {
238 logger.debug("IOException in pollingConfig: {} host {} port {}", e.toString(), host, port);
240 localEGateSocket.close();
242 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.toString());
243 } catch (IOException e1) {
244 logger.debug("EGate Socket not closed {}", e1.toString());
248 if (egateSocket != null) {
249 updateStatus(ThingStatus.ONLINE);
250 startAutomaticRefresh();
251 logger.debug("EGate Handler started automatic refresh, status: {} ",
252 getThing().getStatus().toString());
254 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
260 private void startAutomaticRefresh() {
261 Runnable runnable = () -> {
264 Socket localSocket = egateSocket;
265 if (localSocket == null) {
268 BufferedReader localReader = reader;
269 if (localReader == null) {
270 reader = new BufferedReader(new InputStreamReader(localSocket.getInputStream()));
272 if (localReader != null && localReader.ready()) {
273 String input = localReader.readLine();
274 logger.debug("Reader got from EGATE: {}", input);
277 } catch (IOException e) {
278 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
279 "Error while reading command from Dominoswiss eGate Server " + e.toString());
282 refreshJob = scheduler.submit(runnable);
286 * Finds and returns a child thing for a given UID of this bridge.
288 * @param uid uid of the child thing
289 * @return child thing with the given uid or null if thing was not found
291 public @Nullable Thing getThingByUID(ThingUID uid) {
292 Bridge bridge = getThing();
294 List<Thing> things = bridge.getThings();
296 for (Thing thing : things) {
297 if (thing.getUID().equals(uid)) {
305 protected void onData(String input) {
306 // Instruction=2;ID=19;Command=1;Value=0;Priority=0;
307 Map<String, String> map = new HashMap<String, String>();
309 String[] parts = input.split(";");
310 if (parts.length >= 2) {
311 for (int i = 0; i < parts.length; i += 2) {
312 map.put(parts[i], parts[i + 1]);