Skip to content

Commit

Permalink
[luftdateninfo] Add internal sensor support (openhab#10643)
Browse files Browse the repository at this point in the history
Signed-off-by: Bernd Weymann <[email protected]>
  • Loading branch information
weymann authored and thinkingstone committed Nov 7, 2021
1 parent f8928b9 commit 815eb17
Show file tree
Hide file tree
Showing 17 changed files with 256 additions and 78 deletions.
19 changes: 14 additions & 5 deletions bundles/org.openhab.binding.luftdateninfo/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# LuftdatenInfo Binding

<img align="right" src="./doc/logo-rund.png"/>

Binding for the Sensor Community [luftdaten.info](https://luftdaten.info/). The community provides instructions to build sensors on your own and they can be integrated into the database.
With this binding you can integrate your sensor, a sensor nearby or even any sensors you want into openHAB.

Expand All @@ -21,10 +19,21 @@ There's no auto discovery. See Thing configuration how to setup a Sensor.

## Thing Configuration

Choose either a local IP address of your personal owned sensor _or_ a sensor id of an external one.

| Parameter | Description |
|-----------------|----------------------------------------------------------------------|
| ipAddress | Local IP address of your personal owned sensor |
| sensorid | Sensor ID obtained from https://deutschland.maps.sensor.community/ |

### Local Sensor

Please check in your browser if you can access your sensor with your local IP address.

![Luftdaten.info Logo](doc/local-sensor.png)

### External Sensor

Perform the following steps to get the appropriate Sensor ID

* Go to to [luftdaten.info map](https://deutschland.maps.sensor.community/)
Expand Down Expand Up @@ -69,9 +78,9 @@ Perform the following steps to get the appropriate Sensor ID
luftdaten.things

```
Thing luftdateninfo:particulate:pm_sensor "PM Sensor" [ sensorid=28842]
Thing luftdateninfo:conditions:cond_sensor "Condition Sensor" [ sensorid=28843]
Thing luftdateninfo:noise:noise_sensor "Noise Sensor" [ sensorid=39745]
Thing luftdateninfo:particulate:pm_sensor "PM Sensor" [ ipAddress=192.168.178.50 ]
Thing luftdateninfo:conditions:cond_sensor "Condition Sensor" [ sensorid=28843 ]
Thing luftdateninfo:noise:noise_sensor "Noise Sensor" [ sensorid=39745 ]
```

### Items
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package org.openhab.binding.luftdateninfo.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.luftdateninfo.internal.utils.Constants;

/**
* The {@link LuftdatenInfoConfiguration} class contains fields mapping thing configuration parameters.
Expand All @@ -22,5 +23,7 @@
@NonNullByDefault
public class LuftdatenInfoConfiguration {

public int sensorid = -1;
public int sensorid = Constants.UNDEF;

public String ipAddress = Constants.EMPTY;
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,9 @@ public LuftdatenInfoHandlerFactory(final @Reference HttpClientFactory httpClient

@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
if (thingTypeUID.equals(LuftdatenInfoBindingConstants.THING_TYPE_PARTICULATE)
return (thingTypeUID.equals(LuftdatenInfoBindingConstants.THING_TYPE_PARTICULATE)
|| thingTypeUID.equals(LuftdatenInfoBindingConstants.THING_TYPE_CONDITIONS)
|| thingTypeUID.equals(LuftdatenInfoBindingConstants.THING_TYPE_NOISE)) {
return true;
} else {
return false;
}
|| thingTypeUID.equals(LuftdatenInfoBindingConstants.THING_TYPE_NOISE));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
package org.openhab.binding.luftdateninfo.internal.handler;

import java.time.LocalDateTime;
import java.util.Optional;
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.luftdateninfo.internal.LuftdatenInfoConfiguration;
import org.openhab.binding.luftdateninfo.internal.utils.Constants;
import org.openhab.binding.luftdateninfo.internal.utils.DateTimeUtils;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
Expand Down Expand Up @@ -48,9 +50,12 @@ public abstract class BaseSensorHandler extends BaseThingHandler {
protected ThingStatus myThingStatus = ThingStatus.UNKNOWN;
protected UpdateStatus lastUpdateStatus = UpdateStatus.UNKNOWN;
protected @Nullable ScheduledFuture<?> refreshJob;
private Optional<String> sensorUrl = Optional.empty();
private boolean firstUpdate = true;

public enum ConfigStatus {
OK,
INTERNAL_SENSOR_OK,
EXTERNAL_SENSOR_OK,
IS_NULL,
SENSOR_IS_NULL,
SENSOR_ID_NEGATIVE,
Expand Down Expand Up @@ -88,14 +93,15 @@ public void handleCommand(ChannelUID channelUID, Command command) {

@Override
public void initialize() {
firstUpdate = true;
lifecycleStatus = LifecycleStatus.INITIALIZING;
scheduler.execute(this::startUp);
}

private void startUp() {
config = getConfigAs(LuftdatenInfoConfiguration.class);
configStatus = checkConfig(config);
if (configStatus == ConfigStatus.OK) {
if (configStatus == ConfigStatus.INTERNAL_SENSOR_OK || configStatus == ConfigStatus.EXTERNAL_SENSOR_OK) {
// start getting values
dataUpdate();
} else {
Expand Down Expand Up @@ -135,10 +141,16 @@ public void dispose() {
*/
private ConfigStatus checkConfig(@Nullable LuftdatenInfoConfiguration c) {
if (c != null) {
if (c.sensorid >= 0) {
return ConfigStatus.OK;
if (c.ipAddress != null && !Constants.EMPTY.equals(c.ipAddress)) {
sensorUrl = Optional.of("http://" + c.ipAddress + "/data.json");
return ConfigStatus.INTERNAL_SENSOR_OK;
} else {
return ConfigStatus.SENSOR_ID_NEGATIVE;
if (c.sensorid >= 0) {
sensorUrl = Optional.of("http://data.sensor.community/airrohr/v1/sensor/" + c.sensorid + "/");
return ConfigStatus.EXTERNAL_SENSOR_OK;
} else {
return ConfigStatus.SENSOR_ID_NEGATIVE;
}
}
} else {
return ConfigStatus.IS_NULL;
Expand All @@ -150,11 +162,21 @@ public LifecycleStatus getLifecycleStatus() {
}

protected void dataUpdate() {
HTTPHandler.getHandler().request(config.sensorid, this);
if (sensorUrl.isPresent()) {
HTTPHandler.getHandler().request(sensorUrl.get(), this);
}
}

public void onResponse(String data) {
lastUpdateStatus = updateChannels(data);
if (firstUpdate) {
logger.debug("{} delivers {}", sensorUrl.get(), data);
firstUpdate = false;
}
if (configStatus == ConfigStatus.INTERNAL_SENSOR_OK) {
lastUpdateStatus = updateChannels("[" + data + "]");
} else {
lastUpdateStatus = updateChannels(data);
}
statusUpdate(lastUpdateStatus, EMPTY);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
package org.openhab.binding.luftdateninfo.internal.handler;

import static org.openhab.binding.luftdateninfo.internal.LuftdatenInfoBindingConstants.*;
import static org.openhab.binding.luftdateninfo.internal.handler.HTTPHandler.*;
import static org.openhab.binding.luftdateninfo.internal.utils.Constants.*;
import static org.openhab.core.library.unit.MetricPrefix.HECTO;

import java.util.List;

Expand All @@ -38,11 +39,10 @@
*/
@NonNullByDefault
public class ConditionHandler extends BaseSensorHandler {

protected QuantityType<Temperature> temperatureCache = QuantityType.valueOf(-1, SIUnits.CELSIUS);
protected QuantityType<Dimensionless> humidityCache = QuantityType.valueOf(-1, Units.PERCENT);
protected QuantityType<Pressure> pressureCache = QuantityType.valueOf(-1, SIUnits.PASCAL);
protected QuantityType<Pressure> pressureSeaCache = QuantityType.valueOf(-1, SIUnits.PASCAL);
protected QuantityType<Pressure> pressureCache = QuantityType.valueOf(-1, HECTO(SIUnits.PASCAL));
protected QuantityType<Pressure> pressureSeaCache = QuantityType.valueOf(-1, HECTO(SIUnits.PASCAL));

public ConditionHandler(Thing thing) {
super(thing);
Expand All @@ -55,18 +55,22 @@ public UpdateStatus updateChannels(@Nullable String json) {
if (valueList != null) {
if (HTTPHandler.getHandler().isCondition(valueList)) {
valueList.forEach(v -> {
if (v.getValueType().equals(TEMPERATURE)) {
if (v.getValueType().endsWith(TEMPERATURE)) {
temperatureCache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1),
SIUnits.CELSIUS);
updateState(TEMPERATURE_CHANNEL, temperatureCache);
} else if (v.getValueType().equals(HUMIDITY)) {
} else if (v.getValueType().endsWith(HUMIDITY)) {
humidityCache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1), Units.PERCENT);
updateState(HUMIDITY_CHANNEL, humidityCache);
} else if (v.getValueType().equals(PRESSURE)) {
pressureCache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1), SIUnits.PASCAL);
} else if (v.getValueType().endsWith(PRESSURE)) {
pressureCache = QuantityType.valueOf(
NumberUtils.round(NumberUtils.convert(v.getValue()) / 100, 1),
HECTO(SIUnits.PASCAL));
updateState(PRESSURE_CHANNEL, pressureCache);
} else if (v.getValueType().equals(PRESSURE_SEALEVEL)) {
pressureSeaCache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1), SIUnits.PASCAL);
} else if (v.getValueType().endsWith(PRESSURE_SEALEVEL)) {
pressureSeaCache = QuantityType.valueOf(
NumberUtils.round(NumberUtils.convert(v.getValue()) / 100, 1),
HECTO(SIUnits.PASCAL));
updateState(PRESSURE_SEA_CHANNEL, pressureSeaCache);
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
*/
package org.openhab.binding.luftdateninfo.internal.handler;

import static org.openhab.binding.luftdateninfo.internal.utils.Constants.*;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
Expand Down Expand Up @@ -42,19 +44,6 @@ public class HTTPHandler {
private static final Gson GSON = new Gson();
private static final HTTPHandler HTTP_HANDLER = new HTTPHandler();

public static final String P1 = "P1";
public static final String P2 = "P2";

public static final String TEMPERATURE = "temperature";
public static final String HUMIDITY = "humidity";
public static final String PRESSURE = "pressure";
public static final String PRESSURE_SEALEVEL = "pressure_at_sealevel";

public static final String NOISE_EQ = "noise_LAeq";
public static final String NOISE_MIN = "noise_LA_min";
public static final String NOISE_MAX = "noise_LA_max";

private static String sensorUrl = "http://data.sensor.community/airrohr/v1/sensor/";
private static @Nullable HttpClient commonHttpClient;

public static void init(HttpClient httpClient) {
Expand All @@ -65,12 +54,11 @@ public static HTTPHandler getHandler() {
return HTTP_HANDLER;
}

public synchronized void request(int sensorId, BaseSensorHandler callback) {
public synchronized void request(String url, BaseSensorHandler callback) {
HttpClient localClient = commonHttpClient;
if (localClient == null) {
logger.warn("HTTP Client not initialized");
} else {
String url = sensorUrl + sensorId + "/";
Request req = localClient.newRequest(url);
req.timeout(15, TimeUnit.SECONDS).send(new BufferingResponseListener() {
@NonNullByDefault({})
Expand Down Expand Up @@ -142,24 +130,24 @@ public boolean isParticulate(@Nullable List<SensorDataValue> valueList) {
if (valueList == null) {
return false;
}
return valueList.stream().map(v -> v.getValueType()).filter(t -> t.equals(P1) || t.equals(P2)).findAny()
return valueList.stream().map(v -> v.getValueType()).filter(t -> t.endsWith(P1) || t.endsWith(P2)).findAny()
.isPresent();
}

public boolean isCondition(@Nullable List<SensorDataValue> valueList) {
if (valueList == null) {
return false;
}
return valueList.stream().map(v -> v.getValueType()).filter(
t -> t.equals(TEMPERATURE) || t.equals(HUMIDITY) || t.equals(PRESSURE) || t.equals(PRESSURE_SEALEVEL))
.findAny().isPresent();
return valueList.stream().map(v -> v.getValueType()).filter(t -> t.equals(TEMPERATURE) || t.endsWith(HUMIDITY)
|| t.endsWith(PRESSURE) || t.endsWith(PRESSURE_SEALEVEL)).findAny().isPresent();
}

public boolean isNoise(@Nullable List<SensorDataValue> valueList) {
if (valueList == null) {
return false;
}
return valueList.stream().map(v -> v.getValueType())
.filter(t -> t.equals(NOISE_EQ) || t.equals(NOISE_MAX) || t.equals(NOISE_MIN)).findAny().isPresent();
.filter(t -> t.endsWith(NOISE_EQ) || t.endsWith(NOISE_MAX) || t.endsWith(NOISE_MIN)).findAny()
.isPresent();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
package org.openhab.binding.luftdateninfo.internal.handler;

import static org.openhab.binding.luftdateninfo.internal.LuftdatenInfoBindingConstants.*;
import static org.openhab.binding.luftdateninfo.internal.handler.HTTPHandler.*;
import static org.openhab.binding.luftdateninfo.internal.utils.Constants.*;

import java.util.List;

Expand Down Expand Up @@ -50,13 +50,13 @@ public UpdateStatus updateChannels(@Nullable String json) {
if (valueList != null) {
if (HTTPHandler.getHandler().isNoise(valueList)) {
valueList.forEach(v -> {
if (v.getValueType().equals(NOISE_EQ)) {
if (v.getValueType().endsWith(NOISE_EQ)) {
noiseEQCache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1), Units.DECIBEL);
updateState(NOISE_EQ_CHANNEL, noiseEQCache);
} else if (v.getValueType().equals(NOISE_MIN)) {
} else if (v.getValueType().endsWith(NOISE_MIN)) {
noiseMinCache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1), Units.DECIBEL);
updateState(NOISE_MIN_CHANNEL, noiseMinCache);
} else if (v.getValueType().equals(NOISE_MAX)) {
} else if (v.getValueType().endsWith(NOISE_MAX)) {
noiseMaxCache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1), Units.DECIBEL);
updateState(NOISE_MAX_CHANNEL, noiseMaxCache);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
package org.openhab.binding.luftdateninfo.internal.handler;

import static org.openhab.binding.luftdateninfo.internal.LuftdatenInfoBindingConstants.*;
import static org.openhab.binding.luftdateninfo.internal.handler.HTTPHandler.*;
import static org.openhab.binding.luftdateninfo.internal.utils.Constants.*;

import java.util.List;

Expand Down Expand Up @@ -49,11 +49,11 @@ public UpdateStatus updateChannels(@Nullable String json) {
if (valueList != null) {
if (HTTPHandler.getHandler().isParticulate(valueList)) {
valueList.forEach(v -> {
if (v.getValueType().equals(P1)) {
if (v.getValueType().endsWith(P1)) {
pm100Cache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1),
Units.MICROGRAM_PER_CUBICMETRE);
updateState(PM100_CHANNEL, pm100Cache);
} else if (v.getValueType().equals(P2)) {
} else if (v.getValueType().endsWith(P2)) {
pm25Cache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1),
Units.MICROGRAM_PER_CUBICMETRE);
updateState(PM25_CHANNEL, pm25Cache);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Copyright (c) 2010-2021 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.luftdateninfo.internal.utils;

import org.eclipse.jdt.annotation.NonNullByDefault;

/**
* The {@link Constants} Constants used in this binding
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class Constants {
public static final String EMPTY = "";
public static final String P1 = "P1";
public static final String P2 = "P2";

public static final String TEMPERATURE = "temperature";
public static final String HUMIDITY = "humidity";
public static final String PRESSURE = "pressure";
public static final String PRESSURE_SEALEVEL = "pressure_at_sealevel";

public static final String NOISE_EQ = "noise_LAeq";
public static final String NOISE_MIN = "noise_LA_min";
public static final String NOISE_MAX = "noise_LA_max";
public static final int UNDEF = -1;
}
Loading

0 comments on commit 815eb17

Please sign in to comment.