added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.sensebox</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@@ -0,0 +1,107 @@
# senseBox Binding
The senseBox binding integrates environment data from the [senseBox](https://sensebox.de/)
Citizen Science Toolkit.
## Prerequisites
The API server uses Letsencrypt certificates.
Therefore, one needs to either import the Letsencrypt root certificates into the local keystore (see the description in the
[Netatmo Binding](https://www.openhab.org/addons/bindings/netatmo/#missing-certificate-authority)).
Another way would be to simply update the JDK to at least JDK 1.8.0_111
## Supported Things
This binding supports a generic "senseBox" API endpoint which is a representation of the physical box.
## Discovery
This binding provides no discovery.
The desired senseBox must be configured manually or via a things file.
## Binding configuration
The binding has no configuration options itself, all configuration is done at 'Things' level.
## Thing configuration
The senseBox thing requires the box Id (which can be obtained on the map) and an interval in seconds for the API polling.
The senseBox support team wrote in an email that polling even every five minutes is still o.k., therefore the minimum is hardcoded to be 300 seconds.
```
Thing sensebox:box:home [ senseBoxId = "foothesmurfingbar", refreshInterval = 600 ]
```
## Channels
In the table is shown more detailed information about each Channel type.
The binding introduces the following channels:
| Channel ID | Channel Description | Supported item type | Advanced |
|-------------------------------------------------|-------------------------------------------------------------|----------------------|----------|
| descriptors#location | Location of the box | Point | False |
| measurements#uvIntensity | Intensity of Ultraviolet radiation | Number:Intensity | False |
| measurements#illuminance | Illuminance | Number:Illuminance | False |
| measurements#pressure | Air pressure | Number:Pressure | False |
| measurements#humidity | Humidity | Number:Dimensionless | False |
| measurements#temperature | Temperature | Number:Temperature | False |
| measurements#particulateMatter2dot5 | Particulate Matter 2.5 µm in diameter | Number:Density | False |
| measurements#particulateMatter10 | Temperature Matter 10 µm in diameter | Number:Density | False |
| lastReported#uvIntensityLastReported | The timestamp when uv radiation intensity was last reported | DateTime | True |
| lastReported#illuminanceLastReported | The timestamp when illuminance was last reported | DateTime | True |
| lastReported#pressureLastReported | The timestamp when pressure was last reported | DateTime | True |
| lastReported#humidityLastReported | The timestamp when humidity was last reported | DateTime | True |
| lastReported#temperatureLastReported | The timestamp when temperature was last reported | DateTime | True |
| lastReported#particulateMatter2dot5LastReported | The timestamp when particulate matter 2.5 was last reported | DateTime | True |
| lastReported#particulateMatter10LastReported | The timestamp when particulate matter 10 was last reported | DateTime | True |
Channels starting with "descriptors" are defined on the API server.
## Example
The Temperature and Pressure items are defined two times, one with the native unit and one with a localized unit.
This is to show an example of using Units of Measurements to display data without explicit recalculation is rules.
sensebox.things:
```
Thing sensebox:box:davos [ senseBoxId = "5b94a2c97c51910019097f14", refreshInterval = 900 ]
```
sensebox.items:
```
Location Davos_Location "Davos Location" { channel = "sensebox:box:davos:descriptors#location" }
Number:Intensity Davos_UVIntensity "Davos UvIntensity [%.2f %unit%]" <light> { channel = "sensebox:box:davos:measurements#uvIntensity" }
Number:Illuminance Davos_Illuminance "Davos Light Level [%.2f %unit%]" <light> { channel = "sensebox:box:davos:measurements#illuminance" }
Number:Pressure Davos_Pressure "Davos Pressure [%.2f %unit%]" <pressure> { channel = "sensebox:box:davos:measurements#pressure" }
Number:Dimensionless Davos_Humidity "Davos Humidity [%.2f %%]" <humidity> { channel = "sensebox:box:davos:measurements#humidity" }
Number:Temperature Davos_Temperature "Davos Temperature [%.2f %unit%]" <temperature> { channel = "sensebox:box:davos:measurements#temperature" }
Number:Density Davos_PM2dot5 "Davos PM2.5 [%.2f %unit%]" { channel = "sensebox:box:davos:measurements#particulateMatter2dot5" }
Number:Density Davos_PM10 "Davos PM10 [%.2f %unit%]" { channel = "sensebox:box:davos:measurements#particulateMatter10" }
```
sensebox.sitemap:
```
sitemap sensebox label="SenseBox Davos" {
Text item=Davos_Temperature
Text item=Davos_Pressure
Text item=Davos_Humidity
Text item=Davos_Luminance
Text item=Davos_UVIntensity
Text item=Davos_PM2dot5
Text item=Davos_PM10
Mapview item=Davos_Location height=10
}
```
## senseBox API
* <https://docs.opensensemap.org/>
* <https://docs.opensensemap.org/#api-Boxes-findBox>
* <https://docs.opensensemap.org/#api-Measurements-getMeasurements>
* <https://api.opensensemap.org/boxes/:boxId>
* <https://api.opensensemap.org/boxes/:senseBoxId/sensors>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.sensebox</artifactId>
<name>openHAB Add-ons :: Bundles :: senseBox Binding</name>
</project>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.sensebox-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-sensebox" description="Sensebox Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.sensebox/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,158 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sensebox.internal;
import static org.openhab.binding.sensebox.internal.SenseBoxBindingConstants.*;
import java.io.IOException;
import java.util.List;
import java.util.Properties;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.sensebox.internal.dto.SenseBoxData;
import org.openhab.binding.sensebox.internal.dto.SenseBoxDescriptor;
import org.openhab.binding.sensebox.internal.dto.SenseBoxLoc;
import org.openhab.binding.sensebox.internal.dto.SenseBoxLocation;
import org.openhab.binding.sensebox.internal.dto.SenseBoxSensor;
import org.openhab.core.io.net.http.HttpUtil;
import org.openhab.core.thing.ThingStatus;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
/**
* The {@link SenseBoxAPIConnection} is responsible for fetching data from the senseBox API server.
*
* @author Hakan Tandogan - Initial contribution
*/
@NonNullByDefault
public class SenseBoxAPIConnection {
private final Logger logger = LoggerFactory.getLogger(SenseBoxAPIConnection.class);
private final Gson gson = new Gson();
private static final Properties HEADERS = new Properties();
private static final String METHOD = "GET";
private static final int TIMEOUT = 30 * 1000; // 30 seconds
public SenseBoxAPIConnection() {
Version version = FrameworkUtil.getBundle(this.getClass()).getVersion();
HEADERS.put("User-Agent", "openHAB / senseBox binding " + version.toString());
logger.trace("Headers: {}", HEADERS);
}
public SenseBoxData reallyFetchDataFromServer(String senseBoxId) {
String query = SENSEMAP_API_URL_BASE + "/boxes/" + senseBoxId;
// the caching layer does not like null values
SenseBoxData result = new SenseBoxData();
try {
String body = HttpUtil.executeUrl(METHOD, query, HEADERS, null, null, TIMEOUT);
logger.trace("Fetched Data: {}", body);
SenseBoxData parsedData = gson.fromJson(body, SenseBoxData.class);
// Assume all is well at first
parsedData.setStatus(ThingStatus.ONLINE);
// Could perhaps be simplified via triply-nested arrays
// http://stackoverflow.com/questions/36946875/how-can-i-parse-geojson-with-gson
for (SenseBoxLoc loc : parsedData.getLocs()) {
if (loc.getGeometry() != null) {
List<Double> locationData = loc.getGeometry().getData();
if (locationData != null) {
SenseBoxLocation location = new SenseBoxLocation();
if (locationData.size() > 0) {
location.setLongitude(locationData.get(0));
}
if (locationData.size() > 1) {
location.setLatitude(locationData.get(1));
}
if (locationData.size() > 2) {
location.setHeight(locationData.get(2));
}
parsedData.setLocation(location);
}
}
}
for (SenseBoxSensor sensor : parsedData.getSensors()) {
if ("VEML6070".equals(sensor.getSensorType())) {
// "unit" is not nicely comparable, so use sensor type for now
parsedData.setUvIntensity(sensor);
} else if ("SDS 011".equals(sensor.getSensorType())) {
// "unit" is not nicely comparable, neither is type, so use sensor title for now
if ("PM2.5".equals(sensor.getTitle())) {
parsedData.setParticulateMatter2dot5(sensor);
} else if ("PM10".equals(sensor.getTitle())) {
parsedData.setParticulateMatter10(sensor);
} else {
logger.debug("SDS 011 sensor title is {}", sensor.getTitle());
}
} else if ("lx".equals(sensor.getUnit())) {
if (sensor.getLastMeasurement() != null) {
if (!(INVALID_BRIGHTNESS.equals(sensor.getLastMeasurement().getValue()))) {
parsedData.setLuminance(sensor);
}
}
} else if ("Pa".equals(sensor.getUnit()) || "hPa".equals(sensor.getUnit())) {
parsedData.setPressure(sensor);
} else if ("%".equals(sensor.getUnit())) {
parsedData.setHumidity(sensor);
} else if ("°C".equals(sensor.getUnit())) {
parsedData.setTemperature(sensor);
} else {
if (logger.isDebugEnabled()) {
logger.debug(" Sensor: {}", sensor);
logger.debug(" Sensor unit: {}", sensor.getUnit());
logger.debug(" Sensor type: {}", sensor.getSensorType());
logger.debug(" Sensor LM: {}", sensor.getLastMeasurement());
if (sensor.getLastMeasurement() != null) {
logger.debug(" Sensor LM value: {}", sensor.getLastMeasurement().getValue());
logger.debug(" Sensor LM date: '{}'", sensor.getLastMeasurement().getCreatedAt());
}
}
}
}
SenseBoxDescriptor descriptor = new SenseBoxDescriptor();
descriptor.setApiUrl(query);
String image = parsedData.getImage();
if (image != null && !image.isEmpty()) {
descriptor.setImageUrl(SENSEMAP_IMAGE_URL_BASE + "/" + image);
}
descriptor.setMapUrl(SENSEMAP_MAP_URL_BASE + "/explore/" + senseBoxId);
parsedData.setDescriptor(descriptor);
logger.trace("=================================");
result = parsedData;
} catch (IOException e) {
logger.debug("IO problems while fetching data: {} / {}", query, e.getMessage());
result.setStatus(ThingStatus.OFFLINE);
}
return result;
}
}

View File

@@ -0,0 +1,70 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sensebox.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link SenseBoxBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Hakan Tandogan - Initial contribution
*/
@NonNullByDefault
public class SenseBoxBindingConstants {
public static final String BINDING_ID = "sensebox";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_BOX = new ThingTypeUID(BINDING_ID, "box");
// List of all Channel ids
public static final String CHANNEL_LOCATION = "descriptors#location";
public static final String CHANNEL_UV_INTENSITY = "measurements#uvIntensity";
public static final String CHANNEL_ILLUMINANCE = "measurements#illuminance";
public static final String CHANNEL_PRESSURE = "measurements#pressure";
public static final String CHANNEL_HUMIDITY = "measurements#humidity";
public static final String CHANNEL_TEMPERATURE = "measurements#temperature";
public static final String CHANNEL_PARTICULATE_MATTER_2_5 = "measurements#particulateMatter2dot5";
public static final String CHANNEL_PARTICULATE_MATTER_10 = "measurements#particulateMatter10";
public static final String CHANNEL_UV_INTENSITY_LR = "lastReported#uvIntensityLastReported";
public static final String CHANNEL_ILLUMINANCE_LR = "lastReported#illuminanceLastReported";
public static final String CHANNEL_PRESSURE_LR = "lastReported#pressureLastReported";
public static final String CHANNEL_HUMIDITY_LR = "lastReported#humidityLastReported";
public static final String CHANNEL_TEMPERATURE_LR = "lastReported#temperatureLastReported";
public static final String CHANNEL_PARTICULATE_MATTER_2_5_LR = "lastReported#particulateMatter2dot5LastReported";
public static final String CHANNEL_PARTICULATE_MATTER_10_LR = "lastReported#particulateMatter10LastReported";
// List of all Property names
public static final String PROPERTY_NAME = "Name";
public static final String PROPERTY_EXPOSURE = "Exposure";
public static final String PROPERTY_IMAGE_URL = "ImageUrl";
public static final String PROPERTY_MAP_URL = "MapUrl";
// Base URL of the API server
public static final String SENSEMAP_API_URL_BASE = "https://api.opensensemap.org";
public static final String SENSEMAP_IMAGE_URL_BASE = "https://opensensemap.org/userimages";
public static final String SENSEMAP_MAP_URL_BASE = "https://opensensemap.org";
// Minimum timeslice between API requests in seconds
public static final int MINIMUM_UPDATE_INTERVAL = 5 * 60;
// How long do we want each cache entry to be available?
public static final int CACHE_EXPIRY = 10 * 1000; // 10s
// 67108860 is an invalid reading which the API sends us nevertheless
public static final String INVALID_BRIGHTNESS = "67108860";
}

View File

@@ -0,0 +1,57 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sensebox.internal;
import static org.openhab.binding.sensebox.internal.SenseBoxBindingConstants.THING_TYPE_BOX;
import java.util.Collections;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.sensebox.internal.handler.SenseBoxHandler;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.annotations.Component;
/**
* The {@link SenseBoxHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Hakan Tandogan - Initial contribution
*/
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.sensebox")
@NonNullByDefault
public class SenseBoxHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_BOX);
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (thingTypeUID.equals(THING_TYPE_BOX)) {
return new SenseBoxHandler(thing);
}
return null;
}
}

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sensebox.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link SenseBoxConfiguration} is the base class for configuration
* information held by devices and modules
*
* @author Hakan Tandogan - Initial contribution
*/
@NonNullByDefault
public class SenseBoxConfiguration {
private @Nullable String senseBoxId;
private long refreshInterval = 300;
public long getRefreshInterval() {
return refreshInterval;
}
public void setRefreshInterval(long refreshInterval) {
this.refreshInterval = refreshInterval;
}
public @Nullable String getSenseBoxId() {
return senseBoxId;
}
public void setSenseBoxId(String senseBoxId) {
this.senseBoxId = senseBoxId;
}
}

View File

@@ -0,0 +1,205 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sensebox.internal.dto;
import java.util.List;
import org.openhab.core.thing.ThingStatus;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* The {@link SenseBoxData} holds a de-serialized representation
* of the API response and the data therein...
*
* @author Hakan Tandogan - Initial contribution
*/
public class SenseBoxData {
@SerializedName("_id")
private String id;
@SerializedName("name")
private String name;
@SerializedName("exposure")
private String exposure;
@SerializedName("image")
private String image;
@SerializedName("loc")
private List<SenseBoxLoc> locs;
@SerializedName("sensors")
private List<SenseBoxSensor> sensors;
@Expose(deserialize = false)
private ThingStatus status;
@Expose(deserialize = false)
private SenseBoxDescriptor descriptor;
@Expose(deserialize = false)
private SenseBoxLocation location;
@Expose(deserialize = false)
private SenseBoxSensor uvIntensity;
@Expose(deserialize = false)
private SenseBoxSensor luminance;
@Expose(deserialize = false)
private SenseBoxSensor pressure;
@Expose(deserialize = false)
private SenseBoxSensor humidity;
@Expose(deserialize = false)
private SenseBoxSensor temperature;
@Expose(deserialize = false)
private SenseBoxSensor particulateMatter2dot5;
@Expose(deserialize = false)
private SenseBoxSensor particulateMatter10;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getExposure() {
return exposure;
}
public void setExposure(String exposure) {
this.exposure = exposure;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public SenseBoxDescriptor getDescriptor() {
return descriptor;
}
public void setDescriptor(SenseBoxDescriptor descriptor) {
this.descriptor = descriptor;
}
public SenseBoxLocation getLocation() {
return location;
}
public void setLocation(SenseBoxLocation location) {
this.location = location;
}
public List<SenseBoxLoc> getLocs() {
return locs;
}
public void setLocs(List<SenseBoxLoc> locs) {
this.locs = locs;
}
public List<SenseBoxSensor> getSensors() {
return sensors;
}
public void setSensors(List<SenseBoxSensor> sensors) {
this.sensors = sensors;
}
public ThingStatus getStatus() {
return status;
}
public void setStatus(ThingStatus status) {
this.status = status;
}
public SenseBoxSensor getUvIntensity() {
return uvIntensity;
}
public void setUvIntensity(SenseBoxSensor uvIntensity) {
this.uvIntensity = uvIntensity;
}
public SenseBoxSensor getLuminance() {
return luminance;
}
public void setLuminance(SenseBoxSensor luminance) {
this.luminance = luminance;
}
public SenseBoxSensor getPressure() {
return pressure;
}
public void setPressure(SenseBoxSensor pressure) {
this.pressure = pressure;
}
public SenseBoxSensor getHumidity() {
return humidity;
}
public void setHumidity(SenseBoxSensor humidity) {
this.humidity = humidity;
}
public SenseBoxSensor getTemperature() {
return temperature;
}
public void setTemperature(SenseBoxSensor temperature) {
this.temperature = temperature;
}
public SenseBoxSensor getParticulateMatter2dot5() {
return particulateMatter2dot5;
}
public void setParticulateMatter2dot5(SenseBoxSensor particulateMatter2dot5) {
this.particulateMatter2dot5 = particulateMatter2dot5;
}
public SenseBoxSensor getParticulateMatter10() {
return particulateMatter10;
}
public void setParticulateMatter10(SenseBoxSensor particulateMatter10) {
this.particulateMatter10 = particulateMatter10;
}
}

View File

@@ -0,0 +1,52 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sensebox.internal.dto;
/**
* The {@link SenseBoxDescriptor} holds a de-serialized representation
* of the API response and the data therein...
*
* @author Hakan Tandogan - Initial contribution
*/
public class SenseBoxDescriptor {
private String apiUrl;
private String imageUrl;
private String mapUrl;
public String getApiUrl() {
return apiUrl;
}
public void setApiUrl(String apiUrl) {
this.apiUrl = apiUrl;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public String getMapUrl() {
return mapUrl;
}
public void setMapUrl(String mapUrl) {
this.mapUrl = mapUrl;
}
}

View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sensebox.internal.dto;
import java.util.List;
import com.google.gson.annotations.SerializedName;
/**
* The {@link SenseBoxGeometry} holds a de-serialized representation
* of the API response and the data therein...
*
* @author Hakan Tandogan - Initial contribution
*/
public class SenseBoxGeometry {
@SerializedName("coordinates")
private List<Double> data;
public List<Double> getData() {
return data;
}
public void setData(List<Double> data) {
this.data = data;
}
}

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sensebox.internal.dto;
import com.google.gson.annotations.SerializedName;
/**
* The {@link SenseBoxLoc} holds a de-serialized representation
* of the API response and the data therein...
*
* @author Hakan Tandogan - Initial contribution
*/
public class SenseBoxLoc {
@SerializedName("geometry")
private SenseBoxGeometry geometry;
public SenseBoxGeometry getGeometry() {
return geometry;
}
public void setGeometry(SenseBoxGeometry geometry) {
this.geometry = geometry;
}
}

View File

@@ -0,0 +1,52 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sensebox.internal.dto;
/**
* The {@link SenseBoxLocation} holds a de-serialized representation
* of the API response and the data therein...
*
* @author Hakan Tandogan - Initial contribution
*/
public class SenseBoxLocation {
private double latitude;
private double longitude;
private double height;
public double getLatitude() {
return latitude;
}
public void setLatitude(double latitude) {
this.latitude = latitude;
}
public double getLongitude() {
return longitude;
}
public void setLongitude(double longitude) {
this.longitude = longitude;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
}

View File

@@ -0,0 +1,46 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sensebox.internal.dto;
import com.google.gson.annotations.SerializedName;
/**
* The {@link SenseBoxMeasurement} holds a de-serialized representation
* of the API response and the data therein...
*
* @author Hakan Tandogan - Initial contribution
*/
public class SenseBoxMeasurement {
@SerializedName("value")
private String value;
@SerializedName("createdAt")
private String createdAt;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getCreatedAt() {
return createdAt;
}
public void setCreatedAt(String createdAt) {
this.createdAt = createdAt;
}
}

View File

@@ -0,0 +1,92 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sensebox.internal.dto;
import com.google.gson.annotations.SerializedName;
/**
* The {@link SenseBoxSensor} holds a de-serialized representation
* of the API response and the data therein...
*
* @author Hakan Tandogan - Initial contribution
*/
public class SenseBoxSensor {
@SerializedName("_id")
private String id;
@SerializedName("title")
private String title;
@SerializedName("unit")
private String unit;
@SerializedName("icon")
private String icon;
@SerializedName("sensorType")
private String sensorType;
@SerializedName("lastMeasurement")
private SenseBoxMeasurement lastMeasurement;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getUnit() {
// the uom library uses the 'MICRO SIGN', so if we encounter the GREEK SMALL LETTER MU,
// replace it with the proper representation.
return unit != null ? unit.replaceAll("\u03bc", "\u00b5") : "";
}
public void setUnit(String unit) {
this.unit = unit;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public String getSensorType() {
return sensorType;
}
public void setSensorType(String sensorType) {
this.sensorType = sensorType;
}
public SenseBoxMeasurement getLastMeasurement() {
return lastMeasurement;
}
public void setLastMeasurement(SenseBoxMeasurement lastMeasurement) {
this.lastMeasurement = lastMeasurement;
}
}

View File

@@ -0,0 +1,301 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sensebox.internal.handler;
import static org.openhab.binding.sensebox.internal.SenseBoxBindingConstants.*;
import java.math.BigDecimal;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.sensebox.internal.SenseBoxAPIConnection;
import org.openhab.binding.sensebox.internal.config.SenseBoxConfiguration;
import org.openhab.binding.sensebox.internal.dto.SenseBoxData;
import org.openhab.binding.sensebox.internal.dto.SenseBoxLocation;
import org.openhab.binding.sensebox.internal.dto.SenseBoxSensor;
import org.openhab.core.cache.ExpiringCacheMap;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.PointType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.MetricPrefix;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.SmartHomeUnits;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SenseBoxHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Hakan Tandogan - Initial contribution
* @author Hakan Tandogan - Ignore incorrect data for brightness readings
* @author Hakan Tandogan - Changed use of caching utils to ESH ExpiringCacheMap
* @author Hakan Tandogan - Unit of Measurement support
*/
@NonNullByDefault
public class SenseBoxHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(SenseBoxHandler.class);
protected @NonNullByDefault({}) SenseBoxConfiguration thingConfiguration;
private @Nullable SenseBoxData data;
private @Nullable ScheduledFuture<?> refreshJob;
private static final BigDecimal ONEHUNDRED = BigDecimal.valueOf(100l);
private static final String CACHE_KEY_DATA = "DATA";
private final ExpiringCacheMap<String, SenseBoxData> cache = new ExpiringCacheMap<>(CACHE_EXPIRY);
private final SenseBoxAPIConnection connection = new SenseBoxAPIConnection();
public SenseBoxHandler(Thing thing) {
super(thing);
}
@Override
public void initialize() {
logger.debug("Start initializing!");
thingConfiguration = getConfigAs(SenseBoxConfiguration.class);
String senseBoxId = thingConfiguration.getSenseBoxId();
logger.debug("Thing Configuration {} initialized {}", getThing().getUID(), senseBoxId);
String offlineReason = "";
boolean validConfig = true;
if (senseBoxId == null || senseBoxId.trim().isEmpty()) {
offlineReason = "senseBox ID is mandatory and must be configured";
validConfig = false;
}
if (thingConfiguration.getRefreshInterval() < MINIMUM_UPDATE_INTERVAL) {
logger.warn("Refresh interval is much too small, setting to default of {} seconds",
MINIMUM_UPDATE_INTERVAL);
thingConfiguration.setRefreshInterval(MINIMUM_UPDATE_INTERVAL);
}
if (senseBoxId != null && validConfig) {
cache.put(CACHE_KEY_DATA, () -> {
return connection.reallyFetchDataFromServer(senseBoxId);
});
updateStatus(ThingStatus.UNKNOWN);
startAutomaticRefresh();
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, offlineReason);
}
logger.debug("Thing {} initialized {}", getThing().getUID(), getThing().getStatus());
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
data = fetchData();
if (data != null && ThingStatus.ONLINE == data.getStatus()) {
publishDataForChannel(channelUID);
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
}
} else {
logger.debug("Unsupported command {}! Supported commands: REFRESH", command);
}
}
@Override
public void dispose() {
stopAutomaticRefresh();
}
private void stopAutomaticRefresh() {
if (refreshJob != null) {
refreshJob.cancel(true);
}
}
private void publishData() {
logger.debug("Refreshing data for box {}, scheduled after {} seconds...", thingConfiguration.getSenseBoxId(),
thingConfiguration.getRefreshInterval());
data = fetchData();
if (data != null && ThingStatus.ONLINE == data.getStatus()) {
publishProperties();
publishChannels();
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
}
};
private void startAutomaticRefresh() {
stopAutomaticRefresh();
refreshJob = scheduler.scheduleWithFixedDelay(this::publishData, 0, thingConfiguration.getRefreshInterval(),
TimeUnit.SECONDS);
}
private @Nullable SenseBoxData fetchData() {
return cache.get(CACHE_KEY_DATA);
}
private void publishProperties() {
SenseBoxData localData = data;
if (localData != null) {
Map<String, String> properties = editProperties();
properties.put(PROPERTY_NAME, localData.getName());
properties.put(PROPERTY_EXPOSURE, localData.getExposure());
properties.put(PROPERTY_IMAGE_URL, localData.getDescriptor().getImageUrl());
properties.put(PROPERTY_MAP_URL, localData.getDescriptor().getMapUrl());
updateProperties(properties);
}
}
private void publishChannels() {
thing.getChannels().forEach(channel -> publishDataForChannel(channel.getUID()));
}
private void publishDataForChannel(ChannelUID channelUID) {
SenseBoxData localData = data;
if (localData != null && isLinked(channelUID)) {
switch (channelUID.getId()) {
case CHANNEL_LOCATION:
updateState(channelUID, locationFromData(localData.getLocation()));
break;
case CHANNEL_UV_INTENSITY:
updateState(channelUID, decimalFromSensor(localData.getUvIntensity()));
break;
case CHANNEL_ILLUMINANCE:
updateState(channelUID, decimalFromSensor(localData.getLuminance()));
break;
case CHANNEL_PRESSURE:
updateState(channelUID, decimalFromSensor(localData.getPressure()));
break;
case CHANNEL_HUMIDITY:
updateState(channelUID, decimalFromSensor(localData.getHumidity()));
break;
case CHANNEL_TEMPERATURE:
updateState(channelUID, decimalFromSensor(localData.getTemperature()));
break;
case CHANNEL_PARTICULATE_MATTER_2_5:
updateState(channelUID, decimalFromSensor(localData.getParticulateMatter2dot5()));
break;
case CHANNEL_PARTICULATE_MATTER_10:
updateState(channelUID, decimalFromSensor(localData.getParticulateMatter10()));
break;
case CHANNEL_UV_INTENSITY_LR:
updateState(channelUID, dateTimeFromSensor(localData.getUvIntensity()));
break;
case CHANNEL_ILLUMINANCE_LR:
updateState(channelUID, dateTimeFromSensor(localData.getLuminance()));
break;
case CHANNEL_PRESSURE_LR:
updateState(channelUID, dateTimeFromSensor(localData.getPressure()));
break;
case CHANNEL_HUMIDITY_LR:
updateState(channelUID, dateTimeFromSensor(localData.getHumidity()));
break;
case CHANNEL_TEMPERATURE_LR:
updateState(channelUID, dateTimeFromSensor(localData.getTemperature()));
break;
case CHANNEL_PARTICULATE_MATTER_2_5_LR:
updateState(channelUID, dateTimeFromSensor(localData.getParticulateMatter2dot5()));
break;
case CHANNEL_PARTICULATE_MATTER_10_LR:
updateState(channelUID, dateTimeFromSensor(localData.getParticulateMatter10()));
break;
default:
logger.debug("Command received for an unknown channel: {}", channelUID.getId());
break;
}
}
}
private State dateTimeFromSensor(@Nullable SenseBoxSensor sensorData) {
State result = UnDefType.UNDEF;
if (sensorData != null && sensorData.getLastMeasurement() != null
&& sensorData.getLastMeasurement().getCreatedAt() != null
&& !sensorData.getLastMeasurement().getCreatedAt().isEmpty()) {
result = new DateTimeType(sensorData.getLastMeasurement().getCreatedAt());
}
return result;
}
private State decimalFromSensor(@Nullable SenseBoxSensor sensorData) {
State result = UnDefType.UNDEF;
if (sensorData != null && sensorData.getLastMeasurement() != null
&& sensorData.getLastMeasurement().getValue() != null
&& !sensorData.getLastMeasurement().getValue().isEmpty()) {
logger.debug("About to determine quantity for {} / {}", sensorData.getLastMeasurement().getValue(),
sensorData.getUnit());
BigDecimal bd = new BigDecimal(sensorData.getLastMeasurement().getValue());
switch (sensorData.getUnit()) {
case "%":
result = new QuantityType<>(bd, SmartHomeUnits.PERCENT);
break;
case "°C":
result = new QuantityType<>(bd, SIUnits.CELSIUS);
break;
case "Pa":
result = new QuantityType<>(bd, SIUnits.PASCAL);
break;
case "hPa":
if (BigDecimal.valueOf(10000l).compareTo(bd) < 0) {
// Some stations report measurements in Pascal, but send 'hPa' as units...
bd = bd.divide(ONEHUNDRED);
}
result = new QuantityType<>(bd, MetricPrefix.HECTO(SIUnits.PASCAL));
break;
case "lx":
result = new QuantityType<>(bd, SmartHomeUnits.LUX);
break;
case "\u00b5g/m³":
result = new QuantityType<>(bd, SmartHomeUnits.MICROGRAM_PER_CUBICMETRE);
break;
case "\u00b5W/cm²":
result = new QuantityType<>(bd, SmartHomeUnits.MICROWATT_PER_SQUARE_CENTIMETRE);
break;
default:
// The data provider might have configured some unknown unit, accept at least the
// measurement
logger.debug("Could not determine unit for '{}', using default", sensorData.getUnit());
result = new QuantityType<>(bd, SmartHomeUnits.ONE);
}
logger.debug("State: '{}'", result);
}
return result;
}
private State locationFromData(@Nullable SenseBoxLocation locationData) {
State result = UnDefType.UNDEF;
if (locationData != null) {
result = new PointType(new DecimalType(locationData.getLatitude()),
new DecimalType(locationData.getLongitude()), new DecimalType(locationData.getHeight()));
}
return result;
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="sensebox" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>senseBox Binding</name>
<description>The senseBox binding imports data from the senseBox Citizen Science project.</description>
<author>Hakan Tandoğan</author>
</binding:binding>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<config-description:config-descriptions
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
https://openhab.org/schemas/config-description-1.0.0.xsd">
<config-description uri="thing-type:sensebox:config">
<parameter name="senseBoxId" type="text" required="true">
<label>Box ID</label>
<description>Id of the box, can be found on the map details.</description>
</parameter>
<parameter name="refreshInterval" type="integer" min="300" max="86400" unit="s">
<label>Refresh Interval</label>
<description>Refresh interval for senseBox data.</description>
<default>300</default>
</parameter>
</config-description>
</config-description:config-descriptions>

View File

@@ -0,0 +1,51 @@
# binding
binding.sensebox.description = Das senseBox Binding importiert Daten aus dem senseBox Citizen Science-Projekt.
# thing types
thing-type.sensebox.box.label = senseBox
thing-type.sensebox.box.description = Ein senseBox Sensor.
# thing type config description
thing-type.config.sensebox.sensor.senseBoxId.label = senseBox ID
thing-type.config.sensebox.sensor.senseBoxId.description = Die ID des senseBox Sensors.
thing-type.config.sensebox.sensor.refreshInterval.label = Abfrageintervall
thing-type.config.sensebox.sensor.refreshInterval.description = Intervall zur Abfrage der senseBox Daten.
# channel group types
channel-group-type.sensebox.descriptors.label = senseBox Beschreibung
channel-group-type.sensebox.descriptors.description = Fasst Daten der senseBox zusammen.
channel-group-type.sensebox.measurements.label = Messwerte
channel-group-type.sensebox.measurements.description = Fasst Messwerte zusammen.
channel-group-type.sensebox.lastReported.label = Letzte Messung
channel-group-type.sensebox.lastReported.description = Fasst Zeitpunkt der Messwerte zusammen.
# channel types
channel-type.sensebox.uvIntensity.label = UV-Intensität
channel-type.sensebox.uvIntensity.description = Aktuelle UV-Intensität.
channel-type.sensebox.uvIntensityLastReported.label = Letzte Messung UV-Intensität
channel-type.sensebox.uvIntensityLastReported.description = Zeigt den Zeitpunkt der letzten Messung der UV-Intensität an.
channel-type.sensebox.uvIntensityLastReported.state.pattern = %1$td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS
channel-type.sensebox.illuminance.label = Beleuchtungsstärke
channel-type.sensebox.illuminance.description = Aktuelle Beleuchtungsstärke.
channel-type.sensebox.illuminanceLastReported.label = Letzte Messung Beleuchtungsstärke
channel-type.sensebox.illuminanceLastReported.description = Zeigt den Zeitpunkt der letzten Messung der Beleuchtungsstärke an.
channel-type.sensebox.illuminanceLastReported.state.pattern = %1$td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS
channel-type.sensebox.pressureLastReported.label = Letzte Messung Luftdrucks
channel-type.sensebox.pressureLastReported.description = Zeigt den Zeitpunkt der letzten Messung des Luftdrucks an.
channel-type.sensebox.pressureLastReported.state.pattern = %1$td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS
channel-type.sensebox.humidityLastReported.label = Letzte Messung Luftfeuchtigkeit
channel-type.sensebox.humidityLastReported.description = Zeigt den Zeitpunkt der letzten Messung der Luftfeuchtigkeit an.
channel-type.sensebox.humidityLastReported.state.pattern = %1$td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS
channel-type.sensebox.temperatureLastReported.label = Letzte Messung Außentemperatur
channel-type.sensebox.temperatureLastReported.description = Zeigt den Zeitpunkt der letzten Messung der Außentemperatur an.
channel-type.sensebox.temperatureLastReported.state.pattern = %1$td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS
channel-type.sensebox.particulateMatter2dot5.label = Feinstaub - PM2.5
channel-type.sensebox.particulateMatter2dot5.description = Aktuelle Dichte von Teilchen mit Partikelgröße unter 2,5 µm.
channel-type.sensebox.particulateMatter2dot5LastReported.label = Letzte Messung PM2.5
channel-type.sensebox.particulateMatter2dot5LastReported.description = Zeigt den Zeitpunkt der letzten Messung von PM2.5 an.
channel-type.sensebox.particulateMatter2dot5LastReported.state.pattern = %1$td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS
channel-type.sensebox.particulateMatter10.label = Feinstaub - PM10
channel-type.sensebox.particulateMatter10.description = Aktuelle Dichte von Teilchen mit Partikelgröße unter 10 µm.
channel-type.sensebox.particulateMatter10LastReported.label = Letzte Messung PM10
channel-type.sensebox.particulateMatter10LastReported.description = Zeigt den Zeitpunkt der letzten Messung von PM10 an.
channel-type.sensebox.particulateMatter10LastReported.state.pattern = %1$td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="sensebox"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- This thing represents the Arduino box that sends data to api.opensensebox.org . -->
<thing-type id="box">
<label>senseBox</label>
<description>This is a senseBox sensor.</description>
<channel-groups>
<channel-group typeId="descriptors" id="descriptors"/>
<channel-group typeId="measurements" id="measurements"/>
<channel-group typeId="lastReported" id="lastReported"/>
</channel-groups>
<representation-property>senseBoxId</representation-property>
<config-description-ref uri="thing-type:sensebox:config"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,159 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="sensebox"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- Groups to better manage link display -->
<channel-group-type id="descriptors">
<label>Descriptors</label>
<description>Box descriptors like Location, description, etc.</description>
<channels>
<channel id="location" typeId="system.location"/>
</channels>
</channel-group-type>
<channel-group-type id="measurements">
<label>Measurements</label>
<description>Measurements as fetched from the API.</description>
<channels>
<channel id="uvIntensity" typeId="uvIntensity"/>
<channel id="illuminance" typeId="illuminance"/>
<channel id="pressure" typeId="system.barometric-pressure"/>
<channel id="humidity" typeId="system.atmospheric-humidity"/>
<channel id="temperature" typeId="system.outdoor-temperature"/>
<channel id="particulateMatter2dot5" typeId="particulateMatter2dot5"/>
<channel id="particulateMatter10" typeId="particulateMatter10"/>
</channels>
</channel-group-type>
<channel-group-type id="lastReported">
<label>Last Reported</label>
<description>Timestamps when a measurement was last reported.</description>
<channels>
<channel id="uvIntensityLastReported" typeId="uvIntensityLastReported"/>
<channel id="illuminanceLastReported" typeId="illuminanceLastReported"/>
<channel id="pressureLastReported" typeId="pressureLastReported"/>
<channel id="humidityLastReported" typeId="humidityLastReported"/>
<channel id="temperatureLastReported" typeId="temperatureLastReported"/>
<channel id="particulateMatter2dot5LastReported" typeId="particulateMatter2dot5LastReported"/>
<channel id="particulateMatter10LastReported" typeId="particulateMatter10LastReported"/>
</channels>
</channel-group-type>
<!-- The channels themselves -->
<!--
"title": "UV-Intensität",
"unit": "µW/cm²",
"sensorType": "VEML6070",
-->
<channel-type id="uvIntensity">
<item-type>Number:Intensity</item-type>
<label>UV Intensity</label>
<description>Current UV intensity.</description>
<state readOnly="true" pattern="%.2f µW/cm²"></state>
</channel-type>
<channel-type id="uvIntensityLastReported" advanced="true">
<item-type>DateTime</item-type>
<label>UV Intensity Measurement Time</label>
<description>Timestamp when data was measured.</description>
<state readOnly="true"/>
</channel-type>
<!--
"title": "Beleuchtungsstärke",
"unit": "lx",
"sensorType": "TSL45315",
-->
<channel-type id="illuminance">
<item-type>Number:Illuminance</item-type>
<label>Illuminance</label>
<description>Current illuminance.</description>
<state readOnly="true" pattern="%.2f lx"></state>
</channel-type>
<channel-type id="illuminanceLastReported" advanced="true">
<item-type>DateTime</item-type>
<label>Illuminance Measurement Time</label>
<description>Timestamp when data was measured.</description>
<state readOnly="true"/>
</channel-type>
<!--
"title": "Luftdruck",
"unit": "hPa",
"sensorType": "BMP280",
-->
<channel-type id="pressureLastReported" advanced="true">
<item-type>DateTime</item-type>
<label>Pressure Measurement Time</label>
<description>Timestamp when data was measured.</description>
<state readOnly="true"/>
</channel-type>
<!--
"title": "rel. Luftfeuchte",
"unit": "%",
"sensorType": "HDC1008",
-->
<channel-type id="humidityLastReported" advanced="true">
<item-type>DateTime</item-type>
<label>Humidity Measurement Time</label>
<description>Timestamp when data was measured.</description>
<state readOnly="true"/>
</channel-type>
<!--
"title": "Temperatur",
"unit": "°C",
"sensorType": "HDC1008",
-->
<channel-type id="temperatureLastReported" advanced="true">
<item-type>DateTime</item-type>
<label>Temperature Measurement Time</label>
<description>Timestamp when data was measured.</description>
<state readOnly="true"/>
</channel-type>
<!--
"title": "PM2.5",
"unit": "µg/m³",
"sensorType": "SDS 011",
-->
<channel-type id="particulateMatter2dot5">
<item-type>Number:Density</item-type>
<label>Particulate Matter - PM2.5</label>
<description>Current density of particles less than 2.5 µm in diameter.</description>
<state readOnly="true" pattern="%.2f µg/m³">
</state>
</channel-type>
<channel-type id="particulateMatter2dot5LastReported" advanced="true">
<item-type>DateTime</item-type>
<label>Particulate Matter 2.5 Measurement Time</label>
<description>Timestamp when data was measured.</description>
<state readOnly="true"/>
</channel-type>
<!--
"title": "PM10",
"unit": "µg/m³",
"sensorType": "SDS 011",
-->
<channel-type id="particulateMatter10">
<item-type>Number:Density</item-type>
<label>Particulate Matter - PM10</label>
<description>Current density of particles less than 10 µm in diameter.</description>
<state readOnly="true" pattern="%.2f µg/m³">
</state>
</channel-type>
<channel-type id="particulateMatter10LastReported" advanced="true">
<item-type>DateTime</item-type>
<label>Particulate Matter 10 Measurement Time</label>
<description>Timestamp when data was measured.</description>
<state readOnly="true"/>
</channel-type>
</thing:thing-descriptions>