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.sensebox.internal;
15 import static org.openhab.binding.sensebox.internal.SenseBoxBindingConstants.*;
17 import java.io.IOException;
18 import java.util.List;
19 import java.util.Properties;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.openhab.binding.sensebox.internal.dto.SenseBoxData;
23 import org.openhab.binding.sensebox.internal.dto.SenseBoxDescriptor;
24 import org.openhab.binding.sensebox.internal.dto.SenseBoxLoc;
25 import org.openhab.binding.sensebox.internal.dto.SenseBoxLocation;
26 import org.openhab.binding.sensebox.internal.dto.SenseBoxSensor;
27 import org.openhab.core.io.net.http.HttpUtil;
28 import org.openhab.core.thing.ThingStatus;
29 import org.osgi.framework.FrameworkUtil;
30 import org.osgi.framework.Version;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
34 import com.google.gson.Gson;
35 import com.google.gson.JsonSyntaxException;
38 * The {@link SenseBoxAPIConnection} is responsible for fetching data from the senseBox API server.
40 * @author Hakan Tandogan - Initial contribution
43 public class SenseBoxAPIConnection {
45 private final Logger logger = LoggerFactory.getLogger(SenseBoxAPIConnection.class);
47 private final Gson gson = new Gson();
49 private static final Properties HEADERS = new Properties();
51 private static final String METHOD = "GET";
53 private static final int TIMEOUT = 30 * 1000; // 30 seconds
55 public SenseBoxAPIConnection() {
56 Version version = FrameworkUtil.getBundle(this.getClass()).getVersion();
57 HEADERS.put("User-Agent", "openHAB / senseBox binding " + version.toString());
58 logger.trace("Headers: {}", HEADERS);
61 public SenseBoxData reallyFetchDataFromServer(String senseBoxId) {
62 String query = SENSEMAP_API_URL_BASE + "/boxes/" + senseBoxId;
64 // the caching layer does not like null values
65 SenseBoxData result = new SenseBoxData();
69 body = HttpUtil.executeUrl(METHOD, query, HEADERS, null, null, TIMEOUT);
71 logger.trace("Fetched Data: {}", body);
72 SenseBoxData parsedData = gson.fromJson(body, SenseBoxData.class);
74 if (parsedData != null) {
75 // Assume all is well at first
76 parsedData.setStatus(ThingStatus.ONLINE);
78 // Could perhaps be simplified via triply-nested arrays
79 // http://stackoverflow.com/questions/36946875/how-can-i-parse-geojson-with-gson
80 for (SenseBoxLoc loc : parsedData.getLocs()) {
81 if (loc.getGeometry() != null) {
82 List<Double> locationData = loc.getGeometry().getData();
83 if (locationData != null) {
84 int locationDataCount = locationData.size();
85 SenseBoxLocation location = new SenseBoxLocation();
87 if (locationDataCount > 0) {
88 location.setLongitude(locationData.get(0));
91 if (locationDataCount > 1) {
92 location.setLatitude(locationData.get(1));
95 if (locationDataCount > 2) {
96 location.setHeight(locationData.get(2));
99 parsedData.setLocation(location);
104 for (SenseBoxSensor sensor : parsedData.getSensors()) {
105 if ("VEML6070".equals(sensor.getSensorType())) {
106 // "unit" is not nicely comparable, so use sensor type for now
107 parsedData.setUvIntensity(sensor);
108 } else if ("SDS 011".equals(sensor.getSensorType())) {
109 // "unit" is not nicely comparable, neither is type, so use sensor title for now
110 if ("PM2.5".equals(sensor.getTitle())) {
111 parsedData.setParticulateMatter2dot5(sensor);
112 } else if ("PM10".equals(sensor.getTitle())) {
113 parsedData.setParticulateMatter10(sensor);
115 logger.debug("SDS 011 sensor title is {}", sensor.getTitle());
117 } else if ("lx".equals(sensor.getUnit())) {
118 if (sensor.getLastMeasurement() != null) {
119 if (!(INVALID_BRIGHTNESS.equals(sensor.getLastMeasurement().getValue()))) {
120 parsedData.setLuminance(sensor);
123 } else if ("Pa".equals(sensor.getUnit()) || "hPa".equals(sensor.getUnit())) {
124 parsedData.setPressure(sensor);
125 } else if ("%".equals(sensor.getUnit())) {
126 parsedData.setHumidity(sensor);
127 } else if ("°C".equals(sensor.getUnit())) {
128 parsedData.setTemperature(sensor);
130 if (logger.isDebugEnabled()) {
131 logger.debug(" Sensor: {}", sensor);
132 logger.debug(" Sensor unit: {}", sensor.getUnit());
133 logger.debug(" Sensor type: {}", sensor.getSensorType());
134 logger.debug(" Sensor LM: {}", sensor.getLastMeasurement());
135 if (sensor.getLastMeasurement() != null) {
136 logger.debug(" Sensor LM value: {}", sensor.getLastMeasurement().getValue());
137 logger.debug(" Sensor LM date: '{}'", sensor.getLastMeasurement().getCreatedAt());
143 SenseBoxDescriptor descriptor = new SenseBoxDescriptor();
144 descriptor.setApiUrl(query);
145 String image = parsedData.getImage();
146 if (image != null && !image.isEmpty()) {
147 descriptor.setImageUrl(SENSEMAP_IMAGE_URL_BASE + "/" + image);
149 descriptor.setMapUrl(SENSEMAP_MAP_URL_BASE + "/explore/" + senseBoxId);
150 parsedData.setDescriptor(descriptor);
152 logger.trace("=================================");
156 logger.debug("An error occurred while parsing the data into desired class: {} / {}", body,
157 SenseBoxData.class.getName());
158 result.setStatus(ThingStatus.OFFLINE);
160 } catch (JsonSyntaxException e) {
161 logger.debug("An error occurred while parsing the data into desired class: {} / {} / {}", body,
162 SenseBoxData.class.getName(), e.getMessage());
163 result.setStatus(ThingStatus.OFFLINE);
164 } catch (IOException e) {
165 logger.debug("IO problems while fetching data: {} / {}", query, e.getMessage());
166 result.setStatus(ThingStatus.OFFLINE);