added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
32
bundles/org.openhab.binding.openuv/.classpath
Normal file
32
bundles/org.openhab.binding.openuv/.classpath
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
23
bundles/org.openhab.binding.openuv/.project
Normal file
23
bundles/org.openhab.binding.openuv/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.binding.openuv</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
13
bundles/org.openhab.binding.openuv/NOTICE
Normal file
13
bundles/org.openhab.binding.openuv/NOTICE
Normal file
@@ -0,0 +1,13 @@
|
||||
This content is produced and maintained by the openHAB project.
|
||||
|
||||
* Project home: https://www.openhab.org
|
||||
|
||||
== Declared Project Licenses
|
||||
|
||||
This program and the accompanying materials are made available under the terms
|
||||
of the Eclipse Public License 2.0 which is available at
|
||||
https://www.eclipse.org/legal/epl-2.0/.
|
||||
|
||||
== Source Code
|
||||
|
||||
https://github.com/openhab/openhab-addons
|
||||
102
bundles/org.openhab.binding.openuv/README.md
Normal file
102
bundles/org.openhab.binding.openuv/README.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# OpenUV Binding
|
||||
|
||||
This binding uses the [OpenUV Index API service](https://www.openuv.io/) for providing UV Index information for any location worldwide.
|
||||
|
||||
To use this binding, you first need to [register and get your API token](https://www.openuv.io/auth/google).
|
||||
|
||||
## Binding Installation
|
||||
|
||||
This binding can be installed via the Add-ons section of the Paper UI.
|
||||
|
||||
Go to Bindings and search for `OpenUV`. Click on install.
|
||||
|
||||
## Discovery
|
||||
|
||||
Once a bridge with the api Key has been created, Local UV Index informations can be autodiscovered based on system location.
|
||||
|
||||
## Binding Configuration
|
||||
|
||||
The binding has no configuration options, all configuration is done at Bridge and Thing level.
|
||||
|
||||
## Bridge Configuration
|
||||
|
||||
The bridge has only one configuration parameter :
|
||||
|
||||
| Parameter | Description |
|
||||
|-----------|--------------------------------------------------------------|
|
||||
| apikey | Data-platform token to access the OpenUV service. Mandatory. |
|
||||
|
||||
Will accept a Refresh command in order to reinitiate connexion (eg in case of Quota exceeded).
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
The thing has a few configuration parameters :
|
||||
|
||||
| Parameter | Description |
|
||||
|-----------|--------------------------------------------------------------|
|
||||
| location | Geo coordinates to be considered by the service. |
|
||||
| refresh | Refresh interval in minutes. Optional. |
|
||||
|
||||
For the location parameter, the following syntax is allowed (comma separated latitude, longitude and optional altitude):
|
||||
|
||||
```java
|
||||
37.8,-122.4
|
||||
37.8255,-122.456
|
||||
37.8,-122.4,177
|
||||
```
|
||||
|
||||
## Channels
|
||||
|
||||
The OpenUV Report thing that is retrieved has these channels:
|
||||
|
||||
| Channel ID | Item Type | Description |
|
||||
|--------------|---------------------|-------------------------------------------------|
|
||||
| UVIndex | Number | UV Index |
|
||||
| UVColor | Color | Color associated to given UV Index. |
|
||||
| UVMax | Number | Max UV Index for the day (at solar noon) |
|
||||
| UVMaxTime | DateTime | Max UV Index datetime (solar noon) |
|
||||
| Ozone | Number:ArealDensity | Ozone level in du (Dobson Units) from OMI data |
|
||||
| OzoneTime | DateTime | Latest OMI ozone update datetime |
|
||||
| UVTime | DateTime | UV Index datetime |
|
||||
| SafeExposure | Number:Time | Safe exposure time for Fitzpatrick Skin Types. |
|
||||
| elevation | Number:Angle | Current Sun elevation. |
|
||||
|
||||
The elevation channel will be used as an input in order to limit API queries to OpenUV. If not used,
|
||||
the binding will not consider it. When value is provided queries will only be issued if the elevation is > 0°.
|
||||
|
||||
## Examples
|
||||
|
||||
demo.things:
|
||||
|
||||
```xtend
|
||||
Bridge openuv:openuvapi:local "OpenUV Api" [ apikey="xxxxYYYxxxx" ] {
|
||||
Thing uvreport city1 "UV In My City" [ location="52.5200066,13.4049540", refresh=10 ]{
|
||||
Channels:
|
||||
Type SafeExposure : Parents [
|
||||
index=3
|
||||
]
|
||||
Type SafeExposure : Childs [
|
||||
index=2
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
demo.items:
|
||||
|
||||
```xtend
|
||||
Number UVIndex "UV Index" {channel="openuv:uvreport:local:city1:UVIndex" }
|
||||
Number UVMax "UV Max" {channel="openuv:uvreport:local:city1:UVMaxEvent" }
|
||||
Number:ArealDensity Ozone "Ozone" {channel="openuv:uvreport:local:city1:Ozone" }
|
||||
```
|
||||
|
||||
astro.items:
|
||||
|
||||
```xtend
|
||||
|
||||
Number:Angle Elevation "Elevation" {channel="astro:sun:home:position#elevation",
|
||||
channel="openuv:uvreport:local:city1:elevation" [profile="follow"] }
|
||||
|
||||
```
|
||||
|
||||
17
bundles/org.openhab.binding.openuv/pom.xml
Normal file
17
bundles/org.openhab.binding.openuv/pom.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.openuv</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: OpenUV Binding</name>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.openuv-${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-openuv" description="OpenUV Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.openuv/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openuv.internal;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link OpenUVBindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenUVBindingConstants {
|
||||
public static final String BASE_URL = "https://api.openuv.io/api/v1/uv";
|
||||
public static final String BINDING_ID = "openuv";
|
||||
public static final String LOCAL = "local";
|
||||
|
||||
public static final String LOCATION = "location";
|
||||
public static final String APIKEY = "apikey";
|
||||
|
||||
// List of Bridge Type UIDs
|
||||
public static final ThingTypeUID APIBRIDGE_THING_TYPE = new ThingTypeUID(BINDING_ID, "openuvapi");
|
||||
|
||||
// List of Things Type UIDs
|
||||
public static final ThingTypeUID LOCATION_REPORT_THING_TYPE = new ThingTypeUID(BINDING_ID, "uvreport");
|
||||
|
||||
// List of all Channel id's
|
||||
public static final String UV_INDEX = "UVIndex";
|
||||
public static final String UV_COLOR = "UVColor";
|
||||
public static final String UV_MAX = "UVMax";
|
||||
public static final String UV_MAX_TIME = "UVMaxTime";
|
||||
public static final String UV_MAX_EVENT = "UVMaxEvent";
|
||||
public static final String OZONE = "Ozone";
|
||||
public static final String OZONE_TIME = "OzoneTime";
|
||||
public static final String UV_TIME = "UVTime";
|
||||
public static final String SAFE_EXPOSURE = "SafeExposure";
|
||||
public static final String ELEVATION = "elevation";
|
||||
|
||||
public static final Set<ThingTypeUID> BRIDGE_THING_TYPES_UIDS = Collections.singleton(APIBRIDGE_THING_TYPE);
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = new HashSet<>(
|
||||
Arrays.asList(LOCATION_REPORT_THING_TYPE));
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openuv.internal;
|
||||
|
||||
import static org.openhab.binding.openuv.internal.OpenUVBindingConstants.*;
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.openuv.internal.discovery.OpenUVDiscoveryService;
|
||||
import org.openhab.binding.openuv.internal.handler.OpenUVBridgeHandler;
|
||||
import org.openhab.binding.openuv.internal.handler.OpenUVReportHandler;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.i18n.LocationProvider;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
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;
|
||||
|
||||
/**
|
||||
* The {@link OpenUVHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.openuv")
|
||||
public class OpenUVHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private final LocationProvider locationProvider;
|
||||
|
||||
@Activate
|
||||
public OpenUVHandlerFactory(@Reference LocationProvider locationProvider) {
|
||||
this.locationProvider = locationProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID) || BRIDGE_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (APIBRIDGE_THING_TYPE.equals(thingTypeUID)) {
|
||||
OpenUVBridgeHandler handler = new OpenUVBridgeHandler((Bridge) thing);
|
||||
registerOpenUVDiscoveryService(handler);
|
||||
return handler;
|
||||
} else if (LOCATION_REPORT_THING_TYPE.equals(thingTypeUID)) {
|
||||
return new OpenUVReportHandler(thing);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void registerOpenUVDiscoveryService(OpenUVBridgeHandler bridgeHandler) {
|
||||
OpenUVDiscoveryService discoveryService = new OpenUVDiscoveryService(bridgeHandler, locationProvider);
|
||||
bridgeHandler.getDiscoveryServiceRegs().put(bridgeHandler.getThing().getUID(),
|
||||
bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openuv.internal;
|
||||
|
||||
/**
|
||||
* The {@link ReportConfiguration} is the class used to match the
|
||||
* thing configuration.
|
||||
*
|
||||
* @author Gaël L"hopital - Initial contribution
|
||||
*/
|
||||
public class ReportConfiguration {
|
||||
String[] elements = null;
|
||||
|
||||
private String location;
|
||||
public Integer refresh;
|
||||
|
||||
public String getLatitude() {
|
||||
return getElement(0);
|
||||
}
|
||||
|
||||
public String getLongitude() {
|
||||
return getElement(1);
|
||||
}
|
||||
|
||||
public String getAltitude() {
|
||||
return getElement(2);
|
||||
}
|
||||
|
||||
private String getElement(int index) {
|
||||
if (elements == null) {
|
||||
elements = location.split(",");
|
||||
}
|
||||
if (index < elements.length) {
|
||||
return elements[index].trim();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openuv.internal;
|
||||
|
||||
/**
|
||||
* The {@link SafeExposureConfiguration} is the class used to match the
|
||||
* SafeExposure channel configuration.
|
||||
*
|
||||
* @author Gaël L"hopital - Initial contribution
|
||||
*/
|
||||
public class SafeExposureConfiguration {
|
||||
public int index = -1;
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openuv.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.openuv.internal.OpenUVBindingConstants.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
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.openuv.internal.handler.OpenUVBridgeHandler;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.i18n.LocationProvider;
|
||||
import org.openhab.core.library.types.PointType;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.service.component.annotations.Modified;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link OpenUVDiscoveryService} creates things based on the configured location.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenUVDiscoveryService extends AbstractDiscoveryService {
|
||||
private final Logger logger = LoggerFactory.getLogger(OpenUVDiscoveryService.class);
|
||||
|
||||
private static final int DISCOVER_TIMEOUT_SECONDS = 10;
|
||||
private static final int LOCATION_CHANGED_CHECK_INTERVAL = 60;
|
||||
|
||||
private final LocationProvider locationProvider;
|
||||
private final OpenUVBridgeHandler bridgeHandler;
|
||||
private @Nullable ScheduledFuture<?> discoveryJob;
|
||||
private @Nullable PointType previousLocation;
|
||||
|
||||
/**
|
||||
* Creates a OpenUVDiscoveryService with enabled autostart.
|
||||
*/
|
||||
public OpenUVDiscoveryService(OpenUVBridgeHandler bridgeHandler, LocationProvider locationProvider) {
|
||||
super(SUPPORTED_THING_TYPES_UIDS, DISCOVER_TIMEOUT_SECONDS, true);
|
||||
this.locationProvider = locationProvider;
|
||||
this.bridgeHandler = bridgeHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void activate(@Nullable Map<String, @Nullable Object> configProperties) {
|
||||
super.activate(configProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modified
|
||||
protected void modified(@Nullable Map<String, @Nullable Object> configProperties) {
|
||||
super.modified(configProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
logger.debug("Starting OpenUV discovery scan");
|
||||
PointType location = locationProvider.getLocation();
|
||||
if (location == null) {
|
||||
logger.debug("LocationProvider.getLocation() is not set -> Will not provide any discovery results");
|
||||
return;
|
||||
}
|
||||
createResults(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startBackgroundDiscovery() {
|
||||
if (discoveryJob == null) {
|
||||
discoveryJob = scheduler.scheduleWithFixedDelay(() -> {
|
||||
PointType currentLocation = locationProvider.getLocation();
|
||||
if (currentLocation != null && !Objects.equals(currentLocation, previousLocation)) {
|
||||
logger.debug("Location has been changed from {} to {}: Creating new discovery results",
|
||||
previousLocation, currentLocation);
|
||||
createResults(currentLocation);
|
||||
previousLocation = currentLocation;
|
||||
}
|
||||
}, 0, LOCATION_CHANGED_CHECK_INTERVAL, TimeUnit.SECONDS);
|
||||
logger.debug("Scheduled OpenUV-changed job every {} seconds", LOCATION_CHANGED_CHECK_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
public void createResults(PointType location) {
|
||||
ThingUID bridgeUID = bridgeHandler.getThing().getUID();
|
||||
ThingUID localOpenUVThing = new ThingUID(LOCATION_REPORT_THING_TYPE, bridgeUID, LOCAL);
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
properties.put(LOCATION, location.toString());
|
||||
thingDiscovered(DiscoveryResultBuilder.create(localOpenUVThing).withLabel("Local UV Information")
|
||||
.withProperties(properties).withRepresentationProperty(location.toString()).withBridge(bridgeUID)
|
||||
.build());
|
||||
}
|
||||
|
||||
@SuppressWarnings("null")
|
||||
@Override
|
||||
protected void stopBackgroundDiscovery() {
|
||||
logger.debug("Stopping OpenUV background discovery");
|
||||
if (discoveryJob != null && !discoveryJob.isCancelled()) {
|
||||
if (discoveryJob.cancel(true)) {
|
||||
discoveryJob = null;
|
||||
logger.debug("Stopped OpenUV background discovery");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openuv.internal.handler;
|
||||
|
||||
import static org.openhab.binding.openuv.internal.OpenUVBindingConstants.BASE_URL;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.openuv.internal.OpenUVBindingConstants;
|
||||
import org.openhab.binding.openuv.internal.json.OpenUVResponse;
|
||||
import org.openhab.binding.openuv.internal.json.OpenUVResult;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.io.net.http.HttpUtil;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.osgi.framework.ServiceRegistration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.FieldNamingPolicy;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
|
||||
/**
|
||||
* {@link OpenUVBridgeHandler} is the handler for OpenUV API and connects it
|
||||
* to the webservice.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenUVBridgeHandler extends BaseBridgeHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(OpenUVBridgeHandler.class);
|
||||
private static final String ERROR_QUOTA_EXCEEDED = "Daily API quota exceeded";
|
||||
private static final String ERROR_WRONG_KEY = "User with API Key not found";
|
||||
|
||||
private static final int REQUEST_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(30);
|
||||
|
||||
private final Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(DecimalType.class,
|
||||
(JsonDeserializer<DecimalType>) (json, type, jsonDeserializationContext) -> DecimalType
|
||||
.valueOf(json.getAsJsonPrimitive().getAsString()))
|
||||
.registerTypeAdapter(ZonedDateTime.class,
|
||||
(JsonDeserializer<ZonedDateTime>) (json, type, jsonDeserializationContext) -> ZonedDateTime
|
||||
.parse(json.getAsJsonPrimitive().getAsString()))
|
||||
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
|
||||
|
||||
private Map<ThingUID, @Nullable ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
|
||||
private final Properties header = new Properties();
|
||||
|
||||
public OpenUVBridgeHandler(Bridge bridge) {
|
||||
super(bridge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing OpenUV API bridge handler.");
|
||||
Configuration config = getThing().getConfiguration();
|
||||
String apiKey = (String) config.get(OpenUVBindingConstants.APIKEY);
|
||||
if (StringUtils.trimToNull(apiKey) == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Parameter 'apikey' must be configured.");
|
||||
} else {
|
||||
header.put("x-access-token", apiKey);
|
||||
initiateConnexion();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command instanceof RefreshType) {
|
||||
initiateConnexion();
|
||||
} else {
|
||||
logger.debug("The OpenUV bridge only handles Refresh command and not '{}'", command);
|
||||
}
|
||||
}
|
||||
|
||||
private void initiateConnexion() {
|
||||
// Check if the provided api key is valid for use with the OpenUV service
|
||||
getUVData("0", "0", null);
|
||||
}
|
||||
|
||||
public Map<ThingUID, @Nullable ServiceRegistration<?>> getDiscoveryServiceRegs() {
|
||||
return discoveryServiceRegs;
|
||||
}
|
||||
|
||||
public void setDiscoveryServiceRegs(Map<ThingUID, @Nullable ServiceRegistration<?>> discoveryServiceRegs) {
|
||||
this.discoveryServiceRegs = discoveryServiceRegs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRemoval() {
|
||||
// removes the old registration service associated to the bridge, if existing
|
||||
ServiceRegistration<?> dis = getDiscoveryServiceRegs().get(getThing().getUID());
|
||||
if (dis != null) {
|
||||
dis.unregister();
|
||||
}
|
||||
super.handleRemoval();
|
||||
}
|
||||
|
||||
public @Nullable OpenUVResult getUVData(String latitude, String longitude, @Nullable String altitude) {
|
||||
StringBuilder urlBuilder = new StringBuilder(BASE_URL).append("?lat=").append(latitude).append("&lng=")
|
||||
.append(longitude);
|
||||
|
||||
if (altitude != null) {
|
||||
urlBuilder.append("&alt=").append(altitude);
|
||||
}
|
||||
String errorMessage = null;
|
||||
try {
|
||||
String jsonData = HttpUtil.executeUrl("GET", urlBuilder.toString(), header, null, null, REQUEST_TIMEOUT);
|
||||
OpenUVResponse uvResponse = gson.fromJson(jsonData, OpenUVResponse.class);
|
||||
if (uvResponse.getError() == null) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
return uvResponse.getResult();
|
||||
} else {
|
||||
errorMessage = uvResponse.getError();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
errorMessage = e.getMessage();
|
||||
}
|
||||
|
||||
if (errorMessage.startsWith(ERROR_QUOTA_EXCEEDED)) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, errorMessage);
|
||||
LocalDate today = LocalDate.now();
|
||||
LocalDate tomorrow = today.plusDays(1);
|
||||
LocalDateTime tomorrowMidnight = tomorrow.atStartOfDay().plusMinutes(2);
|
||||
|
||||
logger.warn("Quota Exceeded, going OFFLINE for today, will retry at : {} ", tomorrowMidnight);
|
||||
scheduler.schedule(this::initiateConnexion,
|
||||
Duration.between(LocalDateTime.now(), tomorrowMidnight).toMinutes(), TimeUnit.MINUTES);
|
||||
|
||||
} else if (errorMessage.startsWith(ERROR_WRONG_KEY)) {
|
||||
logger.error("Error occured during API query : {}", errorMessage);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, errorMessage);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openuv.internal.handler;
|
||||
|
||||
import static org.openhab.binding.openuv.internal.OpenUVBindingConstants.*;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.measure.quantity.Angle;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.openuv.internal.ReportConfiguration;
|
||||
import org.openhab.binding.openuv.internal.SafeExposureConfiguration;
|
||||
import org.openhab.binding.openuv.internal.json.OpenUVResult;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.HSBType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.SmartHomeUnits;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
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.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link OpenUVReportHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenUVReportHandler extends BaseThingHandler {
|
||||
private static final int DEFAULT_REFRESH_PERIOD = 30;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(OpenUVReportHandler.class);
|
||||
|
||||
private @NonNullByDefault({}) OpenUVBridgeHandler bridgeHandler;
|
||||
private @NonNullByDefault({}) ScheduledFuture<?> refreshJob;
|
||||
private @NonNullByDefault({}) ScheduledFuture<?> uvMaxJob;
|
||||
private boolean suspendUpdates = false;
|
||||
|
||||
public OpenUVReportHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing OpenUV handler.");
|
||||
|
||||
ReportConfiguration config = getConfigAs(ReportConfiguration.class);
|
||||
|
||||
if (config.refresh != null && config.refresh < 3) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Parameter 'refresh' must be higher than 3 minutes to stay in free API plan");
|
||||
} else {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Invalid bridge");
|
||||
} else {
|
||||
bridgeHandler = (OpenUVBridgeHandler) bridge.getHandler();
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
startAutomaticRefresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the job screening UV Max reached
|
||||
*
|
||||
* @param openUVData
|
||||
*/
|
||||
private void scheduleUVMaxEvent(OpenUVResult openUVData) {
|
||||
if ((uvMaxJob == null || uvMaxJob.isCancelled())) {
|
||||
State uvMaxTime = openUVData.getUVMaxTime();
|
||||
if (uvMaxTime != UnDefType.NULL) {
|
||||
ZonedDateTime uvMaxZdt = ((DateTimeType) uvMaxTime).getZonedDateTime();
|
||||
long timeDiff = ChronoUnit.MINUTES.between(ZonedDateTime.now(ZoneId.systemDefault()), uvMaxZdt);
|
||||
if (timeDiff > 0) {
|
||||
logger.debug("Scheduling {} in {} minutes", UV_MAX_EVENT, timeDiff);
|
||||
uvMaxJob = scheduler.schedule(() -> {
|
||||
triggerChannel(UV_MAX_EVENT);
|
||||
uvMaxJob = null;
|
||||
}, timeDiff, TimeUnit.MINUTES);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the job refreshing the data
|
||||
*/
|
||||
private void startAutomaticRefresh() {
|
||||
if (refreshJob == null || refreshJob.isCancelled()) {
|
||||
ReportConfiguration config = getConfigAs(ReportConfiguration.class);
|
||||
int delay = (config.refresh != null) ? config.refresh.intValue() : DEFAULT_REFRESH_PERIOD;
|
||||
refreshJob = scheduler.scheduleWithFixedDelay(() -> {
|
||||
if (!suspendUpdates) {
|
||||
updateChannels(config);
|
||||
}
|
||||
}, 0, delay, TimeUnit.MINUTES);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateChannels(ReportConfiguration config) {
|
||||
ThingStatusInfo bridgeStatusInfo = bridgeHandler.getThing().getStatusInfo();
|
||||
if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
|
||||
OpenUVResult openUVData = bridgeHandler.getUVData(config.getLatitude(), config.getLongitude(),
|
||||
config.getAltitude());
|
||||
if (openUVData != null) {
|
||||
scheduleUVMaxEvent(openUVData);
|
||||
getThing().getChannels().forEach(channel -> {
|
||||
updateChannel(channel.getUID(), openUVData);
|
||||
});
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, bridgeStatusInfo.getStatusDetail(),
|
||||
bridgeStatusInfo.getDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
logger.debug("Disposing the OpenUV handler.");
|
||||
|
||||
if (refreshJob != null && !refreshJob.isCancelled()) {
|
||||
refreshJob.cancel(true);
|
||||
refreshJob = null;
|
||||
}
|
||||
|
||||
if (uvMaxJob != null && !uvMaxJob.isCancelled()) {
|
||||
uvMaxJob.cancel(true);
|
||||
uvMaxJob = null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command instanceof RefreshType) {
|
||||
scheduler.execute(() -> {
|
||||
ReportConfiguration config = getConfigAs(ReportConfiguration.class);
|
||||
updateChannels(config);
|
||||
});
|
||||
} else if (ELEVATION.equals(channelUID.getId()) && command instanceof QuantityType) {
|
||||
QuantityType<?> qtty = (QuantityType<?>) command;
|
||||
if ("°".equals(qtty.getUnit().toString())) {
|
||||
suspendUpdates = ((QuantityType<Angle>) qtty).doubleValue() < 0;
|
||||
} else {
|
||||
logger.info("The OpenUV Report handles Sun Elevation of Number:Angle type, {} does not fit.", command);
|
||||
}
|
||||
} else {
|
||||
logger.info("The OpenUV Report Thing handles Refresh or Sun Elevation command and not '{}'", command);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the channel from the last OpenUV data retrieved
|
||||
*
|
||||
* @param channelUID the id identifying the channel to be updated
|
||||
* @param openUVData
|
||||
*
|
||||
*/
|
||||
private void updateChannel(ChannelUID channelUID, OpenUVResult openUVData) {
|
||||
Channel channel = getThing().getChannel(channelUID.getId());
|
||||
if (channel != null && isLinked(channelUID)) {
|
||||
ChannelTypeUID channelTypeUID = channel.getChannelTypeUID();
|
||||
if (channelTypeUID != null) {
|
||||
switch (channelTypeUID.getId()) {
|
||||
case UV_INDEX:
|
||||
updateState(channelUID, openUVData.getUv());
|
||||
break;
|
||||
case UV_COLOR:
|
||||
updateState(channelUID, getAsHSB(openUVData.getUv().intValue()));
|
||||
break;
|
||||
case UV_MAX:
|
||||
updateState(channelUID, openUVData.getUvMax());
|
||||
break;
|
||||
case OZONE:
|
||||
updateState(channelUID, new QuantityType<>(openUVData.getOzone(), SmartHomeUnits.DOBSON_UNIT));
|
||||
break;
|
||||
case OZONE_TIME:
|
||||
updateState(channelUID, openUVData.getOzoneTime());
|
||||
break;
|
||||
case UV_MAX_TIME:
|
||||
updateState(channelUID, openUVData.getUVMaxTime());
|
||||
break;
|
||||
case UV_TIME:
|
||||
updateState(channelUID, openUVData.getUVTime());
|
||||
break;
|
||||
case SAFE_EXPOSURE:
|
||||
SafeExposureConfiguration configuration = channel.getConfiguration()
|
||||
.as(SafeExposureConfiguration.class);
|
||||
if (configuration.index != -1) {
|
||||
updateState(channelUID,
|
||||
openUVData.getSafeExposureTime().getSafeExposure(configuration.index));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private State getAsHSB(int uv) {
|
||||
if (uv >= 11) {
|
||||
return HSBType.fromRGB(106, 27, 154);
|
||||
} else if (uv >= 8) {
|
||||
return HSBType.fromRGB(183, 28, 28);
|
||||
} else if (uv >= 6) {
|
||||
return HSBType.fromRGB(239, 108, 0);
|
||||
} else if (uv >= 3) {
|
||||
return HSBType.fromRGB(249, 168, 37);
|
||||
} else {
|
||||
return HSBType.fromRGB(85, 139, 47);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openuv.internal.json;
|
||||
|
||||
/**
|
||||
* The {@link OpenUVResponse} is the Java class used to map the JSON
|
||||
* response to the OpenUV request.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
public class OpenUVResponse {
|
||||
private String error;
|
||||
private OpenUVResult result;
|
||||
|
||||
public OpenUVResult getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openuv.internal.json;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
/**
|
||||
* The {@link OpenUVResult} is responsible for storing
|
||||
* the "result" node from the OpenUV JSON response
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenUVResult {
|
||||
private final ZonedDateTime DEFAULT_ZDT = ZonedDateTime.of(LocalDateTime.MIN, ZoneId.systemDefault());
|
||||
private DecimalType uv = new DecimalType(0);
|
||||
private ZonedDateTime uvTime = DEFAULT_ZDT;
|
||||
private DecimalType uvMax = new DecimalType(0);
|
||||
private ZonedDateTime uvMaxTime = DEFAULT_ZDT;
|
||||
private DecimalType ozone = new DecimalType(0);
|
||||
private ZonedDateTime ozoneTime = DEFAULT_ZDT;
|
||||
private SafeExposureTime safeExposureTime = new SafeExposureTime();
|
||||
|
||||
public DecimalType getUv() {
|
||||
return uv;
|
||||
}
|
||||
|
||||
public DecimalType getUvMax() {
|
||||
return uvMax;
|
||||
}
|
||||
|
||||
public DecimalType getOzone() {
|
||||
return ozone;
|
||||
}
|
||||
|
||||
public State getUVTime() {
|
||||
return uvTime != DEFAULT_ZDT ? new DateTimeType(uvTime.withZoneSameInstant(ZoneId.systemDefault()))
|
||||
: UnDefType.NULL;
|
||||
}
|
||||
|
||||
public State getUVMaxTime() {
|
||||
return uvMaxTime != DEFAULT_ZDT ? new DateTimeType(uvMaxTime.withZoneSameInstant(ZoneId.systemDefault()))
|
||||
: UnDefType.NULL;
|
||||
}
|
||||
|
||||
public State getOzoneTime() {
|
||||
return ozoneTime != DEFAULT_ZDT ? new DateTimeType(ozoneTime.withZoneSameInstant(ZoneId.systemDefault()))
|
||||
: UnDefType.NULL;
|
||||
}
|
||||
|
||||
public SafeExposureTime getSafeExposureTime() {
|
||||
return safeExposureTime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openuv.internal.json;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.SmartHomeUnits;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
/**
|
||||
* Wrapper type around values reported by OpenUV safe exposure time.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
public class SafeExposureTime {
|
||||
public @Nullable BigInteger st1;
|
||||
public @Nullable BigInteger st2;
|
||||
public @Nullable BigInteger st3;
|
||||
public @Nullable BigInteger st4;
|
||||
public @Nullable BigInteger st5;
|
||||
public @Nullable BigInteger st6;
|
||||
|
||||
public State getSafeExposure(int index) {
|
||||
BigInteger result;
|
||||
switch (index) {
|
||||
case 1:
|
||||
result = st1;
|
||||
break;
|
||||
case 2:
|
||||
result = st2;
|
||||
break;
|
||||
case 3:
|
||||
result = st3;
|
||||
break;
|
||||
case 4:
|
||||
result = st4;
|
||||
break;
|
||||
case 5:
|
||||
result = st5;
|
||||
break;
|
||||
case 6:
|
||||
result = st6;
|
||||
break;
|
||||
default:
|
||||
result = null;
|
||||
}
|
||||
return (result != null) ? new QuantityType<>(result, SmartHomeUnits.MINUTE) : UnDefType.NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="openuv" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>OpenUV Binding</name>
|
||||
<description>Global Real-Time UV Index Forecast API</description>
|
||||
<author>Gaël L'hopital</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,150 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="openuv"
|
||||
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">
|
||||
|
||||
<!-- OpenUV Bridge -->
|
||||
<bridge-type id="openuvapi">
|
||||
<label>Open UV API</label>
|
||||
<description>
|
||||
Bridge to the OpenUV Project API. In order to receive the data, you must register an account on
|
||||
https://www.openuv.io/auth/google and get your API token.
|
||||
</description>
|
||||
|
||||
<config-description>
|
||||
<parameter name="apikey" type="text" required="true">
|
||||
<context>password</context>
|
||||
<label>API Key</label>
|
||||
<description>Data-platform token to access the OpenUV API service</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</bridge-type>
|
||||
|
||||
<!-- OpenUV Report Thing -->
|
||||
<thing-type id="uvreport">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="openuvapi"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>UV Report</label>
|
||||
<description>
|
||||
Provides various UV data from the OpenUV Project for a given location.
|
||||
</description>
|
||||
|
||||
<channels>
|
||||
<channel id="UVIndex" typeId="UVIndex"/>
|
||||
<channel id="UVColor" typeId="UVColor"/>
|
||||
<channel id="UVMax" typeId="UVMax"/>
|
||||
<channel id="UVMaxTime" typeId="UVMaxTime"/>
|
||||
<channel id="UVMaxEvent" typeId="UVMaxEvent"/>
|
||||
<channel id="Ozone" typeId="Ozone"/>
|
||||
<channel id="OzoneTime" typeId="OzoneTime"/>
|
||||
<channel id="UVTime" typeId="UVTime"/>
|
||||
<channel id="SafeExposure" typeId="SafeExposure"/>
|
||||
<channel id="elevation" typeId="elevation"/>
|
||||
</channels>
|
||||
|
||||
<representation-property>location</representation-property>
|
||||
|
||||
<config-description>
|
||||
<parameter name="refresh" type="integer" min="3">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Specifies the refresh interval in minutes.</description>
|
||||
<default>10</default>
|
||||
</parameter>
|
||||
<parameter name="location" type="text" required="true">
|
||||
<label>Location</label>
|
||||
<context>location</context>
|
||||
<description>Your geo coordinates separated with comma (e.g. "37.8,-122.4,177").</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="UVIndex">
|
||||
<item-type>Number</item-type>
|
||||
<label>UV Index</label>
|
||||
<description>UV Index</description>
|
||||
<state readOnly="true" pattern="%.2f/16" min="0" max="16"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="UVMax" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>UV Max</label>
|
||||
<description>Max UV Index for the day (at solar noon)</description>
|
||||
<state readOnly="true" pattern="%.2f/16" min="0" max="16"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="Ozone">
|
||||
<item-type>Number:ArealDensity</item-type>
|
||||
<label>Ozone</label>
|
||||
<description>Ozone level from OMI data</description>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="OzoneTime" advanced="true">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Ozone Observation Time</label>
|
||||
<description>Latest OMI ozone update time</description>
|
||||
<state readOnly="true" pattern="%1$tF %1$tR"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="UVMaxTime" advanced="true">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>UV Max Time</label>
|
||||
<description>Max UV Index time (solar noon)</description>
|
||||
<state readOnly="true" pattern="%1$tF %1$tR"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="UVTime" advanced="true">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>UV Time</label>
|
||||
<description>UV Index time</description>
|
||||
<state readOnly="true" pattern="%1$tF %1$tR"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="UVColor" advanced="true">
|
||||
<item-type>Color</item-type>
|
||||
<label>UV Color</label>
|
||||
<description>Color associated to given UV Index.</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="SafeExposure" advanced="false">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Safe Exposure</label>
|
||||
<description>Safe exposure time for Fitzpatrick Skin Types</description>
|
||||
<state readOnly="true" pattern="%d %unit%"/>
|
||||
<config-description>
|
||||
<parameter name="index" type="integer">
|
||||
<label>Skin Type</label>
|
||||
<description>Fitzpatrick Skin Type.</description>
|
||||
<options>
|
||||
<option value="1">I – White</option>
|
||||
<option value="2">II – White</option>
|
||||
<option value="3">III – Light brown</option>
|
||||
<option value="4">IV – Moderate brown</option>
|
||||
<option value="5">V – Dark brown</option>
|
||||
<option value="6">VI – Black</option>
|
||||
</options>
|
||||
<default>2</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="elevation">
|
||||
<item-type>Number:Angle</item-type>
|
||||
<label>Elevation</label>
|
||||
<description>The elevation of the sun</description>
|
||||
<state pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<!-- UV Max Event Channel Type -->
|
||||
<channel-type id="UVMaxEvent">
|
||||
<kind>trigger</kind>
|
||||
<label>UV Max Event</label>
|
||||
<description>Triggers when current UV Index reaches maximum of the day</description>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user