2 * Copyright (c) 2010-2023 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.fineoffsetweatherstation.internal.service;
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;
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;
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;
41 * Interface defining the API for querying a gateway device.
43 * @author Andreas Berger - Initial contribution
46 public abstract class GatewayQueryService implements AutoCloseable {
47 protected final Logger logger = LoggerFactory.getLogger(this.getClass());
49 private static final Lock REQUEST_LOCK = new ReentrantLock();
51 private @Nullable Socket socket;
54 private final ThingStatusListener thingStatusListener;
56 private final FineOffsetGatewayConfiguration config;
59 public abstract String getFirmwareVersion();
61 public abstract Map<SensorGatewayBinding, SensorDevice> getRegisteredSensors();
64 public abstract SystemInfo fetchSystemInfo();
66 public abstract Collection<MeasuredValue> getMeasuredValues();
68 public GatewayQueryService(FineOffsetGatewayConfiguration config,
69 @Nullable ThingStatusListener thingStatusListener) {
71 this.thingStatusListener = thingStatusListener;
74 protected byte @Nullable [] executeCommand(String command, byte[] request,
75 Function<byte[], Boolean> validateResponse) {
77 if (!REQUEST_LOCK.tryLock(30, TimeUnit.SECONDS)) {
78 logger.debug("executeCommand({}): timed out while getting the lock", command);
81 } catch (InterruptedException e) {
82 logger.debug("executeCommand({}): was interrupted while getting the lock", command);
86 byte[] buffer = new byte[2028];
90 Socket socket = getConnection();
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);
102 if (!validateResponse.apply(buffer)) {
104 logger.debug("executeCommand({}), invalid response: {}", command,
105 Utils.toHexString(buffer, bytesRead, ""));
107 logger.debug("executeCommand({}): no response", command);
112 } catch (IOException ex) {
114 ThingStatusListener statusListener = thingStatusListener;
115 if (statusListener != null) {
116 statusListener.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
121 } catch (IOException e) {
125 } catch (Exception ex) {
126 logger.warn("executeCommand({})", command, ex);
129 REQUEST_LOCK.unlock();
132 var data = Arrays.copyOfRange(buffer, 0, bytesRead);
133 logger.trace("executeCommand({}): received: {}", command, Utils.toHexString(data, data.length, ""));
137 protected synchronized @Nullable Socket getConnection() {
138 Socket socket = this.socket;
139 if (socket == null) {
141 ThingStatusListener statusListener = thingStatusListener;
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);
149 } catch (IOException e) {
150 if (statusListener != null) {
151 statusListener.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
160 public void close() throws IOException {
161 Socket socket = this.socket;
163 if (socket != null) {