* [luftdateninfo][sensorcommunity] Rename binding to match the new name Sensor.Community
Signed-off-by: Holger Friedrich <mail@holger-friedrich.de>
/bundles/org.openhab.binding.livisismarthome/ @Novanic
/bundles/org.openhab.binding.logreader/ @paulianttila
/bundles/org.openhab.binding.loxone/ @ppieczul
-/bundles/org.openhab.binding.luftdateninfo/ @weymann
/bundles/org.openhab.binding.lutron/ @actong @bobadair
/bundles/org.openhab.binding.luxom/ @jesperskriasoft
/bundles/org.openhab.binding.luxtronikheatpump/ @sgiehl
/bundles/org.openhab.binding.seneye/ @nikotanghe
/bundles/org.openhab.binding.sensebox/ @hakan42
/bundles/org.openhab.binding.sensibo/ @seime
+/bundles/org.openhab.binding.sensorcommunity/ @weymann
/bundles/org.openhab.binding.serial/ @MikeJMajor
/bundles/org.openhab.binding.serialbutton/ @kaikreuzer
/bundles/org.openhab.binding.shelly/ @markus7017
<artifactId>org.openhab.binding.loxone</artifactId>
<version>${project.version}</version>
</dependency>
- <dependency>
- <groupId>org.openhab.addons.bundles</groupId>
- <artifactId>org.openhab.binding.luftdateninfo</artifactId>
- <version>${project.version}</version>
- </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.lutron</artifactId>
<artifactId>org.openhab.binding.sensibo</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.openhab.addons.bundles</groupId>
+ <artifactId>org.openhab.binding.sensorcommunity</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.serial</artifactId>
+++ /dev/null
-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
+++ /dev/null
-# LuftdatenInfo Binding
-
-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.
-
-## Supported Things
-
-Three Things are supported
-
-| Name | Thing Type ID | Description |
-|--------------------|---------------|--------------------------------------------------------------------------------------------------------|
-| Particulate Sensor | particulate | measure particulate matter PM2.5 and PM10 |
-| Conditions Sensor | condition | measures environment conditions like temperature, humidity and some also provides atmospheric pressure |
-| Noise Sensor | noise | measures noise exposures in the environment |
-
-## Discovery
-
-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.
-
-
-
-### External Sensor
-
-Perform the following steps to get the appropriate Sensor ID
-
-- Go to to [luftdaten.info map](https://deutschland.maps.sensor.community/)
-- Choose your desired value in bottom list - now only the Sensors are displayed which are supporting this
-- Click on your / any Sensor and the ID is displayed in the top right corner. Note: Sensor ID is just the number without beginning hash #
-- Enter this Sensor ID into the thing configuration
-
-
-
-## Channels
-
-### Particulate Sensor
-
-| Channel ID | Item Type | Description |
-|----------------------|----------------------|------------------------------------------|
-| pm25 | Number:Density | [Ultrafine particulates](https://en.wikipedia.org/wiki/Particulates#Size,_shape_and_solubility_matter) microgram per cubic meter |
-| pm100 | Number:Density | [Coarse particulate matter](https://en.wikipedia.org/wiki/Particulates#Size,_shape_and_solubility_matter) microgram per cubic meter |
-
-### Conditions Sensor
-
-| Channel ID | Item Type | Description |
-|----------------------|----------------------|------------------------------------------|
-| temperature | Number:Temperature | current temperature |
-| humidity | Number:Dimensionless | current humidity percent |
-| pressure | Number:Pressure | Atmospheric Pressure (not supported by all sensors) |
-| pressure-sea | Number:Pressure | Atmospheric Pressure on sea level (not supported by all sensors) |
-
-### Noise Sensor
-
-| Channel ID | Item Type | Description |
-|----------------------|----------------------|------------------------------------------------------|
-| noise-eq | Number:Dimensionless | Average noise in db |
-| noise-min | Number:Dimensionless | Minimum noise covered in the last 2.5 minutes in db |
-| noise-main | Number:Dimensionless | Maximum noise covered in the last 2.5 minutes in db |
-
-## Full Example
-
-### Things
-
-luftdaten.things
-
-```java
-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
-
-luftdaten.items
-
-```java
-Number:Density PM_25 "PM2.5" { channel="luftdateninfo:particulate:pm_sensor:pm25" }
-Number:Density PM_100 "PM10" { channel="luftdateninfo:particulate:pm_sensor:pm100" }
-
-Number:Temperature LDI_Temperature "Temperature" { channel="luftdateninfo:conditions:cond_sensor:temperature" }
-Number:Dimensionless LDI_Humidity "Humidity" { channel="luftdateninfo:conditions:cond_sensor:humidity" }
-Number:Pressure LDI_Pressure "Atmospheric Pressure" { channel="luftdateninfo:conditions:cond_sensor:pressure" }
-Number:Pressure LDI_PressureSea "Pressure sea level" { channel="luftdateninfo:conditions:cond_sensor:pressure-sea" }
-
-Number:Dimensionless LDI_NoiseEQ "Noise EQ" { channel="luftdateninfo:noise:noise_sensor:noise-eq" }
-Number:Dimensionless LDI_NoiseMin "Noise min" { channel="luftdateninfo:noise:noise_sensor:noise-min" }
-Number:Dimensionless LDI_NoiseMax "Noise max" { channel="luftdateninfo:noise:noise_sensor:noise-max" }
-```
-
-### Sitemap
-
-LuftdatenInfo.sitemap
-
-```perl
-sitemap LuftdatenInfo label="LuftdatenInfo" {
- Text item=PM_25 label="Particulate Matter 2.5 [%.1f %unit%]"
- Text item=PM_100 label="Particulate Matter 10 [%.1f %unit%]"
-
- Text item=LDI_Temperature label="Temperature [%d %unit%]"
- Text item=LDI_Humidity label="Humidity [%d %unit%]"
- Text item=LDI_Pressure label="Atmospheric Pressure [%d %unit%]"
- Text item=LDI_PressureSea label="Atmospheric Pressure sea [%d %unit%]"
-
- Text item=LDI_NoiseEQ label="Noise avg [%.1f %unit%]"
- Text item=LDI_NoiseMin label="Noise min [%.1f %unit%]"
- Text item=LDI_NoiseMax label="Noise max [%.1f %unit%]"
-}
-```
+++ /dev/null
-<?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 https://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>4.0.0-SNAPSHOT</version>
- </parent>
-
- <artifactId>org.openhab.binding.luftdateninfo</artifactId>
-
- <name>openHAB Add-ons :: Bundles :: LuftdatenInfo Binding</name>
-
-</project>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<features name="org.openhab.binding.luftdateninfo-${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-luftdateninfo" description="LuftdatenInfo Binding" version="${project.version}">
- <feature>openhab-runtime-base</feature>
- <bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.luftdateninfo/${project.version}</bundle>
- </feature>
-</features>
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.core.thing.ThingTypeUID;
-
-/**
- * The {@link LuftdatenInfoBindingConstants} class defines common constants, which are
- * used across the whole binding.
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class LuftdatenInfoBindingConstants {
-
- private static final String BINDING_ID = "luftdateninfo";
-
- // List of all Thing Type UIDs
- public static final ThingTypeUID THING_TYPE_PARTICULATE = new ThingTypeUID(BINDING_ID, "particulate");
- public static final ThingTypeUID THING_TYPE_CONDITIONS = new ThingTypeUID(BINDING_ID, "conditions");
- public static final ThingTypeUID THING_TYPE_NOISE = new ThingTypeUID(BINDING_ID, "noise");
-
- // List of all Channel ids
- public static final String PM25_CHANNEL = "pm25";
- public static final String PM100_CHANNEL = "pm100";
- public static final String TEMPERATURE_CHANNEL = "temperature";
- public static final String HUMIDITY_CHANNEL = "humidity";
- public static final String PRESSURE_CHANNEL = "pressure";
- public static final String PRESSURE_SEA_CHANNEL = "pressure-sea";
- public static final String NOISE_EQ_CHANNEL = "noise-eq";
- public static final String NOISE_MIN_CHANNEL = "noise-min";
- public static final String NOISE_MAX_CHANNEL = "noise-max";
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.luftdateninfo.internal.utils.Constants;
-
-/**
- * The {@link LuftdatenInfoConfiguration} class contains fields mapping thing configuration parameters.
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class LuftdatenInfoConfiguration {
-
- public int sensorid = Constants.UNDEF;
-
- public String ipAddress = Constants.EMPTY;
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.luftdateninfo.internal.handler.ConditionHandler;
-import org.openhab.binding.luftdateninfo.internal.handler.HTTPHandler;
-import org.openhab.binding.luftdateninfo.internal.handler.NoiseHandler;
-import org.openhab.binding.luftdateninfo.internal.handler.PMHandler;
-import org.openhab.core.io.net.http.HttpClientFactory;
-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.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link LuftdatenInfoHandlerFactory} is responsible for creating things and thing
- * handlers.
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-@Component(configurationPid = "binding.luftdateninfo", service = ThingHandlerFactory.class)
-public class LuftdatenInfoHandlerFactory extends BaseThingHandlerFactory {
- protected final Logger logger = LoggerFactory.getLogger(LuftdatenInfoHandlerFactory.class);
-
- @Activate
- public LuftdatenInfoHandlerFactory(final @Reference HttpClientFactory httpClientFactory) {
- HTTPHandler.init(httpClientFactory.getCommonHttpClient());
- }
-
- @Override
- public boolean supportsThingType(ThingTypeUID thingTypeUID) {
- return (thingTypeUID.equals(LuftdatenInfoBindingConstants.THING_TYPE_PARTICULATE)
- || thingTypeUID.equals(LuftdatenInfoBindingConstants.THING_TYPE_CONDITIONS)
- || thingTypeUID.equals(LuftdatenInfoBindingConstants.THING_TYPE_NOISE));
- }
-
- @Override
- protected @Nullable ThingHandler createHandler(Thing thing) {
- if (thing.getThingTypeUID().equals(LuftdatenInfoBindingConstants.THING_TYPE_PARTICULATE)) {
- return new PMHandler(thing);
- } else if (thing.getThingTypeUID().equals(LuftdatenInfoBindingConstants.THING_TYPE_CONDITIONS)) {
- return new ConditionHandler(thing);
- } else if (thing.getThingTypeUID().equals(LuftdatenInfoBindingConstants.THING_TYPE_NOISE)) {
- return new NoiseHandler(thing);
- }
- logger.info("Handler for {} not found", thing.getThingTypeUID());
- return null;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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.dto;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * The {@link LuftdatenInfo} class definition for Logging identification
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class Location {
- private int id;
- private String country;
- private String altitude;
- private String latitude;
- private String longitude;
- private int indoor;
- @SerializedName("exact_location")
- private int exactLocation;
-
- public int getId() {
- return id;
- }
-
- public void setId(int id) {
- this.id = id;
- }
-
- public String getCountry() {
- return country;
- }
-
- public void setCountry(String country) {
- this.country = country;
- }
-
- public String getAltitude() {
- return altitude;
- }
-
- public void setAltitude(String altitude) {
- this.altitude = altitude;
- }
-
- public String getLatitude() {
- return latitude;
- }
-
- public void setLatitude(String latitude) {
- this.latitude = latitude;
- }
-
- public String getLongitude() {
- return longitude;
- }
-
- public void setLongitude(String longitude) {
- this.longitude = longitude;
- }
-
- public Integer getIndoor() {
- return indoor;
- }
-
- public void setIndoor(int indoor) {
- this.indoor = indoor;
- }
-
- public int getExactLocation() {
- return exactLocation;
- }
-
- public void setExactLocation(int exactLocation) {
- this.exactLocation = exactLocation;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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.dto;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * The {@link Sensor} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class Sensor {
- private int id;
- private String pin;
- @SerializedName("sensor_type")
- private SensorType sensorType;
-
- public int getId() {
- return id;
- }
-
- public void setId(int id) {
- this.id = id;
- }
-
- public String getPin() {
- return pin;
- }
-
- public void setPin(String pin) {
- this.pin = pin;
- }
-
- public SensorType getSensoTypee() {
- return sensorType;
- }
-
- public void setSensorType(SensorType sensorType) {
- this.sensorType = sensorType;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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.dto;
-
-import java.util.List;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * The {@link SensorData} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class SensorData {
- private long id;
- private String timestamp;
- @SerializedName("sampling_rate")
- private int samplingRate;
- @SerializedName("sensordatavalues")
- private List<SensorDataValue> sensorDataValues;
- private Location location;
- private Sensor sensor;
-
- @Override
- public String toString() {
- return id + timestamp;
- }
-
- public long getId() {
- return id;
- }
-
- public void setId(long id) {
- this.id = id;
- }
-
- public String getTimeStamp() {
- return timestamp;
- }
-
- public void setTimeStamp(String timeStamp) {
- this.timestamp = timeStamp;
- }
-
- public int getSamplingRate() {
- return samplingRate;
- }
-
- public void setSamplingRate(int samplingRate) {
- this.samplingRate = samplingRate;
- }
-
- public List<SensorDataValue> getSensorDataValues() {
- return sensorDataValues;
- }
-
- public void setSensorDataValues(List<SensorDataValue> sensorDataValues) {
- this.sensorDataValues = sensorDataValues;
- }
-
- public Location getLocation() {
- return location;
- }
-
- public void setLocation(Location location) {
- this.location = location;
- }
-
- public Sensor getSensor() {
- return sensor;
- }
-
- public void setSensor(Sensor sensor) {
- this.sensor = sensor;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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.dto;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * The {@link SensorDataValue} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class SensorDataValue {
- private long id;
- @SerializedName("value_type")
- private String valueType;
- private String value;
-
- @Override
- public String toString() {
- return valueType + ":" + value;
- }
-
- public long getId() {
- return id;
- }
-
- public void setId(long id) {
- this.id = id;
- }
-
- public String getValueType() {
- return valueType;
- }
-
- public void setValueType(String valueType) {
- this.valueType = valueType;
- }
-
- public String getValue() {
- return value;
- }
-
- public void setValue(String value) {
- this.value = value;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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.dto;
-
-/**
- * The {@link SensorType} Data Transfer Object
- *
- * @author Bernd Weymann - Initial contribution
- */
-public class SensorType {
- private int id;
- private String manufacturer;
- private String name;
-
- public Integer getId() {
- return id;
- }
-
- public void setId(int id) {
- this.id = id;
- }
-
- public String getManufacturer() {
- return manufacturer;
- }
-
- public void setManufacturer(String manufacturer) {
- this.manufacturer = manufacturer;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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.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;
-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.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link PMHandler} is responsible for handling commands, which are
- * sent to one of the channels.
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public abstract class BaseSensorHandler extends BaseThingHandler {
- private static final LuftdatenInfoConfiguration DEFAULT_CONFIG = new LuftdatenInfoConfiguration();
- private static final String EMPTY = "";
-
- protected static final int REFRESH_INTERVAL_MIN = 5;
- protected final Logger logger = LoggerFactory.getLogger(BaseSensorHandler.class);
- protected LuftdatenInfoConfiguration config = DEFAULT_CONFIG;
- protected ConfigStatus configStatus = ConfigStatus.UNKNOWN;
- 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 {
- INTERNAL_SENSOR_OK,
- EXTERNAL_SENSOR_OK,
- IS_NULL,
- SENSOR_IS_NULL,
- SENSOR_ID_NEGATIVE,
- UNKNOWN
- };
-
- public enum UpdateStatus {
- OK,
- CONNECTION_ERROR,
- CONNECTION_EXCEPTION,
- VALUE_ERROR,
- VALUE_EMPTY,
- UNKNOWN
- }
-
- protected LifecycleStatus lifecycleStatus = LifecycleStatus.UNKNOWN;
-
- public enum LifecycleStatus {
- UNKNOWN,
- RUNNING,
- INITIALIZING,
- DISPOSED
- }
-
- public BaseSensorHandler(Thing thing) {
- super(thing);
- }
-
- @Override
- public void handleCommand(ChannelUID channelUID, Command command) {
- if (command instanceof RefreshType) {
- updateFromCache();
- }
- }
-
- @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.INTERNAL_SENSOR_OK || configStatus == ConfigStatus.EXTERNAL_SENSOR_OK) {
- // start getting values
- dataUpdate();
- } else {
- // config error, no further actions triggered - Thing Status visible in UI
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
- "Configuration not valid. Sensor ID as a number is mandatory!");
- }
- lifecycleStatus = LifecycleStatus.RUNNING;
- }
-
- private void startSchedule() {
- ScheduledFuture<?> localRefreshJob = refreshJob;
- if (localRefreshJob != null) {
- if (localRefreshJob.isCancelled()) {
- refreshJob = scheduler.scheduleWithFixedDelay(this::dataUpdate, 5, REFRESH_INTERVAL_MIN,
- TimeUnit.MINUTES);
- } // else - scheduler is already running!
- } else {
- refreshJob = scheduler.scheduleWithFixedDelay(this::dataUpdate, 5, REFRESH_INTERVAL_MIN, TimeUnit.MINUTES);
- }
- }
-
- @Override
- public void dispose() {
- ScheduledFuture<?> localRefreshJob = refreshJob;
- if (localRefreshJob != null) {
- localRefreshJob.cancel(true);
- }
- lifecycleStatus = LifecycleStatus.DISPOSED;
- }
-
- /**
- * Checks if config is valid - a) not null and b) sensorid is a number
- *
- * @param c
- * @return
- */
- private ConfigStatus checkConfig(@Nullable LuftdatenInfoConfiguration c) {
- if (c != null) {
- if (c.ipAddress != null && !Constants.EMPTY.equals(c.ipAddress)) {
- sensorUrl = Optional.of("http://" + c.ipAddress + "/data.json");
- return ConfigStatus.INTERNAL_SENSOR_OK;
- } else {
- 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;
- }
- }
-
- public LifecycleStatus getLifecycleStatus() {
- return lifecycleStatus;
- }
-
- protected void dataUpdate() {
- if (sensorUrl.isPresent()) {
- HTTPHandler.getHandler().request(sensorUrl.get(), this);
- }
- }
-
- public void onResponse(String 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);
- }
-
- public void onError(String errorReason) {
- statusUpdate(UpdateStatus.CONNECTION_EXCEPTION,
- errorReason + " / " + LocalDateTime.now().format(DateTimeUtils.DTF));
- }
-
- protected void statusUpdate(UpdateStatus updateStatus, String details) {
- if (updateStatus == UpdateStatus.OK) {
- updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, null);
- startSchedule();
- } else {
- switch (updateStatus) {
- case CONNECTION_ERROR:
- // start job even first update delivers no data - recovery is possible
- startSchedule();
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
- "Update failed due to Connection error. Trying to recover in next refresh");
- break;
- case CONNECTION_EXCEPTION:
- // start job even first update delivers a Connection Exception - recovery is possible
- startSchedule();
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, details);
- break;
- case VALUE_EMPTY:
- // start job even if first update delivers no values - recovery possible
- startSchedule();
- updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE,
- "No values delivered by Sensor. Trying to recover in next refresh");
- break;
- case VALUE_ERROR:
- // final status - values from sensor are wrong and manual check is needed
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
- "Sensor values doesn't match - please check if Sensor ID is delivering the correct Thing channel values");
- break;
- default:
- // final status - Configuration is wrong
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
- "Error during update - please check your config data");
- break;
- }
- }
- }
-
- @Override
- protected void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) {
- myThingStatus = status;
- super.updateStatus(status, statusDetail, description);
- }
-
- protected abstract UpdateStatus updateChannels(@Nullable String json);
-
- protected abstract void updateFromCache();
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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.handler;
-
-import static org.openhab.binding.luftdateninfo.internal.LuftdatenInfoBindingConstants.*;
-import static org.openhab.binding.luftdateninfo.internal.utils.Constants.*;
-import static org.openhab.core.library.unit.MetricPrefix.HECTO;
-
-import java.util.List;
-
-import javax.measure.quantity.Dimensionless;
-import javax.measure.quantity.Pressure;
-import javax.measure.quantity.Temperature;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.luftdateninfo.internal.dto.SensorDataValue;
-import org.openhab.binding.luftdateninfo.internal.utils.NumberUtils;
-import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.library.unit.SIUnits;
-import org.openhab.core.library.unit.Units;
-import org.openhab.core.thing.Thing;
-
-/**
- * The {@link ConditionHandler} is responsible for handling commands, which are
- * sent to one of the channels.
- *
- * @author Bernd Weymann - Initial contribution
- */
-@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, HECTO(SIUnits.PASCAL));
- protected QuantityType<Pressure> pressureSeaCache = QuantityType.valueOf(-1, HECTO(SIUnits.PASCAL));
-
- public ConditionHandler(Thing thing) {
- super(thing);
- }
-
- @Override
- public UpdateStatus updateChannels(@Nullable String json) {
- if (json != null) {
- List<SensorDataValue> valueList = HTTPHandler.getHandler().getLatestValues(json);
- if (valueList != null) {
- if (HTTPHandler.getHandler().isCondition(valueList)) {
- valueList.forEach(v -> {
- if (v.getValueType().endsWith(TEMPERATURE)) {
- temperatureCache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1),
- SIUnits.CELSIUS);
- updateState(TEMPERATURE_CHANNEL, temperatureCache);
- } else if (v.getValueType().endsWith(HUMIDITY)) {
- humidityCache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1), Units.PERCENT);
- updateState(HUMIDITY_CHANNEL, humidityCache);
- } 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().endsWith(PRESSURE_SEALEVEL)) {
- pressureSeaCache = QuantityType.valueOf(
- NumberUtils.round(NumberUtils.convert(v.getValue()) / 100, 1),
- HECTO(SIUnits.PASCAL));
- updateState(PRESSURE_SEA_CHANNEL, pressureSeaCache);
- }
- });
- return UpdateStatus.OK;
- } else {
- return UpdateStatus.VALUE_ERROR;
- }
- } else {
- return UpdateStatus.VALUE_EMPTY;
- }
- } else {
- return UpdateStatus.CONNECTION_ERROR;
- }
- }
-
- @Override
- protected void updateFromCache() {
- updateState(TEMPERATURE_CHANNEL, temperatureCache);
- updateState(HUMIDITY_CHANNEL, humidityCache);
- updateState(PRESSURE_CHANNEL, pressureCache);
- updateState(PRESSURE_SEA_CHANNEL, pressureSeaCache);
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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.handler;
-
-import static org.openhab.binding.luftdateninfo.internal.utils.Constants.*;
-
-import java.time.LocalDateTime;
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.client.util.BufferingResponseListener;
-import org.openhab.binding.luftdateninfo.internal.dto.SensorData;
-import org.openhab.binding.luftdateninfo.internal.dto.SensorDataValue;
-import org.openhab.binding.luftdateninfo.internal.utils.DateTimeUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.gson.Gson;
-
-/**
- * The {@link HTTPHandler} is responsible for HTTP requests and JSON handling
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class HTTPHandler {
- private final Logger logger = LoggerFactory.getLogger(HTTPHandler.class);
-
- private static final Gson GSON = new Gson();
- private static final HTTPHandler HTTP_HANDLER = new HTTPHandler();
-
- private static @Nullable HttpClient commonHttpClient;
-
- public static void init(HttpClient httpClient) {
- commonHttpClient = httpClient;
- }
-
- public static HTTPHandler getHandler() {
- return HTTP_HANDLER;
- }
-
- public synchronized void request(String url, BaseSensorHandler callback) {
- HttpClient localClient = commonHttpClient;
- if (localClient == null) {
- logger.warn("HTTP Client not initialized");
- } else {
- Request req = localClient.newRequest(url);
- req.timeout(15, TimeUnit.SECONDS).send(new BufferingResponseListener() {
- @NonNullByDefault({})
- @Override
- public void onComplete(org.eclipse.jetty.client.api.Result result) {
- if (result.getResponse().getStatus() != 200) {
- String failure;
- if (result.getResponse().getReason() != null) {
- failure = result.getResponse().getReason();
- } else {
- failure = result.getFailure().getMessage();
- }
- callback.onError(Objects.requireNonNullElse(failure, "Unknown error"));
- } else {
- callback.onResponse(getContentAsString());
- }
- }
- });
- }
- }
-
- public @Nullable List<SensorDataValue> getLatestValues(String response) {
- SensorData[] valueArray = GSON.fromJson(response, SensorData[].class);
- if (valueArray.length == 0) {
- return null;
- } else if (valueArray.length == 1) {
- SensorData v = valueArray[0];
- return v.getSensorDataValues();
- } else if (valueArray.length > 1) {
- // declare first item as latest
- SensorData latestData = valueArray[0];
- String latestTimeStr = latestData.getTimeStamp();
- LocalDateTime latestTime = DateTimeUtils.toDate(latestTimeStr);
- if (latestTime == null) {
- logDateConversionError(response, latestData);
- }
- for (int i = 1; i < valueArray.length; i++) {
- SensorData iterData = valueArray[i];
- String iterTimeStr = iterData.getTimeStamp();
- LocalDateTime iterTime = DateTimeUtils.toDate(iterTimeStr);
- if (iterTime == null) {
- logDateConversionError(response, latestData);
- }
- if (iterTime != null && latestTime != null) {
- if (latestTime.isBefore(iterTime)) {
- // found item is newer - take it as latest
- latestTime = iterTime;
- latestData = iterData;
- } // else - found item is older - nothing to do
-
- } else {
- logger.warn("One or two dates cannot be decoded 1) {} 2) {}", iterTimeStr, latestTimeStr);
- }
- }
- return latestData.getSensorDataValues();
- } else {
- return null;
- }
- }
-
- public void logDateConversionError(final String response, final Object dto) {
- logger.warn("Unable to get timestamp");
- logger.warn("Response: {}", response);
- String json = GSON.toJson(dto);
- logger.warn("GSon: {}", json);
- }
-
- public boolean isParticulate(@Nullable List<SensorDataValue> valueList) {
- if (valueList == null) {
- return false;
- }
- 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.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.endsWith(NOISE_EQ) || t.endsWith(NOISE_MAX) || t.endsWith(NOISE_MIN)).findAny()
- .isPresent();
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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.handler;
-
-import static org.openhab.binding.luftdateninfo.internal.LuftdatenInfoBindingConstants.*;
-import static org.openhab.binding.luftdateninfo.internal.utils.Constants.*;
-
-import java.util.List;
-
-import javax.measure.quantity.Dimensionless;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.luftdateninfo.internal.dto.SensorDataValue;
-import org.openhab.binding.luftdateninfo.internal.utils.NumberUtils;
-import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.library.unit.Units;
-import org.openhab.core.thing.Thing;
-
-/**
- * The {@link NoiseHandler} is responsible for handling commands, which are
- * sent to one of the channels.
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class NoiseHandler extends BaseSensorHandler {
- protected QuantityType<Dimensionless> noiseEQCache = QuantityType.valueOf(-1, Units.DECIBEL);
- protected QuantityType<Dimensionless> noiseMinCache = QuantityType.valueOf(-1, Units.DECIBEL);
- protected QuantityType<Dimensionless> noiseMaxCache = QuantityType.valueOf(-1, Units.DECIBEL);
-
- public NoiseHandler(Thing thing) {
- super(thing);
- }
-
- @Override
- public UpdateStatus updateChannels(@Nullable String json) {
- if (json != null) {
- List<SensorDataValue> valueList = HTTPHandler.getHandler().getLatestValues(json);
- if (valueList != null) {
- if (HTTPHandler.getHandler().isNoise(valueList)) {
- valueList.forEach(v -> {
- 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().endsWith(NOISE_MIN)) {
- noiseMinCache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1), Units.DECIBEL);
- updateState(NOISE_MIN_CHANNEL, noiseMinCache);
- } else if (v.getValueType().endsWith(NOISE_MAX)) {
- noiseMaxCache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1), Units.DECIBEL);
- updateState(NOISE_MAX_CHANNEL, noiseMaxCache);
- }
- });
- return UpdateStatus.OK;
- } else {
- return UpdateStatus.VALUE_ERROR;
- }
- } else {
- return UpdateStatus.VALUE_EMPTY;
- }
- } else {
- return UpdateStatus.CONNECTION_ERROR;
- }
- }
-
- @Override
- protected void updateFromCache() {
- updateState(NOISE_EQ_CHANNEL, noiseEQCache);
- updateState(NOISE_MIN_CHANNEL, noiseMinCache);
- updateState(NOISE_MAX_CHANNEL, noiseMaxCache);
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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.handler;
-
-import static org.openhab.binding.luftdateninfo.internal.LuftdatenInfoBindingConstants.*;
-import static org.openhab.binding.luftdateninfo.internal.utils.Constants.*;
-
-import java.util.List;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.luftdateninfo.internal.dto.SensorDataValue;
-import org.openhab.binding.luftdateninfo.internal.utils.NumberUtils;
-import org.openhab.core.library.dimension.Density;
-import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.library.unit.Units;
-import org.openhab.core.thing.Thing;
-
-/**
- * The {@link PMHandler} is responsible for handling commands, which are
- * sent to one of the channels.
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class PMHandler extends BaseSensorHandler {
-
- protected QuantityType<Density> pm25Cache = QuantityType.valueOf(-1, Units.MICROGRAM_PER_CUBICMETRE);
- protected QuantityType<Density> pm100Cache = QuantityType.valueOf(-1, Units.MICROGRAM_PER_CUBICMETRE);
-
- public PMHandler(Thing thing) {
- super(thing);
- }
-
- @Override
- public UpdateStatus updateChannels(@Nullable String json) {
- if (json != null) {
- List<SensorDataValue> valueList = HTTPHandler.getHandler().getLatestValues(json);
- if (valueList != null) {
- if (HTTPHandler.getHandler().isParticulate(valueList)) {
- valueList.forEach(v -> {
- 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().endsWith(P2)) {
- pm25Cache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1),
- Units.MICROGRAM_PER_CUBICMETRE);
- updateState(PM25_CHANNEL, pm25Cache);
- }
- });
- return UpdateStatus.OK;
- } else {
- return UpdateStatus.VALUE_ERROR;
- }
- } else {
- return UpdateStatus.VALUE_EMPTY;
- }
- } else {
- return UpdateStatus.CONNECTION_ERROR;
- }
- }
-
- @Override
- protected void updateFromCache() {
- updateState(PM25_CHANNEL, pm25Cache);
- updateState(PM100_CHANNEL, pm100Cache);
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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;
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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 java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
-import java.time.format.DateTimeParseException;
-import java.util.Locale;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link DateTimeUtils} class provides helpers for converting Dates and Times.
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class DateTimeUtils {
- public static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH);
- private static final Logger LOGGER = LoggerFactory.getLogger(DateTimeUtils.class);
-
- public static synchronized @Nullable LocalDateTime toDate(String dateTime) {
- try {
- return LocalDateTime.from(DTF.parse(dateTime));
-
- } catch (DateTimeParseException e) {
- LOGGER.debug("Unable to parse date {}", dateTime);
- return null;
- }
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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 NumberUtils} class provides helpers for converting Numbers.
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class NumberUtils {
- public static final double UNDEF = Double.NaN;
-
- public static double round(Object o, int places) {
- double value = convert(o);
-
- // for negative places return plain number
- if (places < 0) {
- return value;
- }
-
- long factor = (long) Math.pow(10, places);
- value = value * factor;
- long tmp = Math.round(value);
- return (double) tmp / factor;
- }
-
- public static double convert(Object o) {
- // ensure value not null
- double value = UNDEF;
- if (o instanceof Number) {
- value = ((Number) o).doubleValue();
- } else if (o instanceof String) {
- value = Double.parseDouble(o.toString());
- }
- return value;
- }
-}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<addon:addon id="luftdateninfo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
- xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
-
- <type>binding</type>
- <name>LuftdatenInfo Binding</name>
- <description>Binding to integrate DIY Sensors from luftdaten.info Sensor Community</description>
- <connection>hybrid</connection>
-
-</addon:addon>
+++ /dev/null
-# add-on
-
-addon.luftdateninfo.name = LuftdatenInfo Binding
-addon.luftdateninfo.description = Binding to integrate DIY Sensors from luftdaten.info Sensor Community
-
-# thing types
-
-thing-type.luftdateninfo.conditions.label = Condition Sensor
-thing-type.luftdateninfo.conditions.description = Sensor to measure Temperature and Humidity conditions
-thing-type.luftdateninfo.noise.label = Noise Sensor
-thing-type.luftdateninfo.noise.description = Sensor to measure noise on location
-thing-type.luftdateninfo.particulate.label = Particulate Sensor
-thing-type.luftdateninfo.particulate.description = Sensor to measure Particulate Matter (PM)
-
-# thing types config
-
-thing-type.config.luftdateninfo.conditions.ipAddress.label = Internal IP Address
-thing-type.config.luftdateninfo.conditions.ipAddress.description = Local IP address of your personal owned sensor
-thing-type.config.luftdateninfo.conditions.sensorid.label = External Sensor ID
-thing-type.config.luftdateninfo.conditions.sensorid.description = Sensor ID from https://deutschland.maps.sensor.community/
-thing-type.config.luftdateninfo.noise.ipAddress.label = Internal IP Address
-thing-type.config.luftdateninfo.noise.ipAddress.description = Local IP address of your personal owned sensor
-thing-type.config.luftdateninfo.noise.sensorid.label = External Sensor ID
-thing-type.config.luftdateninfo.noise.sensorid.description = Sensor ID from https://deutschland.maps.sensor.community/
-thing-type.config.luftdateninfo.particulate.ipAddress.label = Internal IP Address
-thing-type.config.luftdateninfo.particulate.ipAddress.description = Local IP address of your personal owned sensor
-thing-type.config.luftdateninfo.particulate.sensorid.label = External Sensor ID
-thing-type.config.luftdateninfo.particulate.sensorid.description = Sensor ID from https://deutschland.maps.sensor.community/
-
-# channel types
-
-channel-type.luftdateninfo.hum-channel.label = Humidity
-channel-type.luftdateninfo.hum-channel.description = Humidity from the selected Sensor ID
-channel-type.luftdateninfo.noise-eq-channel.label = Average Noise
-channel-type.luftdateninfo.noise-eq-channel.description = Average noise level from the selected Sensor ID
-channel-type.luftdateninfo.noise-max-channel.label = Maximum Noise
-channel-type.luftdateninfo.noise-max-channel.description = Maximum noise level (last 2.5 minutes) from the selected Sensor ID
-channel-type.luftdateninfo.noise-min-channel.label = Minimum Noise
-channel-type.luftdateninfo.noise-min-channel.description = Minimum noise level (last 2.5 minutes) from the selected Sensor ID
-channel-type.luftdateninfo.pm100-channel.label = Particulate Matter category 10.0
-channel-type.luftdateninfo.pm25-channel.label = Particulate Matter category 2.5
-channel-type.luftdateninfo.pressure-channel.label = Atmospheric Pressure
-channel-type.luftdateninfo.pressure-channel.description = Atmospheric Pressure from the selected Sensor ID
-channel-type.luftdateninfo.pressure-sea-channel.label = Atmospheric Pressure Sea Level
-channel-type.luftdateninfo.pressure-sea-channel.description = Atmospheric Pressure at sea level from the selected Sensor ID
-channel-type.luftdateninfo.temp-channel.label = Temperature
-channel-type.luftdateninfo.temp-channel.description = Temperature from the selected Sensor ID
+++ /dev/null
-# add-on
-
-addon.luftdateninfo.name = LuftdatenInfo Binding
-addon.luftdateninfo.description = Das Binding stellt die Daten der Eigenbau-Sensoren von LuftdatenInfo zur Verfügung
-
-# thing types
-
-thing-type.luftdateninfo.conditions.label = Umweltsensor
-thing-type.luftdateninfo.conditions.description = Messung der Temperatur, Luftfeuchtigkeit und Luftdruck
-thing-type.luftdateninfo.noise.label = Lärmsensor
-thing-type.luftdateninfo.noise.description = Messung der Lärmbelastung in der Umgebung
-thing-type.luftdateninfo.particulate.label = Feinstaubsensor
-thing-type.luftdateninfo.particulate.description = Messung der Feinstaubbelastung in der Umgebung
-
-# thing types config
-
-thing-type.config.luftdateninfo.conditions.ipAddress.label = Interne IP-Adresse
-thing-type.config.luftdateninfo.conditions.ipAddress.description = Lokale IP-Adresse Ihres persönlichen Sensors
-thing-type.config.luftdateninfo.conditions.sensorid.label = Externe Sensor-ID
-thing-type.config.luftdateninfo.conditions.sensorid.description = Sensor-ID von https\://deutschland.maps.sensor.community/
-thing-type.config.luftdateninfo.noise.ipAddress.label = Interne IP-Adresse
-thing-type.config.luftdateninfo.noise.ipAddress.description = Lokale IP-Adresse Ihres persönlichen Sensors
-thing-type.config.luftdateninfo.noise.sensorid.label = Externe Sensor-ID
-thing-type.config.luftdateninfo.noise.sensorid.description = Sensor-ID von https\://deutschland.maps.sensor.community/
-thing-type.config.luftdateninfo.particulate.ipAddress.label = Interne IP-Adresse
-thing-type.config.luftdateninfo.particulate.ipAddress.description = Lokale IP-Adresse Ihres persönlichen Sensors
-thing-type.config.luftdateninfo.particulate.sensorid.label = Externe Sensor-ID
-thing-type.config.luftdateninfo.particulate.sensorid.description = Sensor-ID von https\://deutschland.maps.sensor.community/
-
-# channel types
-
-channel-type.luftdateninfo.hum-channel.label = Luftfeuchtigkeit
-channel-type.luftdateninfo.hum-channel.description = Luftfeuchtigkeit der ausgewählten Sensor-ID
-channel-type.luftdateninfo.noise-eq-channel.label = Durchschnittlicher Lärmpegel
-channel-type.luftdateninfo.noise-eq-channel.description = Durchschnittlicher Rauschpegel der ausgewählten Sensor-ID
-channel-type.luftdateninfo.noise-max-channel.label = Maximaler Lärmpegel
-channel-type.luftdateninfo.noise-max-channel.description = Maximaler Rauschpegel (letzte 2,5 Minuten) der ausgewählten Sensor-ID
-channel-type.luftdateninfo.noise-min-channel.label = Minimaler Lärmpegel
-channel-type.luftdateninfo.noise-min-channel.description = Minimaler Rauschpegel (letzte 2,5 Minuten) der ausgewählten Sensor-ID
-channel-type.luftdateninfo.pm100-channel.label = Feinstaub der Kategorie PM 10.0
-channel-type.luftdateninfo.pm25-channel.label = Feinstaub der Kategorie PM 2.5
-channel-type.luftdateninfo.pressure-channel.label = Atmosphärischer Druck
-channel-type.luftdateninfo.pressure-channel.description = Atmosphärischer Druck der ausgewählten Sensor-ID
-channel-type.luftdateninfo.pressure-sea-channel.label = Atmosphärischer Druck Auf Meereshöhe
-channel-type.luftdateninfo.pressure-sea-channel.description = Atmosphärischer Druck auf Seehöhe der ausgewählten Sensor-ID
-channel-type.luftdateninfo.temp-channel.label = Temperatur
-channel-type.luftdateninfo.temp-channel.description = Temperatur der ausgewählten Sensor-ID
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="luftdateninfo"
- 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">
-
- <thing-type id="particulate">
- <label>Particulate Sensor</label>
- <description>Sensor to measure Particulate Matter (PM)</description>
-
- <channels>
- <channel id="pm25" typeId="pm25-channel"/>
- <channel id="pm100" typeId="pm100-channel"/>
- </channels>
-
- <config-description>
- <parameter name="ipAddress" type="text">
- <context>network-address</context>
- <label>Internal IP Address</label>
- <description>Local IP address of your personal owned sensor</description>
- </parameter>
- <parameter name="sensorid" type="integer">
- <label>External Sensor ID</label>
- <description>Sensor ID from https://deutschland.maps.sensor.community/</description>
- </parameter>
- </config-description>
- </thing-type>
-
- <thing-type id="conditions">
- <label>Condition Sensor</label>
- <description>Sensor to measure Temperature and Humidity conditions</description>
-
- <channels>
- <channel id="temperature" typeId="temp-channel"/>
- <channel id="humidity" typeId="hum-channel"/>
- <channel id="pressure" typeId="pressure-channel"/>
- <channel id="pressure-sea" typeId="pressure-sea-channel"/>
- </channels>
-
- <config-description>
- <parameter name="ipAddress" type="text">
- <context>network-address</context>
- <label>Internal IP Address</label>
- <description>Local IP address of your personal owned sensor</description>
- </parameter>
- <parameter name="sensorid" type="integer">
- <label>External Sensor ID</label>
- <description>Sensor ID from https://deutschland.maps.sensor.community/</description>
- </parameter>
- </config-description>
- </thing-type>
-
- <thing-type id="noise">
- <label>Noise Sensor</label>
- <description>Sensor to measure noise on location</description>
-
- <channels>
- <channel id="noise-eq" typeId="noise-eq-channel"/>
- <channel id="noise-min" typeId="noise-min-channel"/>
- <channel id="noise-max" typeId="noise-max-channel"/>
- </channels>
-
- <config-description>
- <parameter name="ipAddress" type="text">
- <context>network-address</context>
- <label>Internal IP Address</label>
- <description>Local IP address of your personal owned sensor</description>
- </parameter>
- <parameter name="sensorid" type="integer">
- <label>External Sensor ID</label>
- <description>Sensor ID from https://deutschland.maps.sensor.community/</description>
- </parameter>
- </config-description>
- </thing-type>
-
- <channel-type id="pm25-channel">
- <item-type>Number:Density</item-type>
- <label>Particulate Matter category 2.5</label>
- <state pattern="%.1f %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="pm100-channel">
- <item-type>Number:Density</item-type>
- <label>Particulate Matter category 10.0</label>
- <state pattern="%.1f %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="temp-channel">
- <item-type>Number:Temperature</item-type>
- <label>Temperature</label>
- <description>Temperature from the selected Sensor ID</description>
- <state pattern="%.1f %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="hum-channel">
- <item-type>Number:Dimensionless</item-type>
- <label>Humidity</label>
- <description>Humidity from the selected Sensor ID</description>
- <state pattern="%.1f %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="pressure-channel">
- <item-type>Number:Pressure</item-type>
- <label>Atmospheric Pressure</label>
- <description>Atmospheric Pressure from the selected Sensor ID</description>
- <state pattern="%.1f %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="pressure-sea-channel">
- <item-type>Number:Pressure</item-type>
- <label>Atmospheric Pressure Sea Level</label>
- <description>Atmospheric Pressure at sea level from the selected Sensor ID</description>
- <state pattern="%.1f %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="noise-eq-channel">
- <item-type>Number:Dimensionless</item-type>
- <label>Average Noise</label>
- <description>Average noise level from the selected Sensor ID</description>
- <state pattern="%.1f %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="noise-min-channel">
- <item-type>Number:Dimensionless</item-type>
- <label>Minimum Noise</label>
- <description>Minimum noise level (last 2.5 minutes) from the selected Sensor ID</description>
- <state pattern="%.1f %unit%" readOnly="true"/>
- </channel-type>
- <channel-type id="noise-max-channel">
- <item-type>Number:Dimensionless</item-type>
- <label>Maximum Noise</label>
- <description>Maximum noise level (last 2.5 minutes) from the selected Sensor ID</description>
- <state pattern="%.1f %unit%" readOnly="true"/>
- </channel-type>
-</thing:thing-descriptions>
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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;
-
-import static org.junit.jupiter.api.Assertions.*;
-import static org.openhab.core.library.unit.MetricPrefix.HECTO;
-
-import java.util.HashMap;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.junit.jupiter.api.Test;
-import org.openhab.binding.luftdateninfo.internal.handler.BaseSensorHandler.UpdateStatus;
-import org.openhab.binding.luftdateninfo.internal.mock.ConditionHandlerExtension;
-import org.openhab.binding.luftdateninfo.internal.mock.ThingMock;
-import org.openhab.binding.luftdateninfo.internal.util.FileReader;
-import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.library.unit.SIUnits;
-import org.openhab.core.library.unit.Units;
-
-/**
- * The {@link ConditionHandlerTest} Test Condition Handler updates
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class ConditionHandlerTest {
-
- @Test
- public void testValidNoPressureUpdate() {
- ThingMock t = new ThingMock();
-
- HashMap<String, Object> properties = new HashMap<String, Object>();
- // String sensorid taken from thing-types.xml
- properties.put("sensorid", 12345);
- t.setConfiguration(properties);
-
- ConditionHandlerExtension condHandler = new ConditionHandlerExtension(t);
- String pmJson = FileReader.readFileInString("src/test/resources/condition-result-no-pressure.json");
- if (pmJson != null) {
- UpdateStatus result = condHandler.updateChannels(pmJson);
- assertEquals(UpdateStatus.OK, result, "Valid update");
- assertEquals(QuantityType.valueOf(22.7, SIUnits.CELSIUS), condHandler.getTemperature(), "Temperature");
- assertEquals(QuantityType.valueOf(61., Units.PERCENT), condHandler.getHumidity(), "Humidity");
- assertEquals(QuantityType.valueOf(-1, HECTO(SIUnits.PASCAL)), condHandler.getPressure(), "Pressure");
- assertEquals(QuantityType.valueOf(-1, HECTO(SIUnits.PASCAL)), condHandler.getPressureSea(), "Pressure Sea");
- } else {
- assertTrue(false);
- }
- }
-
- @Test
- public void testValidWithPressureUpdate() {
- ThingMock t = new ThingMock();
-
- HashMap<String, Object> properties = new HashMap<String, Object>();
- // String sensorid taken from thing-types.xml
- properties.put("sensorid", 12345);
- t.setConfiguration(properties);
-
- ConditionHandlerExtension condHandler = new ConditionHandlerExtension(t);
- String pmJson = FileReader.readFileInString("src/test/resources/condition-result-plus-pressure.json");
- if (pmJson != null) {
- UpdateStatus result = condHandler.updateChannels(pmJson);
- assertEquals(UpdateStatus.OK, result, "Valid update");
- assertEquals(QuantityType.valueOf(21.5, SIUnits.CELSIUS), condHandler.getTemperature(), "Temperature");
- assertEquals(QuantityType.valueOf(58.5, Units.PERCENT), condHandler.getHumidity(), "Humidity");
- assertEquals(QuantityType.valueOf(1002.0, HECTO(SIUnits.PASCAL)), condHandler.getPressure(), "Pressure");
- assertEquals(QuantityType.valueOf(1019.7, HECTO(SIUnits.PASCAL)), condHandler.getPressureSea(),
- "Pressure Sea");
- } else {
- assertTrue(false);
- }
- }
-
- @Test
- public void testInvalidUpdate() {
- ThingMock t = new ThingMock();
-
- HashMap<String, Object> properties = new HashMap<String, Object>();
- // String sensorid taken from thing-types.xml
- properties.put("sensorid", 12345);
- t.setConfiguration(properties);
-
- ConditionHandlerExtension condHandler = new ConditionHandlerExtension(t);
- String pmJson = FileReader.readFileInString("src/test/resources/noise-result.json");
- if (pmJson != null) {
- UpdateStatus result = condHandler.updateChannels(pmJson);
- assertEquals(UpdateStatus.VALUE_ERROR, result, "Valid update");
- } else {
- assertTrue(false);
- }
- }
-
- @Test
- public void testEmptyUpdate() {
- ThingMock t = new ThingMock();
-
- HashMap<String, Object> properties = new HashMap<String, Object>();
- // String sensorid taken from thing-types.xml
- properties.put("sensorid", 12345);
- t.setConfiguration(properties);
-
- ConditionHandlerExtension condHandler = new ConditionHandlerExtension(t);
- UpdateStatus result = condHandler.updateChannels("[]");
- assertEquals(UpdateStatus.VALUE_EMPTY, result, "Valid update");
- }
-
- @Test
- public void testNullUpdate() {
- ThingMock t = new ThingMock();
-
- HashMap<String, Object> properties = new HashMap<String, Object>();
- // String sensorid taken from thing-types.xml
- properties.put("sensorid", 12345);
- t.setConfiguration(properties);
-
- ConditionHandlerExtension condHandler = new ConditionHandlerExtension(t);
- UpdateStatus result = condHandler.updateChannels(null);
- assertEquals(UpdateStatus.CONNECTION_ERROR, result, "Valid update");
- }
-
- @Test
- public void testInternalUpdate() {
- ThingMock t = new ThingMock();
-
- HashMap<String, Object> properties = new HashMap<String, Object>();
- // String sensorid taken from thing-types.xml
- properties.put("ipAddress", "192.168.178.1");
- t.setConfiguration(properties);
-
- ConditionHandlerExtension condHandler = new ConditionHandlerExtension(t);
- String pmJson = FileReader.readFileInString("src/test/resources/internal-data.json");
- if (pmJson != null) {
- UpdateStatus result = condHandler.updateChannels("[" + pmJson + "]");
- assertEquals(UpdateStatus.OK, result, "Valid update");
- assertEquals(QuantityType.valueOf(17.6, SIUnits.CELSIUS), condHandler.getTemperature(), "Temperature");
- assertEquals(QuantityType.valueOf(57.8, Units.PERCENT), condHandler.getHumidity(), "Humidity");
- assertEquals(QuantityType.valueOf(986.8, HECTO(SIUnits.PASCAL)), condHandler.getPressure(), "Pressure");
- assertEquals(QuantityType.valueOf(-1, HECTO(SIUnits.PASCAL)), condHandler.getPressureSea(), "Pressure Sea");
- } else {
- assertTrue(false);
- }
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import java.util.List;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.junit.jupiter.api.Test;
-import org.openhab.binding.luftdateninfo.internal.dto.SensorData;
-import org.openhab.binding.luftdateninfo.internal.dto.SensorDataValue;
-import org.openhab.binding.luftdateninfo.internal.util.FileReader;
-import org.openhab.binding.luftdateninfo.internal.utils.Constants;
-
-import com.google.gson.Gson;
-
-/**
- * The {@link DTOTest} Data Transfer Object - test conversions
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class DTOTest {
-
- @Test
- public void testConditions() {
- String result = FileReader.readFileInString("src/test/resources/condition-result-no-pressure.json");
- Gson gson = new Gson();
- SensorData[] valueArray = gson.fromJson(result, SensorData[].class);
- // System.out.println(valueArray.length);
- assertEquals(2, valueArray.length, "Array size");
-
- SensorData d = valueArray[0];
- // Assure latest data is taken
- String dateStr = d.getTimeStamp();
- if ("2020-06-09 06:38:08".equals(dateStr)) {
- // take newer one
- d = valueArray[1];
- }
- List<SensorDataValue> sensorDataVaueList = d.getSensorDataValues();
- assertNotNull(d);
- sensorDataVaueList.forEach(v -> {
- if (Constants.TEMPERATURE.equals(v.getValueType())) {
- assertEquals("22.70", v.getValue(), "Temperature");
- } else if (Constants.HUMIDITY.equals(v.getValueType())) {
- assertEquals("61.00", v.getValue(), "Humidity");
- }
- });
- }
-
- @Test
- public void testDecoding() {
- String result = FileReader.readFileInString("src/test/resources/condition-result-no-pressure.json");
- Gson gson = new Gson();
- SensorData[] valueArray = gson.fromJson(result, SensorData[].class);
- // System.out.println(valueArray.length);
- assertEquals(2, valueArray.length, "Array size");
-
- SensorData d = valueArray[0];
- // Assure latest data is taken
- String dateStr = d.getTimeStamp();
- if (dateStr.equals("2020-06-09 06:38:08")) {
- // take newer one
- d = valueArray[1];
- }
-
- // test decoding a small part
- String json = gson.toJson(d);
- // System.out.println(json);
- // check if correct timestamp is included
- assertTrue(json.contains("\"timestamp\":\"2020-06-09 06:40:34\""));
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import java.util.List;
-import java.util.Objects;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.openhab.binding.luftdateninfo.internal.dto.SensorDataValue;
-import org.openhab.binding.luftdateninfo.internal.handler.HTTPHandler;
-import org.openhab.binding.luftdateninfo.internal.util.FileReader;
-
-/**
- * The {@link HTTPHandlerEvalTest} test all evaluations on SensorDataValues
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class HTTPHandlerEvalTest {
-
- private @Nullable List<SensorDataValue> conditions;
- private @Nullable List<SensorDataValue> particulate;
- private @Nullable List<SensorDataValue> noise;
- private HTTPHandler http = new HTTPHandler();
-
- @BeforeEach
- public void setUp() {
- String conditionsStr = FileReader.readFileInString("src/test/resources/condition-result-no-pressure.json");
- assertNotNull(conditionsStr);
- Objects.requireNonNull(conditionsStr);
- conditions = http.getLatestValues(conditionsStr);
-
- String particulateStr = FileReader.readFileInString("src/test/resources/pm-result.json");
- assertNotNull(particulateStr);
- Objects.requireNonNull(particulateStr);
- particulate = http.getLatestValues(particulateStr);
-
- String noiseStr = FileReader.readFileInString("src/test/resources/noise-result.json");
- assertNotNull(noiseStr);
- Objects.requireNonNull(noiseStr);
- noise = http.getLatestValues(noiseStr);
- }
-
- @Test
- public void testIsCondition() {
- assertTrue(http.isCondition(conditions));
- assertFalse(http.isCondition(particulate));
- assertFalse(http.isCondition(noise));
- assertFalse(http.isCondition(null));
- }
-
- @Test
- public void testIsParticulate() {
- assertFalse(http.isParticulate(conditions));
- assertTrue(http.isParticulate(particulate));
- assertFalse(http.isParticulate(noise));
- assertFalse(http.isParticulate(null));
- }
-
- @Test
- public void testIsNoise() {
- assertFalse(http.isNoise(conditions));
- assertFalse(http.isNoise(particulate));
- assertTrue(http.isNoise(noise));
- assertFalse(http.isNoise(null));
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import java.util.List;
-import java.util.Objects;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.junit.jupiter.api.Test;
-import org.openhab.binding.luftdateninfo.internal.dto.SensorDataValue;
-import org.openhab.binding.luftdateninfo.internal.handler.HTTPHandler;
-import org.openhab.binding.luftdateninfo.internal.util.FileReader;
-import org.openhab.binding.luftdateninfo.internal.utils.Constants;
-
-/**
- * The {@link HTTPHandlerValueTest} test values decoding of HTTPHandler
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class HTTPHandlerValueTest {
- private HTTPHandler http = new HTTPHandler();
-
- /**
- * test if really the latest values are returned
- * resource1 is json with ordering according to time while resource2 the entries flipped
- */
- @Test
- public void testValueDecoding() {
- String resource1 = FileReader.readFileInString("src/test/resources/condition-result-no-pressure.json");
- assertNotNull(resource1);
- Objects.requireNonNull(resource1);
- List<SensorDataValue> l = http.getLatestValues(resource1);
- assertNotNull(l);
- Objects.requireNonNull(l);
- l.forEach(sd -> {
- testSensorValue(sd);
- });
-
- String resource2 = FileReader
- .readFileInString("src/test/resources/condition-result-no-pressure-flipped-values.json");
- assertNotNull(resource2);
- Objects.requireNonNull(resource2);
- l = http.getLatestValues(resource2);
- assertNotNull(l);
- Objects.requireNonNull(l);
- l.forEach(sd -> {
- testSensorValue(sd);
- });
- }
-
- private void testSensorValue(SensorDataValue s) {
- if (s.getValueType().equals(Constants.TEMPERATURE)) {
- assertEquals("22.70", s.getValue(), "Temperature resource 1");
- } else if (s.getValueType().equals(Constants.HUMIDITY)) {
- assertEquals("61.00", s.getValue(), "Humidity resource 1");
- } else {
- assertTrue(false);
- }
- // System.out.println(s.getValue_type() + ":" + s.getValue());
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import java.util.HashMap;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.junit.jupiter.api.Test;
-import org.openhab.binding.luftdateninfo.internal.handler.BaseSensorHandler.UpdateStatus;
-import org.openhab.binding.luftdateninfo.internal.mock.NoiseHandlerExtension;
-import org.openhab.binding.luftdateninfo.internal.mock.ThingMock;
-import org.openhab.binding.luftdateninfo.internal.util.FileReader;
-import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.library.unit.Units;
-
-/**
- * The {@link NoiseHandlerTest} Test Noise Handler updates
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class NoiseHandlerTest {
-
- @Test
- public void testValidUpdate() {
- ThingMock t = new ThingMock();
-
- HashMap<String, Object> properties = new HashMap<String, Object>();
- // String sensorid taken from thing-types.xml
- properties.put("sensorid", 12345);
- t.setConfiguration(properties);
-
- NoiseHandlerExtension noiseHandler = new NoiseHandlerExtension(t);
- String pmJson = FileReader.readFileInString("src/test/resources/noise-result.json");
- if (pmJson != null) {
- UpdateStatus result = noiseHandler.updateChannels(pmJson);
- assertEquals(UpdateStatus.OK, result, "Valid update");
- assertEquals(QuantityType.valueOf(51.0, Units.DECIBEL), noiseHandler.getNoiseEQCache(), "Noise EQ");
- assertEquals(QuantityType.valueOf(47.2, Units.DECIBEL), noiseHandler.getNoiseMinCache(), "Noise Min");
- assertEquals(QuantityType.valueOf(57.0, Units.DECIBEL), noiseHandler.getNoiseMaxCache(), "Noise Max");
- } else {
- assertTrue(false);
- }
- }
-
- @Test
- public void testInvalidUpdate() {
- ThingMock t = new ThingMock();
-
- HashMap<String, Object> properties = new HashMap<String, Object>();
- // String sensorid taken from thing-types.xml
- properties.put("sensorid", 12345);
- t.setConfiguration(properties);
-
- NoiseHandlerExtension noiseHandler = new NoiseHandlerExtension(t);
- String pmJson = FileReader.readFileInString("src/test/resources/condition-result-no-pressure.json");
- if (pmJson != null) {
- UpdateStatus result = noiseHandler.updateChannels(pmJson);
- assertEquals(UpdateStatus.VALUE_ERROR, result, "Valid update");
- assertEquals(QuantityType.valueOf(-1, Units.DECIBEL), noiseHandler.getNoiseEQCache(), "Values undefined");
- assertEquals(QuantityType.valueOf(-1, Units.DECIBEL), noiseHandler.getNoiseMinCache(), "Values undefined");
- assertEquals(QuantityType.valueOf(-1, Units.DECIBEL), noiseHandler.getNoiseMaxCache(), "Values undefined");
- } else {
- assertTrue(false);
- }
- }
-
- @Test
- public void testEmptyUpdate() {
- ThingMock t = new ThingMock();
-
- HashMap<String, Object> properties = new HashMap<String, Object>();
- // String sensorid taken from thing-types.xml
- properties.put("sensorid", 12345);
- t.setConfiguration(properties);
-
- NoiseHandlerExtension noiseHandler = new NoiseHandlerExtension(t);
- UpdateStatus result = noiseHandler.updateChannels("[]");
- assertEquals(UpdateStatus.VALUE_EMPTY, result, "Valid update");
- }
-
- @Test
- public void testNullUpdate() {
- ThingMock t = new ThingMock();
-
- HashMap<String, Object> properties = new HashMap<String, Object>();
- // String sensorid taken from thing-types.xml
- properties.put("sensorid", 12345);
- t.setConfiguration(properties);
-
- NoiseHandlerExtension noiseHandler = new NoiseHandlerExtension(t);
- UpdateStatus result = noiseHandler.updateChannels(null);
- assertEquals(UpdateStatus.CONNECTION_ERROR, result, "Valid update");
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.junit.jupiter.api.Test;
-import org.openhab.binding.luftdateninfo.internal.utils.NumberUtils;
-
-/**
- * The {@link NumberTest} Test rounding and converting Numbers
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class NumberTest {
-
- @Test
- public void testRoundingUp() {
- double d1 = 1.95;
- double d1r2 = NumberUtils.round(d1, 2);
- assertEquals("1.95", Double.toString(d1r2), "Double 1.95, 2 places");
- // System.out.println("D1R2 " + d1r2);
- double d1r1 = NumberUtils.round(d1, 1);
- // System.out.println("D1R1 " + d1r1);
- assertEquals("2.0", Double.toString(d1r1), "Double 1.95, 1 place");
- }
-
- @Test
- public void testRoundingDown() {
- double d1 = 1.94;
- double d1r2 = NumberUtils.round(d1, 2);
- assertEquals("1.94", Double.toString(d1r2), "Double 1.94, 2 places");
- // System.out.println("D1R2 " + d1r2);
- double d1r1 = NumberUtils.round(d1, 1);
- // System.out.println("D1R1 " + d1r1);
- assertEquals("1.9", Double.toString(d1r1), "Double 1.94, 1 place");
- }
-
- @Test
- public void testStringNumbers() {
- String d1 = "1.94";
- double d1r2 = NumberUtils.round(d1, 2);
- assertEquals("1.94", Double.toString(d1r2), "Double 1.94, 2 places");
- // System.out.println("D1R2 " + d1r2);
- double d1r1 = NumberUtils.round(d1, 1);
- // System.out.println("D1R1 " + d1r1);
- assertEquals("1.9", Double.toString(d1r1), "Double 1.94, 1 place");
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import java.util.HashMap;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.junit.jupiter.api.Test;
-import org.openhab.binding.luftdateninfo.internal.handler.BaseSensorHandler.ConfigStatus;
-import org.openhab.binding.luftdateninfo.internal.handler.BaseSensorHandler.LifecycleStatus;
-import org.openhab.binding.luftdateninfo.internal.handler.BaseSensorHandler.UpdateStatus;
-import org.openhab.binding.luftdateninfo.internal.mock.PMHandlerExtension;
-import org.openhab.binding.luftdateninfo.internal.mock.ThingMock;
-import org.openhab.binding.luftdateninfo.internal.util.FileReader;
-import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.library.unit.Units;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link PMHandlerTest} Test Particualte Matter Handler - Config and updates
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class PMHandlerTest {
- private Logger logger = LoggerFactory.getLogger(PMHandlerTest.class);
-
- @Test
- public void testValidConfigStatus() {
- ThingMock t = new ThingMock();
-
- HashMap<String, Object> properties = new HashMap<String, Object>();
- // String sensorid taken from thing-types.xml
- properties.put("sensorid", 12345);
- t.setConfiguration(properties);
-
- PMHandlerExtension pmHandler = new PMHandlerExtension(t);
- pmHandler.initialize();
- logger.info("LC status: {}", pmHandler.getLifecycleStatus());
- int retryCount = 0; // Test shall fail after max 10 seconds
- while (pmHandler.getLifecycleStatus() != LifecycleStatus.RUNNING && retryCount < 20) {
- try {
- logger.info("LC running not reached - wait");
- Thread.sleep(500);
- retryCount++;
- } catch (InterruptedException e) {
- // nothing to do
- }
- }
- /*
- * Test if config status is 0 = CONFIG_OK for valid configuration. Take real int for comparison instead of
- * BaseHandler constants - in case of change test needs to be adapted
- */
- assertEquals(ConfigStatus.EXTERNAL_SENSOR_OK, pmHandler.getConfigStatus(), "Handler Configuration status");
- }
-
- @Test
- public void testInvalidConfigStatus() {
- ThingMock t = new ThingMock();
-
- HashMap<String, Object> properties = new HashMap<String, Object>();
- // String sensorid taken from thing-types.xml
- properties.put("sensorid", -1);
- t.setConfiguration(properties);
-
- PMHandlerExtension pmHandler = new PMHandlerExtension(t);
- pmHandler.initialize();
- logger.info("LC status: {}", pmHandler.getLifecycleStatus());
- int retryCount = 0; // Test shall fail after max 10 seconds
- while (pmHandler.getLifecycleStatus() != LifecycleStatus.RUNNING && retryCount < 20) {
- try {
- logger.info("LC running not reached - wait");
- Thread.sleep(500);
- retryCount++;
- } catch (InterruptedException e) {
- // nothing to do
- }
- }
- /*
- * Test if config status is 3 = CONFIG_SENSOR_NUMBER for invalid configuration with non-number sensorid. Take
- * real int for comparison instead of BaseHandler constants - in case of change test needs to be adapted
- */
- assertEquals(ConfigStatus.SENSOR_ID_NEGATIVE, pmHandler.getConfigStatus(), "Handler Configuration status");
- }
-
- @Test
- public void testValidUpdate() {
- ThingMock t = new ThingMock();
-
- HashMap<String, Object> properties = new HashMap<String, Object>();
- // String sensorid taken from thing-types.xml
- properties.put("sensorid", 12345);
- t.setConfiguration(properties);
-
- PMHandlerExtension pmHandler = new PMHandlerExtension(t);
- pmHandler.initialize();
- String pmJson = FileReader.readFileInString("src/test/resources/pm-result.json");
- if (pmJson != null) {
- UpdateStatus result = pmHandler.updateChannels(pmJson);
- assertEquals(UpdateStatus.OK, result, "Valid update");
- assertEquals(QuantityType.valueOf(2.9, Units.MICROGRAM_PER_CUBICMETRE), pmHandler.getPM25Cache(), "PM25");
- assertEquals(QuantityType.valueOf(5.2, Units.MICROGRAM_PER_CUBICMETRE), pmHandler.getPM100Cache(), "PM100");
- } else {
- assertTrue(false);
- }
- }
-
- @Test
- public void testInvalidUpdate() {
- ThingMock t = new ThingMock();
-
- HashMap<String, Object> properties = new HashMap<String, Object>();
- // String sensorid taken from thing-types.xml
- properties.put("sensorid", 12345);
- t.setConfiguration(properties);
-
- PMHandlerExtension pmHandler = new PMHandlerExtension(t);
- String pmJson = FileReader.readFileInString("src/test/resources/noise-result.json");
- if (pmJson != null) {
- UpdateStatus result = pmHandler.updateChannels(pmJson);
- assertEquals(UpdateStatus.VALUE_ERROR, result, "Valid update");
- assertEquals(QuantityType.valueOf(-1, Units.MICROGRAM_PER_CUBICMETRE), pmHandler.getPM25Cache(),
- "Values undefined");
- assertEquals(QuantityType.valueOf(-1, Units.MICROGRAM_PER_CUBICMETRE), pmHandler.getPM100Cache(),
- "Values undefined");
- } else {
- assertTrue(false);
- }
- }
-
- @Test
- public void testEmptyUpdate() {
- ThingMock t = new ThingMock();
-
- HashMap<String, Object> properties = new HashMap<String, Object>();
- // String sensorid taken from thing-types.xml
- properties.put("sensorid", 12345);
- t.setConfiguration(properties);
-
- PMHandlerExtension pmHandler = new PMHandlerExtension(t);
- UpdateStatus result = pmHandler.updateChannels("[]");
- assertEquals(UpdateStatus.VALUE_EMPTY, result, "Valid update");
- }
-
- @Test
- public void testNullUpdate() {
- ThingMock t = new ThingMock();
-
- HashMap<String, Object> properties = new HashMap<String, Object>();
- // String sensorid taken from thing-types.xml
- properties.put("ipAdress", "192.168.178.1");
- t.setConfiguration(properties);
-
- PMHandlerExtension pmHandler = new PMHandlerExtension(t);
- UpdateStatus result = pmHandler.updateChannels(null);
- assertEquals(UpdateStatus.CONNECTION_ERROR, result, "Valid update");
- }
-
- @Test
- public void testInternalPMSensor() {
- ThingMock t = new ThingMock();
-
- HashMap<String, Object> properties = new HashMap<String, Object>();
- // String sensorid taken from thing-types.xml
- properties.put("sensorid", 12345);
- t.setConfiguration(properties);
-
- PMHandlerExtension pmHandler = new PMHandlerExtension(t);
- pmHandler.initialize();
- String pmJson = FileReader.readFileInString("src/test/resources/internal-data.json");
- if (pmJson != null) {
- UpdateStatus result = pmHandler.updateChannels("[" + pmJson + "]");
- assertEquals(UpdateStatus.OK, result, "Valid update");
- assertEquals(QuantityType.valueOf(4.3, Units.MICROGRAM_PER_CUBICMETRE), pmHandler.getPM25Cache(), "PM25");
- assertEquals(QuantityType.valueOf(10.5, Units.MICROGRAM_PER_CUBICMETRE), pmHandler.getPM100Cache(),
- "PM100");
- } else {
- assertTrue(false);
- }
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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.mock;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.luftdateninfo.internal.handler.ConditionHandler;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.types.State;
-
-/**
- * The {@link NoiseHandlerExtension} Test Noise Handler Extension with additonal state queries
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class ConditionHandlerExtension extends ConditionHandler {
-
- public ConditionHandlerExtension(Thing thing) {
- super(thing);
- }
-
- public ConfigStatus getConfigStatus() {
- return configStatus;
- }
-
- public UpdateStatus getUpdateStatus() {
- return lastUpdateStatus;
- }
-
- public @Nullable State getTemperature() {
- return temperatureCache;
- }
-
- public @Nullable State getHumidity() {
- return humidityCache;
- }
-
- public @Nullable State getPressure() {
- return pressureCache;
- }
-
- public @Nullable State getPressureSea() {
- return pressureSeaCache;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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.mock;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.luftdateninfo.internal.handler.NoiseHandler;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.types.State;
-
-/**
- * The {@link NoiseHandlerExtension} Test Noise Handler Extension with additonal state queries
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class NoiseHandlerExtension extends NoiseHandler {
-
- public NoiseHandlerExtension(Thing thing) {
- super(thing);
- }
-
- public ConfigStatus getConfigStatus() {
- return configStatus;
- }
-
- public UpdateStatus getUpdateStatus() {
- return lastUpdateStatus;
- }
-
- public @Nullable State getNoiseEQCache() {
- return noiseEQCache;
- }
-
- public @Nullable State getNoiseMinCache() {
- return noiseMinCache;
- }
-
- public @Nullable State getNoiseMaxCache() {
- return noiseMaxCache;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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.mock;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.luftdateninfo.internal.handler.PMHandler;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.types.State;
-
-/**
- * The {@link PMHandlerExtension} Test Particualte Matter Handler Extension with additonal state queries
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class PMHandlerExtension extends PMHandler {
-
- public PMHandlerExtension(Thing thing) {
- super(thing);
- }
-
- public ConfigStatus getConfigStatus() {
- return configStatus;
- }
-
- public UpdateStatus getUpdateStatus() {
- return lastUpdateStatus;
- }
-
- public @Nullable State getPM25Cache() {
- return pm25Cache;
- }
-
- public @Nullable State getPM100Cache() {
- return pm100Cache;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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.mock;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.core.config.core.Configuration;
-import org.openhab.core.thing.Channel;
-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.ThingStatusInfo;
-import org.openhab.core.thing.ThingTypeUID;
-import org.openhab.core.thing.ThingUID;
-import org.openhab.core.thing.binding.ThingHandler;
-
-/**
- * The {@link ThingMock} Thing Mock
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class ThingMock implements Thing {
- private Configuration config = new Configuration();
-
- @Override
- public @Nullable String getLabel() {
- return null;
- }
-
- @Override
- public void setLabel(@Nullable String label) {
- }
-
- @Override
- public List<Channel> getChannels() {
- return new ArrayList<Channel>();
- }
-
- @Override
- public List<Channel> getChannelsOfGroup(String channelGroupId) {
- return new ArrayList<Channel>();
- }
-
- @Override
- public @Nullable Channel getChannel(String channelId) {
- return null;
- }
-
- @Override
- public @Nullable Channel getChannel(ChannelUID channelUID) {
- return null;
- }
-
- @Override
- public ThingStatus getStatus() {
- return ThingStatus.UNKNOWN;
- }
-
- @Override
- public ThingStatusInfo getStatusInfo() {
- return new ThingStatusInfo(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "");
- }
-
- @Override
- public void setStatusInfo(ThingStatusInfo status) {
- }
-
- @Override
- public void setHandler(@Nullable ThingHandler thingHandler) {
- }
-
- @Override
- public @Nullable ThingHandler getHandler() {
- return null;
- }
-
- @Override
- public @Nullable ThingUID getBridgeUID() {
- return null;
- }
-
- @Override
- public void setBridgeUID(@Nullable ThingUID bridgeUID) {
- }
-
- @Override
- public Configuration getConfiguration() {
- return config;
- }
-
- public void setConfiguration(Map<String, Object> m) {
- config = new Configuration(m);
- }
-
- @Override
- public ThingUID getUID() {
- return new ThingUID("luftdateninfo", "test");
- }
-
- @Override
- public ThingTypeUID getThingTypeUID() {
- return new ThingTypeUID("luftdateninfo:any");
- }
-
- @Override
- public Map<String, String> getProperties() {
- return new HashMap<String, String>();
- }
-
- @Override
- public @Nullable String setProperty(String name, @Nullable String value) {
- return null;
- }
-
- @Override
- public void setProperties(Map<String, String> properties) {
- }
-
- @Override
- public @Nullable String getLocation() {
- return null;
- }
-
- @Override
- public void setLocation(@Nullable String location) {
- }
-
- @Override
- public boolean isEnabled() {
- return false;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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.util;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import java.time.LocalDateTime;
-import java.time.format.DateTimeParseException;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.junit.jupiter.api.Test;
-import org.openhab.binding.luftdateninfo.internal.utils.DateTimeUtils;
-
-/**
- * The {@link DateTimeTest} Test DateTimeFormatter provided in utils package
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class DateTimeTest {
-
- @Test
- public void testJSonTime() {
- String jsonDateString = "2020-08-14 14:53:21";
- try {
- LocalDateTime dt = LocalDateTime.from(DateTimeUtils.DTF.parse(jsonDateString));
- assertEquals(14, dt.getDayOfMonth(), "Day");
- assertEquals(8, dt.getMonthValue(), "Month");
- assertEquals(2020, dt.getYear(), "Year");
-
- String s = dt.format(DateTimeUtils.DTF);
- assertEquals(jsonDateString, s, "String");
- } catch (DateTimeParseException e) {
- assertFalse(true);
- }
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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.util;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import java.io.BufferedReader;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * The {@link FileReader} Helper Util to read test resource files
- *
- * @author Bernd Weymann - Initial contribution
- */
-@NonNullByDefault
-public class FileReader {
-
- public static @Nullable String readFileInString(String filename) {
- try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filename), "CP1252"));) {
- StringBuffer buf = new StringBuffer();
- String sCurrentLine;
-
- while ((sCurrentLine = br.readLine()) != null) {
- buf.append(sCurrentLine);
- }
- return buf.toString();
- } catch (IOException e) {
- // fail if file cannot be read
- assertTrue(false);
- }
- return null;
- }
-}
+++ /dev/null
-[
- {
- "id": 731117559,
- "sensordatavalues": [
- {
- "id": 1573660194,
- "value_type": "temperature",
- "value": "22.70"
- },
- {
- "id": 1573660195,
- "value_type": "humidity",
- "value": "61.00"
- }
- ],
- "timestamp": "2020-06-09 06:40:34",
- "sampling_rate": null,
- "location": {
- "id": 11447,
- "country": "DE",
- "altitude": "151.5",
- "latitude": "50.562",
- "longitude": "8.504",
- "indoor": 0,
- "exact_location": 0
- },
- "sensor": {
- "id": 22562,
- "pin": "7",
- "sensor_type": {
- "id": 9,
- "manufacturer": "various",
- "name": "DHT22"
- }
- }
- },
- {
- "id": 731094694,
- "sensordatavalues": [
- {
- "id": 1573610869,
- "value_type": "temperature",
- "value": "22.50"
- },
- {
- "id": 1573610870,
- "value_type": "humidity",
- "value": "62.00"
- }
- ],
- "timestamp": "2020-06-09 06:38:08",
- "sampling_ra
- te": null,
- "location": {
- "id": 11447,
- "country": "DE",
- "altitude": "151.5",
- "latitude": "50.562",
- "longitude": "8.504",
- "indoor": 0,
- "exact_location": 0
- },
- "sensor": {
- "id": 22562,
- "pin": "7",
- "sensor_type": {
- "id": 9,
- "manufacturer": "various",
- "name": "DHT22"
- }
- }
- }
-]
\ No newline at end of file
+++ /dev/null
-[
- {
- "id": 731094694,
- "sensordatavalues": [
- {
- "id": 1573610869,
- "value_type": "temperature",
- "value": "22.50"
- },
- {
- "id": 1573610870,
- "value_type": "humidity",
- "value": "62.00"
- }
- ],
- "timestamp": "2020-06-09 06:38:08",
- "sampling_rate": null,
- "location": {
- "id": 11447,
- "country": "DE",
- "altitude": "151.5",
- "latitude": "50.562",
- "longitude": "8.504",
- "indoor": 0,
- "exact_location": 0
- },
- "sensor": {
- "id": 22562,
- "pin": "7",
- "sensor_type": {
- "id": 9,
- "manufacturer": "various",
- "name": "DHT22"
- }
- }
- },
- {
- "id": 731117559,
- "sensordatavalues": [
- {
- "id": 1573660194,
- "value_type": "temperature",
- "value": "22.70"
- },
- {
- "id": 1573660195,
- "value_type": "humidity",
- "value": "61.00"
- }
- ],
- "timestamp": "2020-06-09 06:40:34",
- "sampling_rate": null,
- "location": {
- "id": 11447,
- "country": "DE",
- "altitude": "151.5",
- "latitude": "50.562",
- "longitude": "8.504",
- "indoor": 0,
- "exact_location": 0
- },
- "sensor": {
- "id": 22562,
- "pin": "7",
- "sensor_type": {
- "id": 9,
- "manufacturer": "various",
- "name": "DHT22"
- }
- }
- }
-]
\ No newline at end of file
+++ /dev/null
-[
- {
- "id": 1038856661,
- "sensor": {
- "id": 28843,
- "sensor_type": {
- "id": 17,
- "manufacturer": "Bosch",
- "name": "BME280"
- },
- "pin": "11"
- },
- "timestamp": "2020-07-03 09:39:46",
- "sampling_rate": null,
- "location": {
- "id": 15975,
- "altitude": "151.2",
- "longitude": "8.49543571448",
- "exact_location": 1,
- "latitude": "50.55591005174",
- "indoor": 0,
- "country": "DE"
- },
- "sensordatavalues": [
- {
- "id": 2237770681,
- "value_type": "temperature",
- "value": "21.52"
- },
- {
- "id": 2237770683,
- "value_type": "pressure",
- "value": "100199.97"
- },
- {
- "id": 2237770684,
- "value_type": "humidity",
- "value": "58.51"
- },
- {
- "value_type": "pressure_at_sealevel",
- "value": 101968.66
- }
- ]
- },
- {
- "id": 1038834126,
- "sensor": {
- "id": 28843,
- "sensor_type": {
- "id": 17,
- "manufacturer": "Bosch",
- "name": "BME280"
- },
- "pin": "11"
- },
- "timestamp": "2020-07-03 09:37:21",
- "sampling_rate": null,
- "location": {
- "id": 15975,
- "altitude": "151.2",
- "longitude": "8.49543571448",
- "exact_location": 1,
- "latitude": "50.55591005174",
- "indoor": 0,
- "country": "DE"
- },
- "sensordatavalues": [
- {
- "id": 2237722004,
- "value_type": "temperature",
- "value": "21.45"
- },
- {
- "id": 2237722008,
- "value_type": "pressure",
- "value": "100205.09"
- },
- {
- "id": 2237722009,
- "value_type": "humidity",
- "value": "58.79"
- },
- {
- "value_type": "pressure_at_sealevel",
- "value": 101974.29
- }
- ]
- }
-]
\ No newline at end of file
+++ /dev/null
-{
- "software_version": "NRZ-2020-133",
- "age": "112",
- "sensordatavalues": [
- {
- "value_type": "SDS_P1",
- "value": "10.52"
- },
- {
- "value_type": "SDS_P2",
- "value": "4.32"
- },
- {
- "value_type": "BME280_temperature",
- "value": "17.59"
- },
- {
- "value_type": "BME280_pressure",
- "value": "98680.28"
- },
- {
- "value_type": "BME280_humidity",
- "value": "57.78"
- },
- {
- "value_type": "samples",
- "value": "5070500"
- },
- {
- "value_type": "min_micro",
- "value": "28"
- },
- {
- "value_type": "max_micro",
- "value": "20091"
- },
- {
- "value_type": "interval",
- "value": "145000"
- },
- {
- "value_type": "signal",
- "value": "-81"
- }
- ]
-}
\ No newline at end of file
+++ /dev/null
-[
- {
- "timestamp": "2020-06-11 09:39:51",
- "sensordatavalues": [
- {
- "value": "50.95",
- "id": 1629930130,
- "value_type": "noise_LAeq"
- },
- {
- "value": "47.20",
- "id": 1629930131,
- "value_type": "noise_LA_min"
- },
- {
- "value": "56.95",
- "id": 1629930132,
- "value_type": "noise_LA_max"
- }
- ],
- "sampling_rate": null,
- "location": {
- "exact_location": 1,
- "latitude": "50.88827895000",
- "country": "DE",
- "altitude": "294.9",
- "indoor": 0,
- "longitude": "7.87451286686",
- "id": 25429
- },
- "id": 757217220,
- "sensor": {
- "sensor_type": {
- "id": 29,
- "manufacturer": "Luftdaten.info",
- "name": "Laerm"
- },
- "pin": "15",
- "id": 39745
- }
- },
- {
- "timestamp": "2020-06-11 09:37:25",
- "sensordatavalues": [
- {
- "value": "52.02",
- "id": 1629881984,
- "value_type": "noise_LAeq"
- },
- {
- "value": "45.98",
- "id": 1629881986,
- "value_type": "noise_LA_min"
- },
- {
- "value": "69.28",
- "id": 1629881987,
- "value_type": "noise_LA_max"
- }
- ],
- "sampling_rate": null,
- "location": {
- "exact_location": 1,
- "latitude": "50.88827895000",
- "country": "DE",
- "altitude": "294.9",
- "indoor": 0,
- "longitude": "7.87451286686",
- "id": 25429
- },
- "id": 757194885,
- "sensor": {
- "sensor_type": {
- "id": 29,
- "manufacturer": "Luftdaten.info",
- "name": "Laerm"
- },
- "pin": "15",
- "id": 39745
- }
- }
-]
\ No newline at end of file
+++ /dev/null
-[
- {
- "timestamp": "2020-06-11 09:40:41",
- "sensordatavalues": [
- {
- "value": "5.15",
- "id": 1629948185,
- "value_type": "P1"
- },
- {
- "value": "2.87",
- "id": 1629948191,
- "value_type": "P2"
- }
- ],
- "sampling_rate": null,
- "location": {
- "exact_location": 1,
- "latitude": "50.55591005174",
- "country": "DE",
- "altitude": "151.2",
- "indoor": 0,
- "longitude": "8.49543571448",
- "id": 15975
- },
- "id": 757225623,
- "sensor": {
- "sensor_type": {
- "id": 14,
- "manufacturer": "Nova Fitness",
- "name": "SDS011"
- },
- "pin": "1",
- "id": 28842
- }
- },
- {
- "timestamp": "2020-06-11 09:38:16",
- "sensordatavalues": [
- {
- "value": "2.20",
- "id": 1629900061,
- "value_type": "P1"
- },
- {
- "value": "2.00",
- "id": 1629900063,
- "value_type": "P2"
- }
- ],
- "sampling_rate": null,
- "location": {
- "exact_location": 1,
- "latitude": "50.55591005174",
- "country": "DE",
- "altitude": "151.2",
- "indoor": 0,
- "longitude": "8.49543571448",
- "id": 15975
- },
- "id": 757203291,
- "sensor": {
- "sensor_type": {
- "id": 14,
- "manufacturer": "Nova Fitness",
- "name": "SDS011"
- },
- "pin": "1",
- "id": 28842
- }
- }
-]
\ No newline at end of file
--- /dev/null
+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
--- /dev/null
+# SensorCommunity Binding
+
+Binding for the [Sensor.Community](https://sensor.community/).
+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.
+
+## Supported Things
+
+Three Things are supported
+
+| Name | Thing Type ID | Description |
+|--------------------|---------------|--------------------------------------------------------------------------------------------------------|
+| Particulate Sensor | particulate | measure particulate matter PM2.5 and PM10 |
+| Conditions Sensor | condition | measures environment conditions like temperature, humidity and some also provides atmospheric pressure |
+| Noise Sensor | noise | measures noise exposures in the environment |
+
+## Discovery
+
+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.
+
+
+
+### External Sensor
+
+Perform the following steps to get the appropriate Sensor ID
+
+- Go to to [Sensor.Community map](https://deutschland.maps.sensor.community/)
+- Choose your desired value in bottom list - now only the Sensors are displayed which are supporting this
+- Click on your / any Sensor and the ID is displayed in the top right corner. Note: Sensor ID is just the number without beginning hash #
+- Enter this Sensor ID into the thing configuration
+
+
+
+## Channels
+
+### Particulate Sensor
+
+| Channel ID | Item Type | Description |
+|----------------------|----------------------|------------------------------------------|
+| pm25 | Number:Density | [Ultrafine particulates](https://en.wikipedia.org/wiki/Particulates#Size,_shape_and_solubility_matter) microgram per cubic meter |
+| pm100 | Number:Density | [Coarse particulate matter](https://en.wikipedia.org/wiki/Particulates#Size,_shape_and_solubility_matter) microgram per cubic meter |
+
+### Conditions Sensor
+
+| Channel ID | Item Type | Description |
+|----------------------|----------------------|------------------------------------------|
+| temperature | Number:Temperature | current temperature |
+| humidity | Number:Dimensionless | current humidity percent |
+| pressure | Number:Pressure | Atmospheric Pressure (not supported by all sensors) |
+| pressure-sea | Number:Pressure | Atmospheric Pressure on sea level (not supported by all sensors) |
+
+### Noise Sensor
+
+| Channel ID | Item Type | Description |
+|----------------------|----------------------|------------------------------------------------------|
+| noise-eq | Number:Dimensionless | Average noise in db |
+| noise-min | Number:Dimensionless | Minimum noise covered in the last 2.5 minutes in db |
+| noise-main | Number:Dimensionless | Maximum noise covered in the last 2.5 minutes in db |
+
+## Full Example
+
+### Things
+
+sensorcommunity.things
+
+```java
+Thing sensorcommunity:particulate:pm_sensor "PM Sensor" [ ipAddress=192.168.178.50 ]
+Thing sensorcommunity:conditions:cond_sensor "Condition Sensor" [ sensorid=28843 ]
+Thing sensorcommunity:noise:noise_sensor "Noise Sensor" [ sensorid=39745 ]
+```
+
+### Items
+
+sensorcommunity.items
+
+```java
+Number:Density PM_25 "PM2.5" { channel="sensorcommunity:particulate:pm_sensor:pm25" }
+Number:Density PM_100 "PM10" { channel="sensorcommunity:particulate:pm_sensor:pm100" }
+
+Number:Temperature LDI_Temperature "Temperature" { channel="sensorcommunity:conditions:cond_sensor:temperature" }
+Number:Dimensionless LDI_Humidity "Humidity" { channel="sensorcommunity:conditions:cond_sensor:humidity" }
+Number:Pressure LDI_Pressure "Atmospheric Pressure" { channel="sensorcommunity:conditions:cond_sensor:pressure" }
+Number:Pressure LDI_PressureSea "Pressure sea level" { channel="sensorcommunity:conditions:cond_sensor:pressure-sea" }
+
+Number:Dimensionless LDI_NoiseEQ "Noise EQ" { channel="sensorcommunity:noise:noise_sensor:noise-eq" }
+Number:Dimensionless LDI_NoiseMin "Noise min" { channel="sensorcommunity:noise:noise_sensor:noise-min" }
+Number:Dimensionless LDI_NoiseMax "Noise max" { channel="sensorcommunity:noise:noise_sensor:noise-max" }
+```
+
+### Sitemap
+
+SensorCommunity.sitemap
+
+```perl
+sitemap SensorCommunity label="SensorCommunity" {
+ Text item=PM_25 label="Particulate Matter 2.5 [%.1f %unit%]"
+ Text item=PM_100 label="Particulate Matter 10 [%.1f %unit%]"
+
+ Text item=LDI_Temperature label="Temperature [%d %unit%]"
+ Text item=LDI_Humidity label="Humidity [%d %unit%]"
+ Text item=LDI_Pressure label="Atmospheric Pressure [%d %unit%]"
+ Text item=LDI_PressureSea label="Atmospheric Pressure sea [%d %unit%]"
+
+ Text item=LDI_NoiseEQ label="Noise avg [%.1f %unit%]"
+ Text item=LDI_NoiseMin label="Noise min [%.1f %unit%]"
+ Text item=LDI_NoiseMax label="Noise max [%.1f %unit%]"
+}
+```
--- /dev/null
+<?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 https://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>4.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>org.openhab.binding.sensorcommunity</artifactId>
+
+ <name>openHAB Add-ons :: Bundles :: Sensor.Community Binding</name>
+
+</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<features name="org.openhab.binding.sensorcommunity-${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-sensorcommunity" description="Sensor.Community Binding" version="${project.version}">
+ <feature>openhab-runtime-base</feature>
+ <bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.sensorcommunity/${project.version}</bundle>
+ </feature>
+</features>
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.thing.ThingTypeUID;
+
+/**
+ * The {@link SensorCommunityBindingConstants} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@NonNullByDefault
+public class SensorCommunityBindingConstants {
+
+ private static final String BINDING_ID = "sensorcommunity";
+
+ // List of all Thing Type UIDs
+ public static final ThingTypeUID THING_TYPE_PARTICULATE = new ThingTypeUID(BINDING_ID, "particulate");
+ public static final ThingTypeUID THING_TYPE_CONDITIONS = new ThingTypeUID(BINDING_ID, "conditions");
+ public static final ThingTypeUID THING_TYPE_NOISE = new ThingTypeUID(BINDING_ID, "noise");
+
+ // List of all Channel ids
+ public static final String PM25_CHANNEL = "pm25";
+ public static final String PM100_CHANNEL = "pm100";
+ public static final String TEMPERATURE_CHANNEL = "temperature";
+ public static final String HUMIDITY_CHANNEL = "humidity";
+ public static final String PRESSURE_CHANNEL = "pressure";
+ public static final String PRESSURE_SEA_CHANNEL = "pressure-sea";
+ public static final String NOISE_EQ_CHANNEL = "noise-eq";
+ public static final String NOISE_MIN_CHANNEL = "noise-min";
+ public static final String NOISE_MAX_CHANNEL = "noise-max";
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.sensorcommunity.internal.utils.Constants;
+
+/**
+ * The {@link SensorCommunityConfiguration} class contains fields mapping thing configuration parameters.
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@NonNullByDefault
+public class SensorCommunityConfiguration {
+
+ public int sensorid = Constants.UNDEF;
+
+ public String ipAddress = Constants.EMPTY;
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.sensorcommunity.internal.handler.ConditionHandler;
+import org.openhab.binding.sensorcommunity.internal.handler.HTTPHandler;
+import org.openhab.binding.sensorcommunity.internal.handler.NoiseHandler;
+import org.openhab.binding.sensorcommunity.internal.handler.PMHandler;
+import org.openhab.core.io.net.http.HttpClientFactory;
+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.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link SensorCommunityHandlerFactory} is responsible for creating things and thing
+ * handlers.
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@NonNullByDefault
+@Component(configurationPid = "binding.sensorcommunity", service = ThingHandlerFactory.class)
+public class SensorCommunityHandlerFactory extends BaseThingHandlerFactory {
+ protected final Logger logger = LoggerFactory.getLogger(SensorCommunityHandlerFactory.class);
+
+ @Activate
+ public SensorCommunityHandlerFactory(final @Reference HttpClientFactory httpClientFactory) {
+ HTTPHandler.init(httpClientFactory.getCommonHttpClient());
+ }
+
+ @Override
+ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
+ return (thingTypeUID.equals(SensorCommunityBindingConstants.THING_TYPE_PARTICULATE)
+ || thingTypeUID.equals(SensorCommunityBindingConstants.THING_TYPE_CONDITIONS)
+ || thingTypeUID.equals(SensorCommunityBindingConstants.THING_TYPE_NOISE));
+ }
+
+ @Override
+ protected @Nullable ThingHandler createHandler(Thing thing) {
+ if (thing.getThingTypeUID().equals(SensorCommunityBindingConstants.THING_TYPE_PARTICULATE)) {
+ return new PMHandler(thing);
+ } else if (thing.getThingTypeUID().equals(SensorCommunityBindingConstants.THING_TYPE_CONDITIONS)) {
+ return new ConditionHandler(thing);
+ } else if (thing.getThingTypeUID().equals(SensorCommunityBindingConstants.THING_TYPE_NOISE)) {
+ return new NoiseHandler(thing);
+ }
+ logger.info("Handler for {} not found", thing.getThingTypeUID());
+ return null;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal.dto;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link SensorCommunity} class definition for Logging identification
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+public class Location {
+ private int id;
+ private String country;
+ private String altitude;
+ private String latitude;
+ private String longitude;
+ private int indoor;
+ @SerializedName("exact_location")
+ private int exactLocation;
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getCountry() {
+ return country;
+ }
+
+ public void setCountry(String country) {
+ this.country = country;
+ }
+
+ public String getAltitude() {
+ return altitude;
+ }
+
+ public void setAltitude(String altitude) {
+ this.altitude = altitude;
+ }
+
+ public String getLatitude() {
+ return latitude;
+ }
+
+ public void setLatitude(String latitude) {
+ this.latitude = latitude;
+ }
+
+ public String getLongitude() {
+ return longitude;
+ }
+
+ public void setLongitude(String longitude) {
+ this.longitude = longitude;
+ }
+
+ public Integer getIndoor() {
+ return indoor;
+ }
+
+ public void setIndoor(int indoor) {
+ this.indoor = indoor;
+ }
+
+ public int getExactLocation() {
+ return exactLocation;
+ }
+
+ public void setExactLocation(int exactLocation) {
+ this.exactLocation = exactLocation;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal.dto;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link Sensor} Data Transfer Object
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+public class Sensor {
+ private int id;
+ private String pin;
+ @SerializedName("sensor_type")
+ private SensorType sensorType;
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getPin() {
+ return pin;
+ }
+
+ public void setPin(String pin) {
+ this.pin = pin;
+ }
+
+ public SensorType getSensoTypee() {
+ return sensorType;
+ }
+
+ public void setSensorType(SensorType sensorType) {
+ this.sensorType = sensorType;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal.dto;
+
+import java.util.List;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link SensorData} Data Transfer Object
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+public class SensorData {
+ private long id;
+ private String timestamp;
+ @SerializedName("sampling_rate")
+ private int samplingRate;
+ @SerializedName("sensordatavalues")
+ private List<SensorDataValue> sensorDataValues;
+ private Location location;
+ private Sensor sensor;
+
+ @Override
+ public String toString() {
+ return id + timestamp;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getTimeStamp() {
+ return timestamp;
+ }
+
+ public void setTimeStamp(String timeStamp) {
+ this.timestamp = timeStamp;
+ }
+
+ public int getSamplingRate() {
+ return samplingRate;
+ }
+
+ public void setSamplingRate(int samplingRate) {
+ this.samplingRate = samplingRate;
+ }
+
+ public List<SensorDataValue> getSensorDataValues() {
+ return sensorDataValues;
+ }
+
+ public void setSensorDataValues(List<SensorDataValue> sensorDataValues) {
+ this.sensorDataValues = sensorDataValues;
+ }
+
+ public Location getLocation() {
+ return location;
+ }
+
+ public void setLocation(Location location) {
+ this.location = location;
+ }
+
+ public Sensor getSensor() {
+ return sensor;
+ }
+
+ public void setSensor(Sensor sensor) {
+ this.sensor = sensor;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal.dto;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link SensorDataValue} Data Transfer Object
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+public class SensorDataValue {
+ private long id;
+ @SerializedName("value_type")
+ private String valueType;
+ private String value;
+
+ @Override
+ public String toString() {
+ return valueType + ":" + value;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getValueType() {
+ return valueType;
+ }
+
+ public void setValueType(String valueType) {
+ this.valueType = valueType;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal.dto;
+
+/**
+ * The {@link SensorType} Data Transfer Object
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+public class SensorType {
+ private int id;
+ private String manufacturer;
+ private String name;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getManufacturer() {
+ return manufacturer;
+ }
+
+ public void setManufacturer(String manufacturer) {
+ this.manufacturer = manufacturer;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.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.sensorcommunity.internal.SensorCommunityConfiguration;
+import org.openhab.binding.sensorcommunity.internal.utils.Constants;
+import org.openhab.binding.sensorcommunity.internal.utils.DateTimeUtils;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link PMHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@NonNullByDefault
+public abstract class BaseSensorHandler extends BaseThingHandler {
+ private static final SensorCommunityConfiguration DEFAULT_CONFIG = new SensorCommunityConfiguration();
+ private static final String EMPTY = "";
+
+ protected static final int REFRESH_INTERVAL_MIN = 5;
+ protected final Logger logger = LoggerFactory.getLogger(BaseSensorHandler.class);
+ protected SensorCommunityConfiguration config = DEFAULT_CONFIG;
+ protected ConfigStatus configStatus = ConfigStatus.UNKNOWN;
+ 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 {
+ INTERNAL_SENSOR_OK,
+ EXTERNAL_SENSOR_OK,
+ IS_NULL,
+ SENSOR_IS_NULL,
+ SENSOR_ID_NEGATIVE,
+ UNKNOWN
+ };
+
+ public enum UpdateStatus {
+ OK,
+ CONNECTION_ERROR,
+ CONNECTION_EXCEPTION,
+ VALUE_ERROR,
+ VALUE_EMPTY,
+ UNKNOWN
+ }
+
+ protected LifecycleStatus lifecycleStatus = LifecycleStatus.UNKNOWN;
+
+ public enum LifecycleStatus {
+ UNKNOWN,
+ RUNNING,
+ INITIALIZING,
+ DISPOSED
+ }
+
+ public BaseSensorHandler(Thing thing) {
+ super(thing);
+ }
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ if (command instanceof RefreshType) {
+ updateFromCache();
+ }
+ }
+
+ @Override
+ public void initialize() {
+ firstUpdate = true;
+ lifecycleStatus = LifecycleStatus.INITIALIZING;
+ scheduler.execute(this::startUp);
+ }
+
+ private void startUp() {
+ config = getConfigAs(SensorCommunityConfiguration.class);
+ configStatus = checkConfig(config);
+ if (configStatus == ConfigStatus.INTERNAL_SENSOR_OK || configStatus == ConfigStatus.EXTERNAL_SENSOR_OK) {
+ // start getting values
+ dataUpdate();
+ } else {
+ // config error, no further actions triggered - Thing Status visible in UI
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+ "Configuration not valid. Sensor ID as a number is mandatory!");
+ }
+ lifecycleStatus = LifecycleStatus.RUNNING;
+ }
+
+ private void startSchedule() {
+ ScheduledFuture<?> localRefreshJob = refreshJob;
+ if (localRefreshJob != null) {
+ if (localRefreshJob.isCancelled()) {
+ refreshJob = scheduler.scheduleWithFixedDelay(this::dataUpdate, 5, REFRESH_INTERVAL_MIN,
+ TimeUnit.MINUTES);
+ } // else - scheduler is already running!
+ } else {
+ refreshJob = scheduler.scheduleWithFixedDelay(this::dataUpdate, 5, REFRESH_INTERVAL_MIN, TimeUnit.MINUTES);
+ }
+ }
+
+ @Override
+ public void dispose() {
+ ScheduledFuture<?> localRefreshJob = refreshJob;
+ if (localRefreshJob != null) {
+ localRefreshJob.cancel(true);
+ }
+ lifecycleStatus = LifecycleStatus.DISPOSED;
+ }
+
+ /**
+ * Checks if config is valid - a) not null and b) sensorid is a number
+ *
+ * @param c
+ * @return
+ */
+ private ConfigStatus checkConfig(@Nullable SensorCommunityConfiguration c) {
+ if (c != null) {
+ if (c.ipAddress != null && !Constants.EMPTY.equals(c.ipAddress)) {
+ sensorUrl = Optional.of("http://" + c.ipAddress + "/data.json");
+ return ConfigStatus.INTERNAL_SENSOR_OK;
+ } else {
+ 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;
+ }
+ }
+
+ public LifecycleStatus getLifecycleStatus() {
+ return lifecycleStatus;
+ }
+
+ protected void dataUpdate() {
+ if (sensorUrl.isPresent()) {
+ HTTPHandler.getHandler().request(sensorUrl.get(), this);
+ }
+ }
+
+ public void onResponse(String 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);
+ }
+
+ public void onError(String errorReason) {
+ statusUpdate(UpdateStatus.CONNECTION_EXCEPTION,
+ errorReason + " / " + LocalDateTime.now().format(DateTimeUtils.DTF));
+ }
+
+ protected void statusUpdate(UpdateStatus updateStatus, String details) {
+ if (updateStatus == UpdateStatus.OK) {
+ updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, null);
+ startSchedule();
+ } else {
+ switch (updateStatus) {
+ case CONNECTION_ERROR:
+ // start job even first update delivers no data - recovery is possible
+ startSchedule();
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+ "Update failed due to Connection error. Trying to recover in next refresh");
+ break;
+ case CONNECTION_EXCEPTION:
+ // start job even first update delivers a Connection Exception - recovery is possible
+ startSchedule();
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, details);
+ break;
+ case VALUE_EMPTY:
+ // start job even if first update delivers no values - recovery possible
+ startSchedule();
+ updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE,
+ "No values delivered by Sensor. Trying to recover in next refresh");
+ break;
+ case VALUE_ERROR:
+ // final status - values from sensor are wrong and manual check is needed
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+ "Sensor values doesn't match - please check if Sensor ID is delivering the correct Thing channel values");
+ break;
+ default:
+ // final status - Configuration is wrong
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+ "Error during update - please check your config data");
+ break;
+ }
+ }
+ }
+
+ @Override
+ protected void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) {
+ myThingStatus = status;
+ super.updateStatus(status, statusDetail, description);
+ }
+
+ protected abstract UpdateStatus updateChannels(@Nullable String json);
+
+ protected abstract void updateFromCache();
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal.handler;
+
+import static org.openhab.binding.sensorcommunity.internal.SensorCommunityBindingConstants.*;
+import static org.openhab.binding.sensorcommunity.internal.utils.Constants.*;
+import static org.openhab.core.library.unit.MetricPrefix.HECTO;
+
+import java.util.List;
+
+import javax.measure.quantity.Dimensionless;
+import javax.measure.quantity.Pressure;
+import javax.measure.quantity.Temperature;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.sensorcommunity.internal.dto.SensorDataValue;
+import org.openhab.binding.sensorcommunity.internal.utils.NumberUtils;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.unit.SIUnits;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.Thing;
+
+/**
+ * The {@link ConditionHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@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, HECTO(SIUnits.PASCAL));
+ protected QuantityType<Pressure> pressureSeaCache = QuantityType.valueOf(-1, HECTO(SIUnits.PASCAL));
+
+ public ConditionHandler(Thing thing) {
+ super(thing);
+ }
+
+ @Override
+ public UpdateStatus updateChannels(@Nullable String json) {
+ if (json != null) {
+ List<SensorDataValue> valueList = HTTPHandler.getHandler().getLatestValues(json);
+ if (valueList != null) {
+ if (HTTPHandler.getHandler().isCondition(valueList)) {
+ valueList.forEach(v -> {
+ if (v.getValueType().endsWith(TEMPERATURE)) {
+ temperatureCache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1),
+ SIUnits.CELSIUS);
+ updateState(TEMPERATURE_CHANNEL, temperatureCache);
+ } else if (v.getValueType().endsWith(HUMIDITY)) {
+ humidityCache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1), Units.PERCENT);
+ updateState(HUMIDITY_CHANNEL, humidityCache);
+ } 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().endsWith(PRESSURE_SEALEVEL)) {
+ pressureSeaCache = QuantityType.valueOf(
+ NumberUtils.round(NumberUtils.convert(v.getValue()) / 100, 1),
+ HECTO(SIUnits.PASCAL));
+ updateState(PRESSURE_SEA_CHANNEL, pressureSeaCache);
+ }
+ });
+ return UpdateStatus.OK;
+ } else {
+ return UpdateStatus.VALUE_ERROR;
+ }
+ } else {
+ return UpdateStatus.VALUE_EMPTY;
+ }
+ } else {
+ return UpdateStatus.CONNECTION_ERROR;
+ }
+ }
+
+ @Override
+ protected void updateFromCache() {
+ updateState(TEMPERATURE_CHANNEL, temperatureCache);
+ updateState(HUMIDITY_CHANNEL, humidityCache);
+ updateState(PRESSURE_CHANNEL, pressureCache);
+ updateState(PRESSURE_SEA_CHANNEL, pressureSeaCache);
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal.handler;
+
+import static org.openhab.binding.sensorcommunity.internal.utils.Constants.*;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.util.BufferingResponseListener;
+import org.openhab.binding.sensorcommunity.internal.dto.SensorData;
+import org.openhab.binding.sensorcommunity.internal.dto.SensorDataValue;
+import org.openhab.binding.sensorcommunity.internal.utils.DateTimeUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+
+/**
+ * The {@link HTTPHandler} is responsible for HTTP requests and JSON handling
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@NonNullByDefault
+public class HTTPHandler {
+ private final Logger logger = LoggerFactory.getLogger(HTTPHandler.class);
+
+ private static final Gson GSON = new Gson();
+ private static final HTTPHandler HTTP_HANDLER = new HTTPHandler();
+
+ private static @Nullable HttpClient commonHttpClient;
+
+ public static void init(HttpClient httpClient) {
+ commonHttpClient = httpClient;
+ }
+
+ public static HTTPHandler getHandler() {
+ return HTTP_HANDLER;
+ }
+
+ public synchronized void request(String url, BaseSensorHandler callback) {
+ HttpClient localClient = commonHttpClient;
+ if (localClient == null) {
+ logger.warn("HTTP Client not initialized");
+ } else {
+ Request req = localClient.newRequest(url);
+ req.timeout(15, TimeUnit.SECONDS).send(new BufferingResponseListener() {
+ @NonNullByDefault({})
+ @Override
+ public void onComplete(org.eclipse.jetty.client.api.Result result) {
+ if (result.getResponse().getStatus() != 200) {
+ String failure;
+ if (result.getResponse().getReason() != null) {
+ failure = result.getResponse().getReason();
+ } else {
+ failure = result.getFailure().getMessage();
+ }
+ callback.onError(Objects.requireNonNullElse(failure, "Unknown error"));
+ } else {
+ callback.onResponse(getContentAsString());
+ }
+ }
+ });
+ }
+ }
+
+ public @Nullable List<SensorDataValue> getLatestValues(String response) {
+ SensorData[] valueArray = GSON.fromJson(response, SensorData[].class);
+ if (valueArray.length == 0) {
+ return null;
+ } else if (valueArray.length == 1) {
+ SensorData v = valueArray[0];
+ return v.getSensorDataValues();
+ } else if (valueArray.length > 1) {
+ // declare first item as latest
+ SensorData latestData = valueArray[0];
+ String latestTimeStr = latestData.getTimeStamp();
+ LocalDateTime latestTime = DateTimeUtils.toDate(latestTimeStr);
+ if (latestTime == null) {
+ logDateConversionError(response, latestData);
+ }
+ for (int i = 1; i < valueArray.length; i++) {
+ SensorData iterData = valueArray[i];
+ String iterTimeStr = iterData.getTimeStamp();
+ LocalDateTime iterTime = DateTimeUtils.toDate(iterTimeStr);
+ if (iterTime == null) {
+ logDateConversionError(response, latestData);
+ }
+ if (iterTime != null && latestTime != null) {
+ if (latestTime.isBefore(iterTime)) {
+ // found item is newer - take it as latest
+ latestTime = iterTime;
+ latestData = iterData;
+ } // else - found item is older - nothing to do
+
+ } else {
+ logger.warn("One or two dates cannot be decoded 1) {} 2) {}", iterTimeStr, latestTimeStr);
+ }
+ }
+ return latestData.getSensorDataValues();
+ } else {
+ return null;
+ }
+ }
+
+ public void logDateConversionError(final String response, final Object dto) {
+ logger.warn("Unable to get timestamp");
+ logger.warn("Response: {}", response);
+ String json = GSON.toJson(dto);
+ logger.warn("GSon: {}", json);
+ }
+
+ public boolean isParticulate(@Nullable List<SensorDataValue> valueList) {
+ if (valueList == null) {
+ return false;
+ }
+ 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.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.endsWith(NOISE_EQ) || t.endsWith(NOISE_MAX) || t.endsWith(NOISE_MIN)).findAny()
+ .isPresent();
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal.handler;
+
+import static org.openhab.binding.sensorcommunity.internal.SensorCommunityBindingConstants.*;
+import static org.openhab.binding.sensorcommunity.internal.utils.Constants.*;
+
+import java.util.List;
+
+import javax.measure.quantity.Dimensionless;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.sensorcommunity.internal.dto.SensorDataValue;
+import org.openhab.binding.sensorcommunity.internal.utils.NumberUtils;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.Thing;
+
+/**
+ * The {@link NoiseHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@NonNullByDefault
+public class NoiseHandler extends BaseSensorHandler {
+ protected QuantityType<Dimensionless> noiseEQCache = QuantityType.valueOf(-1, Units.DECIBEL);
+ protected QuantityType<Dimensionless> noiseMinCache = QuantityType.valueOf(-1, Units.DECIBEL);
+ protected QuantityType<Dimensionless> noiseMaxCache = QuantityType.valueOf(-1, Units.DECIBEL);
+
+ public NoiseHandler(Thing thing) {
+ super(thing);
+ }
+
+ @Override
+ public UpdateStatus updateChannels(@Nullable String json) {
+ if (json != null) {
+ List<SensorDataValue> valueList = HTTPHandler.getHandler().getLatestValues(json);
+ if (valueList != null) {
+ if (HTTPHandler.getHandler().isNoise(valueList)) {
+ valueList.forEach(v -> {
+ 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().endsWith(NOISE_MIN)) {
+ noiseMinCache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1), Units.DECIBEL);
+ updateState(NOISE_MIN_CHANNEL, noiseMinCache);
+ } else if (v.getValueType().endsWith(NOISE_MAX)) {
+ noiseMaxCache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1), Units.DECIBEL);
+ updateState(NOISE_MAX_CHANNEL, noiseMaxCache);
+ }
+ });
+ return UpdateStatus.OK;
+ } else {
+ return UpdateStatus.VALUE_ERROR;
+ }
+ } else {
+ return UpdateStatus.VALUE_EMPTY;
+ }
+ } else {
+ return UpdateStatus.CONNECTION_ERROR;
+ }
+ }
+
+ @Override
+ protected void updateFromCache() {
+ updateState(NOISE_EQ_CHANNEL, noiseEQCache);
+ updateState(NOISE_MIN_CHANNEL, noiseMinCache);
+ updateState(NOISE_MAX_CHANNEL, noiseMaxCache);
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal.handler;
+
+import static org.openhab.binding.sensorcommunity.internal.SensorCommunityBindingConstants.*;
+import static org.openhab.binding.sensorcommunity.internal.utils.Constants.*;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.sensorcommunity.internal.dto.SensorDataValue;
+import org.openhab.binding.sensorcommunity.internal.utils.NumberUtils;
+import org.openhab.core.library.dimension.Density;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.Thing;
+
+/**
+ * The {@link PMHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@NonNullByDefault
+public class PMHandler extends BaseSensorHandler {
+
+ protected QuantityType<Density> pm25Cache = QuantityType.valueOf(-1, Units.MICROGRAM_PER_CUBICMETRE);
+ protected QuantityType<Density> pm100Cache = QuantityType.valueOf(-1, Units.MICROGRAM_PER_CUBICMETRE);
+
+ public PMHandler(Thing thing) {
+ super(thing);
+ }
+
+ @Override
+ public UpdateStatus updateChannels(@Nullable String json) {
+ if (json != null) {
+ List<SensorDataValue> valueList = HTTPHandler.getHandler().getLatestValues(json);
+ if (valueList != null) {
+ if (HTTPHandler.getHandler().isParticulate(valueList)) {
+ valueList.forEach(v -> {
+ 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().endsWith(P2)) {
+ pm25Cache = QuantityType.valueOf(NumberUtils.round(v.getValue(), 1),
+ Units.MICROGRAM_PER_CUBICMETRE);
+ updateState(PM25_CHANNEL, pm25Cache);
+ }
+ });
+ return UpdateStatus.OK;
+ } else {
+ return UpdateStatus.VALUE_ERROR;
+ }
+ } else {
+ return UpdateStatus.VALUE_EMPTY;
+ }
+ } else {
+ return UpdateStatus.CONNECTION_ERROR;
+ }
+ }
+
+ @Override
+ protected void updateFromCache() {
+ updateState(PM25_CHANNEL, pm25Cache);
+ updateState(PM100_CHANNEL, pm100Cache);
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.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;
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal.utils;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.Locale;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link DateTimeUtils} class provides helpers for converting Dates and Times.
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@NonNullByDefault
+public class DateTimeUtils {
+ public static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH);
+ private static final Logger LOGGER = LoggerFactory.getLogger(DateTimeUtils.class);
+
+ public static synchronized @Nullable LocalDateTime toDate(String dateTime) {
+ try {
+ return LocalDateTime.from(DTF.parse(dateTime));
+
+ } catch (DateTimeParseException e) {
+ LOGGER.debug("Unable to parse date {}", dateTime);
+ return null;
+ }
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal.utils;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link NumberUtils} class provides helpers for converting Numbers.
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@NonNullByDefault
+public class NumberUtils {
+ public static final double UNDEF = Double.NaN;
+
+ public static double round(Object o, int places) {
+ double value = convert(o);
+
+ // for negative places return plain number
+ if (places < 0) {
+ return value;
+ }
+
+ long factor = (long) Math.pow(10, places);
+ value = value * factor;
+ long tmp = Math.round(value);
+ return (double) tmp / factor;
+ }
+
+ public static double convert(Object o) {
+ // ensure value not null
+ double value = UNDEF;
+ if (o instanceof Number) {
+ value = ((Number) o).doubleValue();
+ } else if (o instanceof String) {
+ value = Double.parseDouble(o.toString());
+ }
+ return value;
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<addon:addon id="sensorcommunity" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
+ xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
+
+ <type>binding</type>
+ <name>Sensor.Community Binding</name>
+ <description>Binding to integrate DIY Sensors from Sensor.Community</description>
+ <connection>hybrid</connection>
+
+</addon:addon>
--- /dev/null
+# add-on
+
+addon.sensorcommunity.name = Sensor.Community Binding
+addon.sensorcommunity.description = Binding to integrate DIY Sensors from Sensor.Community
+
+# thing types
+
+thing-type.sensorcommunity.conditions.label = Condition Sensor
+thing-type.sensorcommunity.conditions.description = Sensor to measure Temperature and Humidity conditions
+thing-type.sensorcommunity.noise.label = Noise Sensor
+thing-type.sensorcommunity.noise.description = Sensor to measure noise on location
+thing-type.sensorcommunity.particulate.label = Particulate Sensor
+thing-type.sensorcommunity.particulate.description = Sensor to measure Particulate Matter (PM)
+
+# thing types config
+
+thing-type.config.sensorcommunity.conditions.ipAddress.label = Internal IP Address
+thing-type.config.sensorcommunity.conditions.ipAddress.description = Local IP address of your personal owned sensor
+thing-type.config.sensorcommunity.conditions.sensorid.label = External Sensor ID
+thing-type.config.sensorcommunity.conditions.sensorid.description = Sensor ID from https://deutschland.maps.sensor.community/
+thing-type.config.sensorcommunity.noise.ipAddress.label = Internal IP Address
+thing-type.config.sensorcommunity.noise.ipAddress.description = Local IP address of your personal owned sensor
+thing-type.config.sensorcommunity.noise.sensorid.label = External Sensor ID
+thing-type.config.sensorcommunity.noise.sensorid.description = Sensor ID from https://deutschland.maps.sensor.community/
+thing-type.config.sensorcommunity.particulate.ipAddress.label = Internal IP Address
+thing-type.config.sensorcommunity.particulate.ipAddress.description = Local IP address of your personal owned sensor
+thing-type.config.sensorcommunity.particulate.sensorid.label = External Sensor ID
+thing-type.config.sensorcommunity.particulate.sensorid.description = Sensor ID from https://deutschland.maps.sensor.community/
+
+# channel types
+
+channel-type.sensorcommunity.hum-channel.label = Humidity
+channel-type.sensorcommunity.hum-channel.description = Humidity from the selected Sensor ID
+channel-type.sensorcommunity.noise-eq-channel.label = Average Noise
+channel-type.sensorcommunity.noise-eq-channel.description = Average noise level from the selected Sensor ID
+channel-type.sensorcommunity.noise-max-channel.label = Maximum Noise
+channel-type.sensorcommunity.noise-max-channel.description = Maximum noise level (last 2.5 minutes) from the selected Sensor ID
+channel-type.sensorcommunity.noise-min-channel.label = Minimum Noise
+channel-type.sensorcommunity.noise-min-channel.description = Minimum noise level (last 2.5 minutes) from the selected Sensor ID
+channel-type.sensorcommunity.pm100-channel.label = Particulate Matter category 10.0
+channel-type.sensorcommunity.pm25-channel.label = Particulate Matter category 2.5
+channel-type.sensorcommunity.pressure-channel.label = Atmospheric Pressure
+channel-type.sensorcommunity.pressure-channel.description = Atmospheric Pressure from the selected Sensor ID
+channel-type.sensorcommunity.pressure-sea-channel.label = Atmospheric Pressure Sea Level
+channel-type.sensorcommunity.pressure-sea-channel.description = Atmospheric Pressure at sea level from the selected Sensor ID
+channel-type.sensorcommunity.temp-channel.label = Temperature
+channel-type.sensorcommunity.temp-channel.description = Temperature from the selected Sensor ID
--- /dev/null
+# add-on
+
+addon.sensorcommunity.name = Sensor.Community Binding
+addon.sensorcommunity.description = Das Binding stellt die Daten der Eigenbau-Sensoren von Sensor.Community zur Verfügung
+
+# thing types
+
+thing-type.sensorcommunity.conditions.label = Umweltsensor
+thing-type.sensorcommunity.conditions.description = Messung der Temperatur, Luftfeuchtigkeit und Luftdruck
+thing-type.sensorcommunity.noise.label = Lärmsensor
+thing-type.sensorcommunity.noise.description = Messung der Lärmbelastung in der Umgebung
+thing-type.sensorcommunity.particulate.label = Feinstaubsensor
+thing-type.sensorcommunity.particulate.description = Messung der Feinstaubbelastung in der Umgebung
+
+# thing types config
+
+thing-type.config.sensorcommunity.conditions.ipAddress.label = Interne IP-Adresse
+thing-type.config.sensorcommunity.conditions.ipAddress.description = Lokale IP-Adresse Ihres persönlichen Sensors
+thing-type.config.sensorcommunity.conditions.sensorid.label = Externe Sensor-ID
+thing-type.config.sensorcommunity.conditions.sensorid.description = Sensor-ID von https\://deutschland.maps.sensor.community/
+thing-type.config.sensorcommunity.noise.ipAddress.label = Interne IP-Adresse
+thing-type.config.sensorcommunity.noise.ipAddress.description = Lokale IP-Adresse Ihres persönlichen Sensors
+thing-type.config.sensorcommunity.noise.sensorid.label = Externe Sensor-ID
+thing-type.config.sensorcommunity.noise.sensorid.description = Sensor-ID von https\://deutschland.maps.sensor.community/
+thing-type.config.sensorcommunity.particulate.ipAddress.label = Interne IP-Adresse
+thing-type.config.sensorcommunity.particulate.ipAddress.description = Lokale IP-Adresse Ihres persönlichen Sensors
+thing-type.config.sensorcommunity.particulate.sensorid.label = Externe Sensor-ID
+thing-type.config.sensorcommunity.particulate.sensorid.description = Sensor-ID von https\://deutschland.maps.sensor.community/
+
+# channel types
+
+channel-type.sensorcommunity.hum-channel.label = Luftfeuchtigkeit
+channel-type.sensorcommunity.hum-channel.description = Luftfeuchtigkeit der ausgewählten Sensor-ID
+channel-type.sensorcommunity.noise-eq-channel.label = Durchschnittlicher Lärmpegel
+channel-type.sensorcommunity.noise-eq-channel.description = Durchschnittlicher Rauschpegel der ausgewählten Sensor-ID
+channel-type.sensorcommunity.noise-max-channel.label = Maximaler Lärmpegel
+channel-type.sensorcommunity.noise-max-channel.description = Maximaler Rauschpegel (letzte 2,5 Minuten) der ausgewählten Sensor-ID
+channel-type.sensorcommunity.noise-min-channel.label = Minimaler Lärmpegel
+channel-type.sensorcommunity.noise-min-channel.description = Minimaler Rauschpegel (letzte 2,5 Minuten) der ausgewählten Sensor-ID
+channel-type.sensorcommunity.pm100-channel.label = Feinstaub der Kategorie PM 10.0
+channel-type.sensorcommunity.pm25-channel.label = Feinstaub der Kategorie PM 2.5
+channel-type.sensorcommunity.pressure-channel.label = Atmosphärischer Druck
+channel-type.sensorcommunity.pressure-channel.description = Atmosphärischer Druck der ausgewählten Sensor-ID
+channel-type.sensorcommunity.pressure-sea-channel.label = Atmosphärischer Druck Auf Meereshöhe
+channel-type.sensorcommunity.pressure-sea-channel.description = Atmosphärischer Druck auf Seehöhe der ausgewählten Sensor-ID
+channel-type.sensorcommunity.temp-channel.label = Temperatur
+channel-type.sensorcommunity.temp-channel.description = Temperatur der ausgewählten Sensor-ID
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="sensorcommunity"
+ 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">
+
+ <thing-type id="particulate">
+ <label>Particulate Sensor</label>
+ <description>Sensor to measure Particulate Matter (PM)</description>
+
+ <channels>
+ <channel id="pm25" typeId="pm25-channel"/>
+ <channel id="pm100" typeId="pm100-channel"/>
+ </channels>
+
+ <config-description>
+ <parameter name="ipAddress" type="text">
+ <context>network-address</context>
+ <label>Internal IP Address</label>
+ <description>Local IP address of your personal owned sensor</description>
+ </parameter>
+ <parameter name="sensorid" type="integer">
+ <label>External Sensor ID</label>
+ <description>Sensor ID from https://deutschland.maps.sensor.community/</description>
+ </parameter>
+ </config-description>
+ </thing-type>
+
+ <thing-type id="conditions">
+ <label>Condition Sensor</label>
+ <description>Sensor to measure Temperature and Humidity conditions</description>
+
+ <channels>
+ <channel id="temperature" typeId="temp-channel"/>
+ <channel id="humidity" typeId="hum-channel"/>
+ <channel id="pressure" typeId="pressure-channel"/>
+ <channel id="pressure-sea" typeId="pressure-sea-channel"/>
+ </channels>
+
+ <config-description>
+ <parameter name="ipAddress" type="text">
+ <context>network-address</context>
+ <label>Internal IP Address</label>
+ <description>Local IP address of your personal owned sensor</description>
+ </parameter>
+ <parameter name="sensorid" type="integer">
+ <label>External Sensor ID</label>
+ <description>Sensor ID from https://deutschland.maps.sensor.community/</description>
+ </parameter>
+ </config-description>
+ </thing-type>
+
+ <thing-type id="noise">
+ <label>Noise Sensor</label>
+ <description>Sensor to measure noise on location</description>
+
+ <channels>
+ <channel id="noise-eq" typeId="noise-eq-channel"/>
+ <channel id="noise-min" typeId="noise-min-channel"/>
+ <channel id="noise-max" typeId="noise-max-channel"/>
+ </channels>
+
+ <config-description>
+ <parameter name="ipAddress" type="text">
+ <context>network-address</context>
+ <label>Internal IP Address</label>
+ <description>Local IP address of your personal owned sensor</description>
+ </parameter>
+ <parameter name="sensorid" type="integer">
+ <label>External Sensor ID</label>
+ <description>Sensor ID from https://deutschland.maps.sensor.community/</description>
+ </parameter>
+ </config-description>
+ </thing-type>
+
+ <channel-type id="pm25-channel">
+ <item-type>Number:Density</item-type>
+ <label>Particulate Matter category 2.5</label>
+ <state pattern="%.1f %unit%" readOnly="true"/>
+ </channel-type>
+ <channel-type id="pm100-channel">
+ <item-type>Number:Density</item-type>
+ <label>Particulate Matter category 10.0</label>
+ <state pattern="%.1f %unit%" readOnly="true"/>
+ </channel-type>
+ <channel-type id="temp-channel">
+ <item-type>Number:Temperature</item-type>
+ <label>Temperature</label>
+ <description>Temperature from the selected Sensor ID</description>
+ <state pattern="%.1f %unit%" readOnly="true"/>
+ </channel-type>
+ <channel-type id="hum-channel">
+ <item-type>Number:Dimensionless</item-type>
+ <label>Humidity</label>
+ <description>Humidity from the selected Sensor ID</description>
+ <state pattern="%.1f %unit%" readOnly="true"/>
+ </channel-type>
+ <channel-type id="pressure-channel">
+ <item-type>Number:Pressure</item-type>
+ <label>Atmospheric Pressure</label>
+ <description>Atmospheric Pressure from the selected Sensor ID</description>
+ <state pattern="%.1f %unit%" readOnly="true"/>
+ </channel-type>
+ <channel-type id="pressure-sea-channel">
+ <item-type>Number:Pressure</item-type>
+ <label>Atmospheric Pressure Sea Level</label>
+ <description>Atmospheric Pressure at sea level from the selected Sensor ID</description>
+ <state pattern="%.1f %unit%" readOnly="true"/>
+ </channel-type>
+ <channel-type id="noise-eq-channel">
+ <item-type>Number:Dimensionless</item-type>
+ <label>Average Noise</label>
+ <description>Average noise level from the selected Sensor ID</description>
+ <state pattern="%.1f %unit%" readOnly="true"/>
+ </channel-type>
+ <channel-type id="noise-min-channel">
+ <item-type>Number:Dimensionless</item-type>
+ <label>Minimum Noise</label>
+ <description>Minimum noise level (last 2.5 minutes) from the selected Sensor ID</description>
+ <state pattern="%.1f %unit%" readOnly="true"/>
+ </channel-type>
+ <channel-type id="noise-max-channel">
+ <item-type>Number:Dimensionless</item-type>
+ <label>Maximum Noise</label>
+ <description>Maximum noise level (last 2.5 minutes) from the selected Sensor ID</description>
+ <state pattern="%.1f %unit%" readOnly="true"/>
+ </channel-type>
+</thing:thing-descriptions>
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.openhab.core.library.unit.MetricPrefix.HECTO;
+
+import java.util.HashMap;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.Test;
+import org.openhab.binding.sensorcommunity.internal.handler.BaseSensorHandler.UpdateStatus;
+import org.openhab.binding.sensorcommunity.internal.mock.ConditionHandlerExtension;
+import org.openhab.binding.sensorcommunity.internal.mock.ThingMock;
+import org.openhab.binding.sensorcommunity.internal.util.FileReader;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.unit.SIUnits;
+import org.openhab.core.library.unit.Units;
+
+/**
+ * The {@link ConditionHandlerTest} Test Condition Handler updates
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@NonNullByDefault
+public class ConditionHandlerTest {
+
+ @Test
+ public void testValidNoPressureUpdate() {
+ ThingMock t = new ThingMock();
+
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ // String sensorid taken from thing-types.xml
+ properties.put("sensorid", 12345);
+ t.setConfiguration(properties);
+
+ ConditionHandlerExtension condHandler = new ConditionHandlerExtension(t);
+ String pmJson = FileReader.readFileInString("src/test/resources/condition-result-no-pressure.json");
+ if (pmJson != null) {
+ UpdateStatus result = condHandler.updateChannels(pmJson);
+ assertEquals(UpdateStatus.OK, result, "Valid update");
+ assertEquals(QuantityType.valueOf(22.7, SIUnits.CELSIUS), condHandler.getTemperature(), "Temperature");
+ assertEquals(QuantityType.valueOf(61., Units.PERCENT), condHandler.getHumidity(), "Humidity");
+ assertEquals(QuantityType.valueOf(-1, HECTO(SIUnits.PASCAL)), condHandler.getPressure(), "Pressure");
+ assertEquals(QuantityType.valueOf(-1, HECTO(SIUnits.PASCAL)), condHandler.getPressureSea(), "Pressure Sea");
+ } else {
+ assertTrue(false);
+ }
+ }
+
+ @Test
+ public void testValidWithPressureUpdate() {
+ ThingMock t = new ThingMock();
+
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ // String sensorid taken from thing-types.xml
+ properties.put("sensorid", 12345);
+ t.setConfiguration(properties);
+
+ ConditionHandlerExtension condHandler = new ConditionHandlerExtension(t);
+ String pmJson = FileReader.readFileInString("src/test/resources/condition-result-plus-pressure.json");
+ if (pmJson != null) {
+ UpdateStatus result = condHandler.updateChannels(pmJson);
+ assertEquals(UpdateStatus.OK, result, "Valid update");
+ assertEquals(QuantityType.valueOf(21.5, SIUnits.CELSIUS), condHandler.getTemperature(), "Temperature");
+ assertEquals(QuantityType.valueOf(58.5, Units.PERCENT), condHandler.getHumidity(), "Humidity");
+ assertEquals(QuantityType.valueOf(1002.0, HECTO(SIUnits.PASCAL)), condHandler.getPressure(), "Pressure");
+ assertEquals(QuantityType.valueOf(1019.7, HECTO(SIUnits.PASCAL)), condHandler.getPressureSea(),
+ "Pressure Sea");
+ } else {
+ assertTrue(false);
+ }
+ }
+
+ @Test
+ public void testInvalidUpdate() {
+ ThingMock t = new ThingMock();
+
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ // String sensorid taken from thing-types.xml
+ properties.put("sensorid", 12345);
+ t.setConfiguration(properties);
+
+ ConditionHandlerExtension condHandler = new ConditionHandlerExtension(t);
+ String pmJson = FileReader.readFileInString("src/test/resources/noise-result.json");
+ if (pmJson != null) {
+ UpdateStatus result = condHandler.updateChannels(pmJson);
+ assertEquals(UpdateStatus.VALUE_ERROR, result, "Valid update");
+ } else {
+ assertTrue(false);
+ }
+ }
+
+ @Test
+ public void testEmptyUpdate() {
+ ThingMock t = new ThingMock();
+
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ // String sensorid taken from thing-types.xml
+ properties.put("sensorid", 12345);
+ t.setConfiguration(properties);
+
+ ConditionHandlerExtension condHandler = new ConditionHandlerExtension(t);
+ UpdateStatus result = condHandler.updateChannels("[]");
+ assertEquals(UpdateStatus.VALUE_EMPTY, result, "Valid update");
+ }
+
+ @Test
+ public void testNullUpdate() {
+ ThingMock t = new ThingMock();
+
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ // String sensorid taken from thing-types.xml
+ properties.put("sensorid", 12345);
+ t.setConfiguration(properties);
+
+ ConditionHandlerExtension condHandler = new ConditionHandlerExtension(t);
+ UpdateStatus result = condHandler.updateChannels(null);
+ assertEquals(UpdateStatus.CONNECTION_ERROR, result, "Valid update");
+ }
+
+ @Test
+ public void testInternalUpdate() {
+ ThingMock t = new ThingMock();
+
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ // String sensorid taken from thing-types.xml
+ properties.put("ipAddress", "192.168.178.1");
+ t.setConfiguration(properties);
+
+ ConditionHandlerExtension condHandler = new ConditionHandlerExtension(t);
+ String pmJson = FileReader.readFileInString("src/test/resources/internal-data.json");
+ if (pmJson != null) {
+ UpdateStatus result = condHandler.updateChannels("[" + pmJson + "]");
+ assertEquals(UpdateStatus.OK, result, "Valid update");
+ assertEquals(QuantityType.valueOf(17.6, SIUnits.CELSIUS), condHandler.getTemperature(), "Temperature");
+ assertEquals(QuantityType.valueOf(57.8, Units.PERCENT), condHandler.getHumidity(), "Humidity");
+ assertEquals(QuantityType.valueOf(986.8, HECTO(SIUnits.PASCAL)), condHandler.getPressure(), "Pressure");
+ assertEquals(QuantityType.valueOf(-1, HECTO(SIUnits.PASCAL)), condHandler.getPressureSea(), "Pressure Sea");
+ } else {
+ assertTrue(false);
+ }
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.Test;
+import org.openhab.binding.sensorcommunity.internal.dto.SensorData;
+import org.openhab.binding.sensorcommunity.internal.dto.SensorDataValue;
+import org.openhab.binding.sensorcommunity.internal.util.FileReader;
+import org.openhab.binding.sensorcommunity.internal.utils.Constants;
+
+import com.google.gson.Gson;
+
+/**
+ * The {@link DTOTest} Data Transfer Object - test conversions
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@NonNullByDefault
+public class DTOTest {
+
+ @Test
+ public void testConditions() {
+ String result = FileReader.readFileInString("src/test/resources/condition-result-no-pressure.json");
+ Gson gson = new Gson();
+ SensorData[] valueArray = gson.fromJson(result, SensorData[].class);
+ // System.out.println(valueArray.length);
+ assertEquals(2, valueArray.length, "Array size");
+
+ SensorData d = valueArray[0];
+ // Assure latest data is taken
+ String dateStr = d.getTimeStamp();
+ if ("2020-06-09 06:38:08".equals(dateStr)) {
+ // take newer one
+ d = valueArray[1];
+ }
+ List<SensorDataValue> sensorDataVaueList = d.getSensorDataValues();
+ assertNotNull(d);
+ sensorDataVaueList.forEach(v -> {
+ if (Constants.TEMPERATURE.equals(v.getValueType())) {
+ assertEquals("22.70", v.getValue(), "Temperature");
+ } else if (Constants.HUMIDITY.equals(v.getValueType())) {
+ assertEquals("61.00", v.getValue(), "Humidity");
+ }
+ });
+ }
+
+ @Test
+ public void testDecoding() {
+ String result = FileReader.readFileInString("src/test/resources/condition-result-no-pressure.json");
+ Gson gson = new Gson();
+ SensorData[] valueArray = gson.fromJson(result, SensorData[].class);
+ // System.out.println(valueArray.length);
+ assertEquals(2, valueArray.length, "Array size");
+
+ SensorData d = valueArray[0];
+ // Assure latest data is taken
+ String dateStr = d.getTimeStamp();
+ if (dateStr.equals("2020-06-09 06:38:08")) {
+ // take newer one
+ d = valueArray[1];
+ }
+
+ // test decoding a small part
+ String json = gson.toJson(d);
+ // System.out.println(json);
+ // check if correct timestamp is included
+ assertTrue(json.contains("\"timestamp\":\"2020-06-09 06:40:34\""));
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.List;
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.openhab.binding.sensorcommunity.internal.dto.SensorDataValue;
+import org.openhab.binding.sensorcommunity.internal.handler.HTTPHandler;
+import org.openhab.binding.sensorcommunity.internal.util.FileReader;
+
+/**
+ * The {@link HTTPHandlerEvalTest} test all evaluations on SensorDataValues
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@NonNullByDefault
+public class HTTPHandlerEvalTest {
+
+ private @Nullable List<SensorDataValue> conditions;
+ private @Nullable List<SensorDataValue> particulate;
+ private @Nullable List<SensorDataValue> noise;
+ private HTTPHandler http = new HTTPHandler();
+
+ @BeforeEach
+ public void setUp() {
+ String conditionsStr = FileReader.readFileInString("src/test/resources/condition-result-no-pressure.json");
+ assertNotNull(conditionsStr);
+ Objects.requireNonNull(conditionsStr);
+ conditions = http.getLatestValues(conditionsStr);
+
+ String particulateStr = FileReader.readFileInString("src/test/resources/pm-result.json");
+ assertNotNull(particulateStr);
+ Objects.requireNonNull(particulateStr);
+ particulate = http.getLatestValues(particulateStr);
+
+ String noiseStr = FileReader.readFileInString("src/test/resources/noise-result.json");
+ assertNotNull(noiseStr);
+ Objects.requireNonNull(noiseStr);
+ noise = http.getLatestValues(noiseStr);
+ }
+
+ @Test
+ public void testIsCondition() {
+ assertTrue(http.isCondition(conditions));
+ assertFalse(http.isCondition(particulate));
+ assertFalse(http.isCondition(noise));
+ assertFalse(http.isCondition(null));
+ }
+
+ @Test
+ public void testIsParticulate() {
+ assertFalse(http.isParticulate(conditions));
+ assertTrue(http.isParticulate(particulate));
+ assertFalse(http.isParticulate(noise));
+ assertFalse(http.isParticulate(null));
+ }
+
+ @Test
+ public void testIsNoise() {
+ assertFalse(http.isNoise(conditions));
+ assertFalse(http.isNoise(particulate));
+ assertTrue(http.isNoise(noise));
+ assertFalse(http.isNoise(null));
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.List;
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.Test;
+import org.openhab.binding.sensorcommunity.internal.dto.SensorDataValue;
+import org.openhab.binding.sensorcommunity.internal.handler.HTTPHandler;
+import org.openhab.binding.sensorcommunity.internal.util.FileReader;
+import org.openhab.binding.sensorcommunity.internal.utils.Constants;
+
+/**
+ * The {@link HTTPHandlerValueTest} test values decoding of HTTPHandler
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@NonNullByDefault
+public class HTTPHandlerValueTest {
+ private HTTPHandler http = new HTTPHandler();
+
+ /**
+ * test if really the latest values are returned
+ * resource1 is json with ordering according to time while resource2 the entries flipped
+ */
+ @Test
+ public void testValueDecoding() {
+ String resource1 = FileReader.readFileInString("src/test/resources/condition-result-no-pressure.json");
+ assertNotNull(resource1);
+ Objects.requireNonNull(resource1);
+ List<SensorDataValue> l = http.getLatestValues(resource1);
+ assertNotNull(l);
+ Objects.requireNonNull(l);
+ l.forEach(sd -> {
+ testSensorValue(sd);
+ });
+
+ String resource2 = FileReader
+ .readFileInString("src/test/resources/condition-result-no-pressure-flipped-values.json");
+ assertNotNull(resource2);
+ Objects.requireNonNull(resource2);
+ l = http.getLatestValues(resource2);
+ assertNotNull(l);
+ Objects.requireNonNull(l);
+ l.forEach(sd -> {
+ testSensorValue(sd);
+ });
+ }
+
+ private void testSensorValue(SensorDataValue s) {
+ if (s.getValueType().equals(Constants.TEMPERATURE)) {
+ assertEquals("22.70", s.getValue(), "Temperature resource 1");
+ } else if (s.getValueType().equals(Constants.HUMIDITY)) {
+ assertEquals("61.00", s.getValue(), "Humidity resource 1");
+ } else {
+ assertTrue(false);
+ }
+ // System.out.println(s.getValue_type() + ":" + s.getValue());
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.HashMap;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.Test;
+import org.openhab.binding.sensorcommunity.internal.handler.BaseSensorHandler.UpdateStatus;
+import org.openhab.binding.sensorcommunity.internal.mock.NoiseHandlerExtension;
+import org.openhab.binding.sensorcommunity.internal.mock.ThingMock;
+import org.openhab.binding.sensorcommunity.internal.util.FileReader;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.unit.Units;
+
+/**
+ * The {@link NoiseHandlerTest} Test Noise Handler updates
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@NonNullByDefault
+public class NoiseHandlerTest {
+
+ @Test
+ public void testValidUpdate() {
+ ThingMock t = new ThingMock();
+
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ // String sensorid taken from thing-types.xml
+ properties.put("sensorid", 12345);
+ t.setConfiguration(properties);
+
+ NoiseHandlerExtension noiseHandler = new NoiseHandlerExtension(t);
+ String pmJson = FileReader.readFileInString("src/test/resources/noise-result.json");
+ if (pmJson != null) {
+ UpdateStatus result = noiseHandler.updateChannels(pmJson);
+ assertEquals(UpdateStatus.OK, result, "Valid update");
+ assertEquals(QuantityType.valueOf(51.0, Units.DECIBEL), noiseHandler.getNoiseEQCache(), "Noise EQ");
+ assertEquals(QuantityType.valueOf(47.2, Units.DECIBEL), noiseHandler.getNoiseMinCache(), "Noise Min");
+ assertEquals(QuantityType.valueOf(57.0, Units.DECIBEL), noiseHandler.getNoiseMaxCache(), "Noise Max");
+ } else {
+ assertTrue(false);
+ }
+ }
+
+ @Test
+ public void testInvalidUpdate() {
+ ThingMock t = new ThingMock();
+
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ // String sensorid taken from thing-types.xml
+ properties.put("sensorid", 12345);
+ t.setConfiguration(properties);
+
+ NoiseHandlerExtension noiseHandler = new NoiseHandlerExtension(t);
+ String pmJson = FileReader.readFileInString("src/test/resources/condition-result-no-pressure.json");
+ if (pmJson != null) {
+ UpdateStatus result = noiseHandler.updateChannels(pmJson);
+ assertEquals(UpdateStatus.VALUE_ERROR, result, "Valid update");
+ assertEquals(QuantityType.valueOf(-1, Units.DECIBEL), noiseHandler.getNoiseEQCache(), "Values undefined");
+ assertEquals(QuantityType.valueOf(-1, Units.DECIBEL), noiseHandler.getNoiseMinCache(), "Values undefined");
+ assertEquals(QuantityType.valueOf(-1, Units.DECIBEL), noiseHandler.getNoiseMaxCache(), "Values undefined");
+ } else {
+ assertTrue(false);
+ }
+ }
+
+ @Test
+ public void testEmptyUpdate() {
+ ThingMock t = new ThingMock();
+
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ // String sensorid taken from thing-types.xml
+ properties.put("sensorid", 12345);
+ t.setConfiguration(properties);
+
+ NoiseHandlerExtension noiseHandler = new NoiseHandlerExtension(t);
+ UpdateStatus result = noiseHandler.updateChannels("[]");
+ assertEquals(UpdateStatus.VALUE_EMPTY, result, "Valid update");
+ }
+
+ @Test
+ public void testNullUpdate() {
+ ThingMock t = new ThingMock();
+
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ // String sensorid taken from thing-types.xml
+ properties.put("sensorid", 12345);
+ t.setConfiguration(properties);
+
+ NoiseHandlerExtension noiseHandler = new NoiseHandlerExtension(t);
+ UpdateStatus result = noiseHandler.updateChannels(null);
+ assertEquals(UpdateStatus.CONNECTION_ERROR, result, "Valid update");
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.Test;
+import org.openhab.binding.sensorcommunity.internal.utils.NumberUtils;
+
+/**
+ * The {@link NumberTest} Test rounding and converting Numbers
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@NonNullByDefault
+public class NumberTest {
+
+ @Test
+ public void testRoundingUp() {
+ double d1 = 1.95;
+ double d1r2 = NumberUtils.round(d1, 2);
+ assertEquals("1.95", Double.toString(d1r2), "Double 1.95, 2 places");
+ // System.out.println("D1R2 " + d1r2);
+ double d1r1 = NumberUtils.round(d1, 1);
+ // System.out.println("D1R1 " + d1r1);
+ assertEquals("2.0", Double.toString(d1r1), "Double 1.95, 1 place");
+ }
+
+ @Test
+ public void testRoundingDown() {
+ double d1 = 1.94;
+ double d1r2 = NumberUtils.round(d1, 2);
+ assertEquals("1.94", Double.toString(d1r2), "Double 1.94, 2 places");
+ // System.out.println("D1R2 " + d1r2);
+ double d1r1 = NumberUtils.round(d1, 1);
+ // System.out.println("D1R1 " + d1r1);
+ assertEquals("1.9", Double.toString(d1r1), "Double 1.94, 1 place");
+ }
+
+ @Test
+ public void testStringNumbers() {
+ String d1 = "1.94";
+ double d1r2 = NumberUtils.round(d1, 2);
+ assertEquals("1.94", Double.toString(d1r2), "Double 1.94, 2 places");
+ // System.out.println("D1R2 " + d1r2);
+ double d1r1 = NumberUtils.round(d1, 1);
+ // System.out.println("D1R1 " + d1r1);
+ assertEquals("1.9", Double.toString(d1r1), "Double 1.94, 1 place");
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.HashMap;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.Test;
+import org.openhab.binding.sensorcommunity.internal.handler.BaseSensorHandler.ConfigStatus;
+import org.openhab.binding.sensorcommunity.internal.handler.BaseSensorHandler.LifecycleStatus;
+import org.openhab.binding.sensorcommunity.internal.handler.BaseSensorHandler.UpdateStatus;
+import org.openhab.binding.sensorcommunity.internal.mock.PMHandlerExtension;
+import org.openhab.binding.sensorcommunity.internal.mock.ThingMock;
+import org.openhab.binding.sensorcommunity.internal.util.FileReader;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.unit.Units;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link PMHandlerTest} Test Particualte Matter Handler - Config and updates
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@NonNullByDefault
+public class PMHandlerTest {
+ private Logger logger = LoggerFactory.getLogger(PMHandlerTest.class);
+
+ @Test
+ public void testValidConfigStatus() {
+ ThingMock t = new ThingMock();
+
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ // String sensorid taken from thing-types.xml
+ properties.put("sensorid", 12345);
+ t.setConfiguration(properties);
+
+ PMHandlerExtension pmHandler = new PMHandlerExtension(t);
+ pmHandler.initialize();
+ logger.info("LC status: {}", pmHandler.getLifecycleStatus());
+ int retryCount = 0; // Test shall fail after max 10 seconds
+ while (pmHandler.getLifecycleStatus() != LifecycleStatus.RUNNING && retryCount < 20) {
+ try {
+ logger.info("LC running not reached - wait");
+ Thread.sleep(500);
+ retryCount++;
+ } catch (InterruptedException e) {
+ // nothing to do
+ }
+ }
+ /*
+ * Test if config status is 0 = CONFIG_OK for valid configuration. Take real int for comparison instead of
+ * BaseHandler constants - in case of change test needs to be adapted
+ */
+ assertEquals(ConfigStatus.EXTERNAL_SENSOR_OK, pmHandler.getConfigStatus(), "Handler Configuration status");
+ }
+
+ @Test
+ public void testInvalidConfigStatus() {
+ ThingMock t = new ThingMock();
+
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ // String sensorid taken from thing-types.xml
+ properties.put("sensorid", -1);
+ t.setConfiguration(properties);
+
+ PMHandlerExtension pmHandler = new PMHandlerExtension(t);
+ pmHandler.initialize();
+ logger.info("LC status: {}", pmHandler.getLifecycleStatus());
+ int retryCount = 0; // Test shall fail after max 10 seconds
+ while (pmHandler.getLifecycleStatus() != LifecycleStatus.RUNNING && retryCount < 20) {
+ try {
+ logger.info("LC running not reached - wait");
+ Thread.sleep(500);
+ retryCount++;
+ } catch (InterruptedException e) {
+ // nothing to do
+ }
+ }
+ /*
+ * Test if config status is 3 = CONFIG_SENSOR_NUMBER for invalid configuration with non-number sensorid. Take
+ * real int for comparison instead of BaseHandler constants - in case of change test needs to be adapted
+ */
+ assertEquals(ConfigStatus.SENSOR_ID_NEGATIVE, pmHandler.getConfigStatus(), "Handler Configuration status");
+ }
+
+ @Test
+ public void testValidUpdate() {
+ ThingMock t = new ThingMock();
+
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ // String sensorid taken from thing-types.xml
+ properties.put("sensorid", 12345);
+ t.setConfiguration(properties);
+
+ PMHandlerExtension pmHandler = new PMHandlerExtension(t);
+ pmHandler.initialize();
+ String pmJson = FileReader.readFileInString("src/test/resources/pm-result.json");
+ if (pmJson != null) {
+ UpdateStatus result = pmHandler.updateChannels(pmJson);
+ assertEquals(UpdateStatus.OK, result, "Valid update");
+ assertEquals(QuantityType.valueOf(2.9, Units.MICROGRAM_PER_CUBICMETRE), pmHandler.getPM25Cache(), "PM25");
+ assertEquals(QuantityType.valueOf(5.2, Units.MICROGRAM_PER_CUBICMETRE), pmHandler.getPM100Cache(), "PM100");
+ } else {
+ assertTrue(false);
+ }
+ }
+
+ @Test
+ public void testInvalidUpdate() {
+ ThingMock t = new ThingMock();
+
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ // String sensorid taken from thing-types.xml
+ properties.put("sensorid", 12345);
+ t.setConfiguration(properties);
+
+ PMHandlerExtension pmHandler = new PMHandlerExtension(t);
+ String pmJson = FileReader.readFileInString("src/test/resources/noise-result.json");
+ if (pmJson != null) {
+ UpdateStatus result = pmHandler.updateChannels(pmJson);
+ assertEquals(UpdateStatus.VALUE_ERROR, result, "Valid update");
+ assertEquals(QuantityType.valueOf(-1, Units.MICROGRAM_PER_CUBICMETRE), pmHandler.getPM25Cache(),
+ "Values undefined");
+ assertEquals(QuantityType.valueOf(-1, Units.MICROGRAM_PER_CUBICMETRE), pmHandler.getPM100Cache(),
+ "Values undefined");
+ } else {
+ assertTrue(false);
+ }
+ }
+
+ @Test
+ public void testEmptyUpdate() {
+ ThingMock t = new ThingMock();
+
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ // String sensorid taken from thing-types.xml
+ properties.put("sensorid", 12345);
+ t.setConfiguration(properties);
+
+ PMHandlerExtension pmHandler = new PMHandlerExtension(t);
+ UpdateStatus result = pmHandler.updateChannels("[]");
+ assertEquals(UpdateStatus.VALUE_EMPTY, result, "Valid update");
+ }
+
+ @Test
+ public void testNullUpdate() {
+ ThingMock t = new ThingMock();
+
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ // String sensorid taken from thing-types.xml
+ properties.put("ipAdress", "192.168.178.1");
+ t.setConfiguration(properties);
+
+ PMHandlerExtension pmHandler = new PMHandlerExtension(t);
+ UpdateStatus result = pmHandler.updateChannels(null);
+ assertEquals(UpdateStatus.CONNECTION_ERROR, result, "Valid update");
+ }
+
+ @Test
+ public void testInternalPMSensor() {
+ ThingMock t = new ThingMock();
+
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ // String sensorid taken from thing-types.xml
+ properties.put("sensorid", 12345);
+ t.setConfiguration(properties);
+
+ PMHandlerExtension pmHandler = new PMHandlerExtension(t);
+ pmHandler.initialize();
+ String pmJson = FileReader.readFileInString("src/test/resources/internal-data.json");
+ if (pmJson != null) {
+ UpdateStatus result = pmHandler.updateChannels("[" + pmJson + "]");
+ assertEquals(UpdateStatus.OK, result, "Valid update");
+ assertEquals(QuantityType.valueOf(4.3, Units.MICROGRAM_PER_CUBICMETRE), pmHandler.getPM25Cache(), "PM25");
+ assertEquals(QuantityType.valueOf(10.5, Units.MICROGRAM_PER_CUBICMETRE), pmHandler.getPM100Cache(),
+ "PM100");
+ } else {
+ assertTrue(false);
+ }
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal.mock;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.sensorcommunity.internal.handler.ConditionHandler;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.types.State;
+
+/**
+ * The {@link NoiseHandlerExtension} Test Noise Handler Extension with additonal state queries
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@NonNullByDefault
+public class ConditionHandlerExtension extends ConditionHandler {
+
+ public ConditionHandlerExtension(Thing thing) {
+ super(thing);
+ }
+
+ public ConfigStatus getConfigStatus() {
+ return configStatus;
+ }
+
+ public UpdateStatus getUpdateStatus() {
+ return lastUpdateStatus;
+ }
+
+ public @Nullable State getTemperature() {
+ return temperatureCache;
+ }
+
+ public @Nullable State getHumidity() {
+ return humidityCache;
+ }
+
+ public @Nullable State getPressure() {
+ return pressureCache;
+ }
+
+ public @Nullable State getPressureSea() {
+ return pressureSeaCache;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal.mock;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.sensorcommunity.internal.handler.NoiseHandler;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.types.State;
+
+/**
+ * The {@link NoiseHandlerExtension} Test Noise Handler Extension with additonal state queries
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@NonNullByDefault
+public class NoiseHandlerExtension extends NoiseHandler {
+
+ public NoiseHandlerExtension(Thing thing) {
+ super(thing);
+ }
+
+ public ConfigStatus getConfigStatus() {
+ return configStatus;
+ }
+
+ public UpdateStatus getUpdateStatus() {
+ return lastUpdateStatus;
+ }
+
+ public @Nullable State getNoiseEQCache() {
+ return noiseEQCache;
+ }
+
+ public @Nullable State getNoiseMinCache() {
+ return noiseMinCache;
+ }
+
+ public @Nullable State getNoiseMaxCache() {
+ return noiseMaxCache;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal.mock;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.sensorcommunity.internal.handler.PMHandler;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.types.State;
+
+/**
+ * The {@link PMHandlerExtension} Test Particualte Matter Handler Extension with additonal state queries
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@NonNullByDefault
+public class PMHandlerExtension extends PMHandler {
+
+ public PMHandlerExtension(Thing thing) {
+ super(thing);
+ }
+
+ public ConfigStatus getConfigStatus() {
+ return configStatus;
+ }
+
+ public UpdateStatus getUpdateStatus() {
+ return lastUpdateStatus;
+ }
+
+ public @Nullable State getPM25Cache() {
+ return pm25Cache;
+ }
+
+ public @Nullable State getPM100Cache() {
+ return pm100Cache;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal.mock;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.config.core.Configuration;
+import org.openhab.core.thing.Channel;
+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.ThingStatusInfo;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.ThingUID;
+import org.openhab.core.thing.binding.ThingHandler;
+
+/**
+ * The {@link ThingMock} Thing Mock
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@NonNullByDefault
+public class ThingMock implements Thing {
+ private Configuration config = new Configuration();
+
+ @Override
+ public @Nullable String getLabel() {
+ return null;
+ }
+
+ @Override
+ public void setLabel(@Nullable String label) {
+ }
+
+ @Override
+ public List<Channel> getChannels() {
+ return new ArrayList<Channel>();
+ }
+
+ @Override
+ public List<Channel> getChannelsOfGroup(String channelGroupId) {
+ return new ArrayList<Channel>();
+ }
+
+ @Override
+ public @Nullable Channel getChannel(String channelId) {
+ return null;
+ }
+
+ @Override
+ public @Nullable Channel getChannel(ChannelUID channelUID) {
+ return null;
+ }
+
+ @Override
+ public ThingStatus getStatus() {
+ return ThingStatus.UNKNOWN;
+ }
+
+ @Override
+ public ThingStatusInfo getStatusInfo() {
+ return new ThingStatusInfo(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "");
+ }
+
+ @Override
+ public void setStatusInfo(ThingStatusInfo status) {
+ }
+
+ @Override
+ public void setHandler(@Nullable ThingHandler thingHandler) {
+ }
+
+ @Override
+ public @Nullable ThingHandler getHandler() {
+ return null;
+ }
+
+ @Override
+ public @Nullable ThingUID getBridgeUID() {
+ return null;
+ }
+
+ @Override
+ public void setBridgeUID(@Nullable ThingUID bridgeUID) {
+ }
+
+ @Override
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ public void setConfiguration(Map<String, Object> m) {
+ config = new Configuration(m);
+ }
+
+ @Override
+ public ThingUID getUID() {
+ return new ThingUID("sensorcommunity", "test");
+ }
+
+ @Override
+ public ThingTypeUID getThingTypeUID() {
+ return new ThingTypeUID("sensorcommunity:any");
+ }
+
+ @Override
+ public Map<String, String> getProperties() {
+ return new HashMap<String, String>();
+ }
+
+ @Override
+ public @Nullable String setProperty(String name, @Nullable String value) {
+ return null;
+ }
+
+ @Override
+ public void setProperties(Map<String, String> properties) {
+ }
+
+ @Override
+ public @Nullable String getLocation() {
+ return null;
+ }
+
+ @Override
+ public void setLocation(@Nullable String location) {
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return false;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal.util;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeParseException;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.Test;
+import org.openhab.binding.sensorcommunity.internal.utils.DateTimeUtils;
+
+/**
+ * The {@link DateTimeTest} Test DateTimeFormatter provided in utils package
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@NonNullByDefault
+public class DateTimeTest {
+
+ @Test
+ public void testJSonTime() {
+ String jsonDateString = "2020-08-14 14:53:21";
+ try {
+ LocalDateTime dt = LocalDateTime.from(DateTimeUtils.DTF.parse(jsonDateString));
+ assertEquals(14, dt.getDayOfMonth(), "Day");
+ assertEquals(8, dt.getMonthValue(), "Month");
+ assertEquals(2020, dt.getYear(), "Year");
+
+ String s = dt.format(DateTimeUtils.DTF);
+ assertEquals(jsonDateString, s, "String");
+ } catch (DateTimeParseException e) {
+ assertFalse(true);
+ }
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.sensorcommunity.internal.util;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * The {@link FileReader} Helper Util to read test resource files
+ *
+ * @author Bernd Weymann - Initial contribution
+ */
+@NonNullByDefault
+public class FileReader {
+
+ public static @Nullable String readFileInString(String filename) {
+ try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filename), "CP1252"));) {
+ StringBuffer buf = new StringBuffer();
+ String sCurrentLine;
+
+ while ((sCurrentLine = br.readLine()) != null) {
+ buf.append(sCurrentLine);
+ }
+ return buf.toString();
+ } catch (IOException e) {
+ // fail if file cannot be read
+ assertTrue(false);
+ }
+ return null;
+ }
+}
--- /dev/null
+[
+ {
+ "id": 731117559,
+ "sensordatavalues": [
+ {
+ "id": 1573660194,
+ "value_type": "temperature",
+ "value": "22.70"
+ },
+ {
+ "id": 1573660195,
+ "value_type": "humidity",
+ "value": "61.00"
+ }
+ ],
+ "timestamp": "2020-06-09 06:40:34",
+ "sampling_rate": null,
+ "location": {
+ "id": 11447,
+ "country": "DE",
+ "altitude": "151.5",
+ "latitude": "50.562",
+ "longitude": "8.504",
+ "indoor": 0,
+ "exact_location": 0
+ },
+ "sensor": {
+ "id": 22562,
+ "pin": "7",
+ "sensor_type": {
+ "id": 9,
+ "manufacturer": "various",
+ "name": "DHT22"
+ }
+ }
+ },
+ {
+ "id": 731094694,
+ "sensordatavalues": [
+ {
+ "id": 1573610869,
+ "value_type": "temperature",
+ "value": "22.50"
+ },
+ {
+ "id": 1573610870,
+ "value_type": "humidity",
+ "value": "62.00"
+ }
+ ],
+ "timestamp": "2020-06-09 06:38:08",
+ "sampling_ra
+ te": null,
+ "location": {
+ "id": 11447,
+ "country": "DE",
+ "altitude": "151.5",
+ "latitude": "50.562",
+ "longitude": "8.504",
+ "indoor": 0,
+ "exact_location": 0
+ },
+ "sensor": {
+ "id": 22562,
+ "pin": "7",
+ "sensor_type": {
+ "id": 9,
+ "manufacturer": "various",
+ "name": "DHT22"
+ }
+ }
+ }
+]
\ No newline at end of file
--- /dev/null
+[
+ {
+ "id": 731094694,
+ "sensordatavalues": [
+ {
+ "id": 1573610869,
+ "value_type": "temperature",
+ "value": "22.50"
+ },
+ {
+ "id": 1573610870,
+ "value_type": "humidity",
+ "value": "62.00"
+ }
+ ],
+ "timestamp": "2020-06-09 06:38:08",
+ "sampling_rate": null,
+ "location": {
+ "id": 11447,
+ "country": "DE",
+ "altitude": "151.5",
+ "latitude": "50.562",
+ "longitude": "8.504",
+ "indoor": 0,
+ "exact_location": 0
+ },
+ "sensor": {
+ "id": 22562,
+ "pin": "7",
+ "sensor_type": {
+ "id": 9,
+ "manufacturer": "various",
+ "name": "DHT22"
+ }
+ }
+ },
+ {
+ "id": 731117559,
+ "sensordatavalues": [
+ {
+ "id": 1573660194,
+ "value_type": "temperature",
+ "value": "22.70"
+ },
+ {
+ "id": 1573660195,
+ "value_type": "humidity",
+ "value": "61.00"
+ }
+ ],
+ "timestamp": "2020-06-09 06:40:34",
+ "sampling_rate": null,
+ "location": {
+ "id": 11447,
+ "country": "DE",
+ "altitude": "151.5",
+ "latitude": "50.562",
+ "longitude": "8.504",
+ "indoor": 0,
+ "exact_location": 0
+ },
+ "sensor": {
+ "id": 22562,
+ "pin": "7",
+ "sensor_type": {
+ "id": 9,
+ "manufacturer": "various",
+ "name": "DHT22"
+ }
+ }
+ }
+]
\ No newline at end of file
--- /dev/null
+[
+ {
+ "id": 1038856661,
+ "sensor": {
+ "id": 28843,
+ "sensor_type": {
+ "id": 17,
+ "manufacturer": "Bosch",
+ "name": "BME280"
+ },
+ "pin": "11"
+ },
+ "timestamp": "2020-07-03 09:39:46",
+ "sampling_rate": null,
+ "location": {
+ "id": 15975,
+ "altitude": "151.2",
+ "longitude": "8.49543571448",
+ "exact_location": 1,
+ "latitude": "50.55591005174",
+ "indoor": 0,
+ "country": "DE"
+ },
+ "sensordatavalues": [
+ {
+ "id": 2237770681,
+ "value_type": "temperature",
+ "value": "21.52"
+ },
+ {
+ "id": 2237770683,
+ "value_type": "pressure",
+ "value": "100199.97"
+ },
+ {
+ "id": 2237770684,
+ "value_type": "humidity",
+ "value": "58.51"
+ },
+ {
+ "value_type": "pressure_at_sealevel",
+ "value": 101968.66
+ }
+ ]
+ },
+ {
+ "id": 1038834126,
+ "sensor": {
+ "id": 28843,
+ "sensor_type": {
+ "id": 17,
+ "manufacturer": "Bosch",
+ "name": "BME280"
+ },
+ "pin": "11"
+ },
+ "timestamp": "2020-07-03 09:37:21",
+ "sampling_rate": null,
+ "location": {
+ "id": 15975,
+ "altitude": "151.2",
+ "longitude": "8.49543571448",
+ "exact_location": 1,
+ "latitude": "50.55591005174",
+ "indoor": 0,
+ "country": "DE"
+ },
+ "sensordatavalues": [
+ {
+ "id": 2237722004,
+ "value_type": "temperature",
+ "value": "21.45"
+ },
+ {
+ "id": 2237722008,
+ "value_type": "pressure",
+ "value": "100205.09"
+ },
+ {
+ "id": 2237722009,
+ "value_type": "humidity",
+ "value": "58.79"
+ },
+ {
+ "value_type": "pressure_at_sealevel",
+ "value": 101974.29
+ }
+ ]
+ }
+]
\ No newline at end of file
--- /dev/null
+{
+ "software_version": "NRZ-2020-133",
+ "age": "112",
+ "sensordatavalues": [
+ {
+ "value_type": "SDS_P1",
+ "value": "10.52"
+ },
+ {
+ "value_type": "SDS_P2",
+ "value": "4.32"
+ },
+ {
+ "value_type": "BME280_temperature",
+ "value": "17.59"
+ },
+ {
+ "value_type": "BME280_pressure",
+ "value": "98680.28"
+ },
+ {
+ "value_type": "BME280_humidity",
+ "value": "57.78"
+ },
+ {
+ "value_type": "samples",
+ "value": "5070500"
+ },
+ {
+ "value_type": "min_micro",
+ "value": "28"
+ },
+ {
+ "value_type": "max_micro",
+ "value": "20091"
+ },
+ {
+ "value_type": "interval",
+ "value": "145000"
+ },
+ {
+ "value_type": "signal",
+ "value": "-81"
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+[
+ {
+ "timestamp": "2020-06-11 09:39:51",
+ "sensordatavalues": [
+ {
+ "value": "50.95",
+ "id": 1629930130,
+ "value_type": "noise_LAeq"
+ },
+ {
+ "value": "47.20",
+ "id": 1629930131,
+ "value_type": "noise_LA_min"
+ },
+ {
+ "value": "56.95",
+ "id": 1629930132,
+ "value_type": "noise_LA_max"
+ }
+ ],
+ "sampling_rate": null,
+ "location": {
+ "exact_location": 1,
+ "latitude": "50.88827895000",
+ "country": "DE",
+ "altitude": "294.9",
+ "indoor": 0,
+ "longitude": "7.87451286686",
+ "id": 25429
+ },
+ "id": 757217220,
+ "sensor": {
+ "sensor_type": {
+ "id": 29,
+ "manufacturer": "Sensor.Community",
+ "name": "Laerm"
+ },
+ "pin": "15",
+ "id": 39745
+ }
+ },
+ {
+ "timestamp": "2020-06-11 09:37:25",
+ "sensordatavalues": [
+ {
+ "value": "52.02",
+ "id": 1629881984,
+ "value_type": "noise_LAeq"
+ },
+ {
+ "value": "45.98",
+ "id": 1629881986,
+ "value_type": "noise_LA_min"
+ },
+ {
+ "value": "69.28",
+ "id": 1629881987,
+ "value_type": "noise_LA_max"
+ }
+ ],
+ "sampling_rate": null,
+ "location": {
+ "exact_location": 1,
+ "latitude": "50.88827895000",
+ "country": "DE",
+ "altitude": "294.9",
+ "indoor": 0,
+ "longitude": "7.87451286686",
+ "id": 25429
+ },
+ "id": 757194885,
+ "sensor": {
+ "sensor_type": {
+ "id": 29,
+ "manufacturer": "Sensor.Community",
+ "name": "Laerm"
+ },
+ "pin": "15",
+ "id": 39745
+ }
+ }
+]
\ No newline at end of file
--- /dev/null
+[
+ {
+ "timestamp": "2020-06-11 09:40:41",
+ "sensordatavalues": [
+ {
+ "value": "5.15",
+ "id": 1629948185,
+ "value_type": "P1"
+ },
+ {
+ "value": "2.87",
+ "id": 1629948191,
+ "value_type": "P2"
+ }
+ ],
+ "sampling_rate": null,
+ "location": {
+ "exact_location": 1,
+ "latitude": "50.55591005174",
+ "country": "DE",
+ "altitude": "151.2",
+ "indoor": 0,
+ "longitude": "8.49543571448",
+ "id": 15975
+ },
+ "id": 757225623,
+ "sensor": {
+ "sensor_type": {
+ "id": 14,
+ "manufacturer": "Nova Fitness",
+ "name": "SDS011"
+ },
+ "pin": "1",
+ "id": 28842
+ }
+ },
+ {
+ "timestamp": "2020-06-11 09:38:16",
+ "sensordatavalues": [
+ {
+ "value": "2.20",
+ "id": 1629900061,
+ "value_type": "P1"
+ },
+ {
+ "value": "2.00",
+ "id": 1629900063,
+ "value_type": "P2"
+ }
+ ],
+ "sampling_rate": null,
+ "location": {
+ "exact_location": 1,
+ "latitude": "50.55591005174",
+ "country": "DE",
+ "altitude": "151.2",
+ "indoor": 0,
+ "longitude": "8.49543571448",
+ "id": 15975
+ },
+ "id": 757203291,
+ "sensor": {
+ "sensor_type": {
+ "id": 14,
+ "manufacturer": "Nova Fitness",
+ "name": "SDS011"
+ },
+ "pin": "1",
+ "id": 28842
+ }
+ }
+]
\ No newline at end of file
<module>org.openhab.binding.livisismarthome</module>
<module>org.openhab.binding.logreader</module>
<module>org.openhab.binding.loxone</module>
- <module>org.openhab.binding.luftdateninfo</module>
<module>org.openhab.binding.lutron</module>
<module>org.openhab.binding.luxom</module>
<module>org.openhab.binding.luxtronikheatpump</module>
<module>org.openhab.binding.seneye</module>
<module>org.openhab.binding.sensebox</module>
<module>org.openhab.binding.sensibo</module>
+ <module>org.openhab.binding.sensorcommunity</module>
<module>org.openhab.binding.serial</module>
<module>org.openhab.binding.serialbutton</module>
<module>org.openhab.binding.shelly</module>