]> git.basschouten.com Git - openhab-addons.git/blob
32950d16b4534424e99714429fa1328624771e67
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.Collections;
20 import java.util.List;
21 import java.util.Map;
22
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.binding.fineoffsetweatherstation.internal.FineOffsetGatewayConfiguration;
26 import org.openhab.binding.fineoffsetweatherstation.internal.Utils;
27 import org.openhab.binding.fineoffsetweatherstation.internal.domain.Command;
28 import org.openhab.binding.fineoffsetweatherstation.internal.domain.ConversionContext;
29 import org.openhab.binding.fineoffsetweatherstation.internal.domain.SensorGatewayBinding;
30 import org.openhab.binding.fineoffsetweatherstation.internal.domain.response.MeasuredValue;
31 import org.openhab.binding.fineoffsetweatherstation.internal.domain.response.SensorDevice;
32 import org.openhab.binding.fineoffsetweatherstation.internal.domain.response.SystemInfo;
33 import org.openhab.binding.fineoffsetweatherstation.internal.handler.ThingStatusListener;
34 import org.openhab.core.thing.ThingStatus;
35 import org.openhab.core.thing.ThingStatusDetail;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 /**
40  * Service to query the gateway device.
41  *
42  * @author Andreas Berger - Initial contribution
43  */
44 @NonNullByDefault
45 public class FineOffsetGatewayQueryService implements AutoCloseable {
46     private final Logger logger = LoggerFactory.getLogger(FineOffsetGatewayQueryService.class);
47
48     private @Nullable Socket socket;
49     private final FineOffsetGatewayConfiguration config;
50     private final ThingStatusListener thingStatusListener;
51     private final FineOffsetDataParser fineOffsetDataParser;
52
53     private final ConversionContext conversionContext;
54
55     public FineOffsetGatewayQueryService(FineOffsetGatewayConfiguration config, ThingStatusListener thingStatusListener,
56             ConversionContext conversionContext) {
57         this.config = config;
58         this.thingStatusListener = thingStatusListener;
59         this.fineOffsetDataParser = new FineOffsetDataParser();
60         this.conversionContext = conversionContext;
61     }
62
63     public @Nullable String getFirmwareVersion() {
64         var data = executeCommand(Command.CMD_READ_FIRMWARE_VERSION);
65         if (null != data) {
66             return fineOffsetDataParser.getFirmwareVersion(data);
67         }
68         return null;
69     }
70
71     public Map<SensorGatewayBinding, SensorDevice> getRegisteredSensors() {
72         var data = executeCommand(Command.CMD_READ_SENSOR_ID_NEW);
73         if (null == data) {
74             return Map.of();
75         }
76         return fineOffsetDataParser.getRegisteredSensors(data, () -> {
77             @Nullable
78             SystemInfo systemInfo = fetchSystemInfo();
79             if (systemInfo != null) {
80                 return systemInfo.isUseWh24();
81             }
82             return null;
83         });
84     }
85
86     public @Nullable SystemInfo fetchSystemInfo() {
87         var data = executeCommand(Command.CMD_READ_SSSS);
88         if (data == null) {
89             logger.debug("Unexpected response to System Info!");
90             return null;
91         }
92         return fineOffsetDataParser.fetchSystemInfo(data);
93     }
94
95     public List<MeasuredValue> getLiveData() {
96         byte[] data = executeCommand(Command.CMD_GW1000_LIVEDATA);
97         if (data == null) {
98             return Collections.emptyList();
99         }
100         return fineOffsetDataParser.getLiveData(data, conversionContext);
101     }
102
103     private synchronized byte @Nullable [] executeCommand(Command command) {
104         byte[] buffer = new byte[2028];
105         int bytesRead;
106         byte[] request = command.getPayload();
107
108         try {
109             Socket socket = getConnection();
110             if (socket == null) {
111                 return null;
112             }
113             InputStream in = socket.getInputStream();
114             socket.getOutputStream().write(request);
115             if ((bytesRead = in.read(buffer)) == -1) {
116                 return null;
117             }
118             if (!command.isResponseValid(buffer)) {
119                 if (bytesRead > 0) {
120                     logger.debug("executeCommand({}), invalid response: {}", command,
121                             Utils.toHexString(buffer, bytesRead, ""));
122                 } else {
123                     logger.debug("executeCommand({}): no response", command);
124                 }
125                 return null;
126             }
127
128         } catch (IOException ex) {
129             thingStatusListener.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
130                     ex.getMessage());
131             try {
132                 close();
133             } catch (IOException e) {
134                 // ignored
135             }
136             return null;
137         } catch (Exception ex) {
138             logger.warn("executeCommand({})", command, ex);
139             return null;
140         }
141
142         var data = Arrays.copyOfRange(buffer, 0, bytesRead);
143         logger.trace("executeCommand({}): received: {}", command, Utils.toHexString(data, data.length, ""));
144         return data;
145     }
146
147     private synchronized @Nullable Socket getConnection() {
148         Socket socket = this.socket;
149         if (socket == null) {
150             try {
151                 socket = new Socket(config.ip, config.port);
152                 socket.setSoTimeout(5000);
153                 this.socket = socket;
154                 thingStatusListener.updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, null);
155             } catch (IOException e) {
156                 thingStatusListener.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
157                         e.getMessage());
158             }
159         }
160         return socket;
161     }
162
163     @Override
164     public void close() throws IOException {
165         Socket socket = this.socket;
166         this.socket = null;
167         if (socket != null) {
168             socket.close();
169         }
170     }
171 }