]> git.basschouten.com Git - openhab-addons.git/blob
c60d46a84772043e746ad465421008af80988d02
[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     private 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         byte[] buffer = new byte[2028];
77         int bytesRead;
78
79         try {
80             if (!REQUEST_LOCK.tryLock(30, TimeUnit.SECONDS)) {
81                 logger.trace("executeCommand({}): time out while getting lock", command);
82                 return null;
83             }
84             Socket socket = getConnection();
85             if (socket == null) {
86                 return null;
87             }
88             logger.trace("executeCommand({}): send request: {}", command,
89                     Utils.toHexString(request, request.length, ""));
90             InputStream in = socket.getInputStream();
91             socket.getOutputStream().write(request);
92             if ((bytesRead = in.read(buffer)) == -1) {
93                 logger.trace("executeCommand({}): data exceeded buffer length ({})", command, buffer.length);
94                 return null;
95             }
96             if (!validateResponse.apply(buffer)) {
97                 if (bytesRead > 0) {
98                     logger.debug("executeCommand({}), invalid response: {}", command,
99                             Utils.toHexString(buffer, bytesRead, ""));
100                 } else {
101                     logger.debug("executeCommand({}): no response", command);
102                 }
103                 return null;
104             }
105
106         } catch (IOException ex) {
107             @Nullable
108             ThingStatusListener statusListener = thingStatusListener;
109             if (statusListener != null) {
110                 statusListener.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
111                         ex.getMessage());
112             }
113             try {
114                 close();
115             } catch (IOException e) {
116                 // ignored
117             }
118             return null;
119         } catch (Exception ex) {
120             logger.warn("executeCommand({})", command, ex);
121             return null;
122         } finally {
123             REQUEST_LOCK.unlock();
124         }
125
126         var data = Arrays.copyOfRange(buffer, 0, bytesRead);
127         logger.trace("executeCommand({}): received: {}", command, Utils.toHexString(data, data.length, ""));
128         return data;
129     }
130
131     protected synchronized @Nullable Socket getConnection() {
132         Socket socket = this.socket;
133         if (socket == null) {
134             @Nullable
135             ThingStatusListener statusListener = thingStatusListener;
136             try {
137                 socket = new Socket(config.ip, config.port);
138                 socket.setSoTimeout(5000);
139                 this.socket = socket;
140                 if (statusListener != null) {
141                     statusListener.updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, null);
142                 }
143             } catch (IOException e) {
144                 if (statusListener != null) {
145                     statusListener.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
146                             e.getMessage());
147                 }
148             }
149         }
150         return socket;
151     }
152
153     @Override
154     public void close() throws IOException {
155         Socket socket = this.socket;
156         this.socket = null;
157         if (socket != null) {
158             socket.close();
159         }
160     }
161 }