]> git.basschouten.com Git - openhab-addons.git/blob
e4edd15cbcf29ddb7b5f8b1a2d9996acd47cf3ec
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.fineoffsetweatherstation.internal.service;
14
15 import java.io.IOException;
16 import java.io.InputStream;
17 import java.net.Socket;
18 import java.util.Arrays;
19 import java.util.Collection;
20 import java.util.Map;
21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.locks.Lock;
23 import java.util.concurrent.locks.ReentrantLock;
24 import java.util.function.Function;
25
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.openhab.binding.fineoffsetweatherstation.internal.FineOffsetGatewayConfiguration;
29 import org.openhab.binding.fineoffsetweatherstation.internal.Utils;
30 import org.openhab.binding.fineoffsetweatherstation.internal.domain.SensorGatewayBinding;
31 import org.openhab.binding.fineoffsetweatherstation.internal.domain.response.MeasuredValue;
32 import org.openhab.binding.fineoffsetweatherstation.internal.domain.response.SensorDevice;
33 import org.openhab.binding.fineoffsetweatherstation.internal.domain.response.SystemInfo;
34 import org.openhab.binding.fineoffsetweatherstation.internal.handler.ThingStatusListener;
35 import org.openhab.core.thing.ThingStatus;
36 import org.openhab.core.thing.ThingStatusDetail;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 /**
41  * Interface defining the API for querying a gateway device.
42  *
43  * @author Andreas Berger - Initial contribution
44  */
45 @NonNullByDefault
46 public abstract class GatewayQueryService implements AutoCloseable {
47     protected final Logger logger = LoggerFactory.getLogger(this.getClass());
48
49     private static final Lock REQUEST_LOCK = new ReentrantLock();
50
51     private @Nullable Socket socket;
52
53     @Nullable
54     private final ThingStatusListener thingStatusListener;
55
56     private final FineOffsetGatewayConfiguration config;
57
58     @Nullable
59     public abstract String getFirmwareVersion();
60
61     public abstract Map<SensorGatewayBinding, SensorDevice> getRegisteredSensors();
62
63     @Nullable
64     public abstract SystemInfo fetchSystemInfo();
65
66     public abstract Collection<MeasuredValue> getMeasuredValues();
67
68     public GatewayQueryService(FineOffsetGatewayConfiguration config,
69             @Nullable ThingStatusListener thingStatusListener) {
70         this.config = config;
71         this.thingStatusListener = thingStatusListener;
72     }
73
74     protected byte @Nullable [] executeCommand(String command, byte[] request,
75             Function<byte[], Boolean> validateResponse) {
76         try {
77             if (!REQUEST_LOCK.tryLock(30, TimeUnit.SECONDS)) {
78                 logger.debug("executeCommand({}): timed out while getting the lock", command);
79                 return null;
80             }
81         } catch (InterruptedException e) {
82             logger.debug("executeCommand({}): was interrupted while getting the lock", command);
83             return null;
84         }
85
86         byte[] buffer = new byte[2028];
87         int bytesRead;
88
89         try {
90             Socket socket = getConnection();
91             if (socket == null) {
92                 return null;
93             }
94             logger.trace("executeCommand({}): send request: {}", command,
95                     Utils.toHexString(request, request.length, ""));
96             InputStream in = socket.getInputStream();
97             socket.getOutputStream().write(request);
98             if ((bytesRead = in.read(buffer)) == -1) {
99                 logger.trace("executeCommand({}): data exceeded buffer length ({})", command, buffer.length);
100                 return null;
101             }
102             if (!validateResponse.apply(buffer)) {
103                 if (bytesRead > 0) {
104                     logger.debug("executeCommand({}), invalid response: {}", command,
105                             Utils.toHexString(buffer, bytesRead, ""));
106                 } else {
107                     logger.debug("executeCommand({}): no response", command);
108                 }
109                 return null;
110             }
111
112         } catch (IOException ex) {
113             @Nullable
114             ThingStatusListener statusListener = thingStatusListener;
115             if (statusListener != null) {
116                 statusListener.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
117                         ex.getMessage());
118             }
119             try {
120                 close();
121             } catch (IOException e) {
122                 // ignored
123             }
124             return null;
125         } catch (Exception ex) {
126             logger.warn("executeCommand({})", command, ex);
127             return null;
128         } finally {
129             REQUEST_LOCK.unlock();
130         }
131
132         var data = Arrays.copyOfRange(buffer, 0, bytesRead);
133         logger.trace("executeCommand({}): received: {}", command, Utils.toHexString(data, data.length, ""));
134         return data;
135     }
136
137     protected synchronized @Nullable Socket getConnection() {
138         Socket socket = this.socket;
139         if (socket == null) {
140             @Nullable
141             ThingStatusListener statusListener = thingStatusListener;
142             try {
143                 socket = new Socket(config.ip, config.port);
144                 socket.setSoTimeout(5000);
145                 this.socket = socket;
146                 if (statusListener != null) {
147                     statusListener.updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, null);
148                 }
149             } catch (IOException e) {
150                 if (statusListener != null) {
151                     statusListener.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
152                             e.getMessage());
153                 }
154             }
155         }
156         return socket;
157     }
158
159     @Override
160     public void close() throws IOException {
161         Socket socket = this.socket;
162         this.socket = null;
163         if (socket != null) {
164             socket.close();
165         }
166     }
167 }