added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.weatherunderground-${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-weatherunderground" description="WeatherUnderground Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.weatherunderground/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -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.weatherunderground.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 WeatherUndergroundBinding} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
* @author Theo Giovanna - Added a bridge for the API key
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WeatherUndergroundBindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "weatherunderground";
|
||||
|
||||
public static final String LOCAL = "local";
|
||||
|
||||
// List all Thing Type UIDs, related to the WeatherUnderground Binding
|
||||
public static final ThingTypeUID THING_TYPE_WEATHER = new ThingTypeUID(BINDING_ID, "weather");
|
||||
public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge");
|
||||
|
||||
public static final Set<ThingTypeUID> BRIDGE_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_BRIDGE);
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = new HashSet<>(Arrays.asList(THING_TYPE_WEATHER));
|
||||
|
||||
// Channel configuration Properties
|
||||
public static final String PROPERTY_SOURCE_UNIT = "SourceUnit";
|
||||
|
||||
// Bridge config properties
|
||||
public static final String APIKEY = "apikey";
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* 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.weatherunderground.internal;
|
||||
|
||||
import static org.openhab.binding.weatherunderground.internal.WeatherUndergroundBindingConstants.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.weatherunderground.internal.discovery.WeatherUndergroundDiscoveryService;
|
||||
import org.openhab.binding.weatherunderground.internal.handler.WeatherUndergroundBridgeHandler;
|
||||
import org.openhab.binding.weatherunderground.internal.handler.WeatherUndergroundHandler;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
import org.openhab.core.i18n.LocationProvider;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.i18n.UnitProvider;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.framework.ServiceRegistration;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* The {@link WeatherUndergroundHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
* @author Theo Giovanna - Added a bridge for the API key
|
||||
* @author Laurent Garnier - Registration of the discovery service updated
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.weatherunderground")
|
||||
public class WeatherUndergroundHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Stream
|
||||
.of(BRIDGE_THING_TYPES_UIDS, WeatherUndergroundBindingConstants.SUPPORTED_THING_TYPES_UIDS)
|
||||
.flatMap(x -> x.stream()).collect(Collectors.toSet());
|
||||
|
||||
private final Map<ThingUID, @Nullable ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
|
||||
|
||||
private final LocaleProvider localeProvider;
|
||||
private final LocationProvider locationProvider;
|
||||
private final UnitProvider unitProvider;
|
||||
private final TimeZoneProvider timeZoneProvider;
|
||||
|
||||
@Activate
|
||||
public WeatherUndergroundHandlerFactory(final @Reference LocaleProvider localeProvider,
|
||||
final @Reference LocationProvider locationProvider, final @Reference UnitProvider unitProvider,
|
||||
final @Reference TimeZoneProvider timeZoneProvider) {
|
||||
this.localeProvider = localeProvider;
|
||||
this.locationProvider = locationProvider;
|
||||
this.unitProvider = unitProvider;
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (thingTypeUID.equals(THING_TYPE_WEATHER)) {
|
||||
return new WeatherUndergroundHandler(thing, localeProvider, unitProvider, timeZoneProvider);
|
||||
}
|
||||
|
||||
if (thingTypeUID.equals(THING_TYPE_BRIDGE)) {
|
||||
WeatherUndergroundBridgeHandler handler = new WeatherUndergroundBridgeHandler((Bridge) thing);
|
||||
registerDiscoveryService(handler.getThing().getUID());
|
||||
return handler;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeHandler(ThingHandler thingHandler) {
|
||||
if (thingHandler instanceof WeatherUndergroundBridgeHandler) {
|
||||
unregisterDiscoveryService(thingHandler.getThing().getUID());
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void registerDiscoveryService(ThingUID bridgeUID) {
|
||||
WeatherUndergroundDiscoveryService discoveryService = new WeatherUndergroundDiscoveryService(bridgeUID,
|
||||
localeProvider, locationProvider);
|
||||
discoveryService.activate(null);
|
||||
discoveryServiceRegs.put(bridgeUID,
|
||||
bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
|
||||
}
|
||||
|
||||
private synchronized void unregisterDiscoveryService(ThingUID bridgeUID) {
|
||||
ServiceRegistration<?> serviceReg = discoveryServiceRegs.remove(bridgeUID);
|
||||
if (serviceReg != null) {
|
||||
WeatherUndergroundDiscoveryService service = (WeatherUndergroundDiscoveryService) bundleContext
|
||||
.getService(serviceReg.getReference());
|
||||
serviceReg.unregister();
|
||||
if (service != null) {
|
||||
service.deactivate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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.weatherunderground.internal.config;
|
||||
|
||||
/**
|
||||
* The {@link WeatherUndergroundConfiguration} is the class used to match the
|
||||
* thing configuration.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
public class WeatherUndergroundConfiguration {
|
||||
|
||||
public static final String LOCATION = "location";
|
||||
public static final String LANGUAGE = "language";
|
||||
|
||||
public String location;
|
||||
public String language;
|
||||
public Integer refresh;
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* 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.weatherunderground.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.weatherunderground.internal.WeatherUndergroundBindingConstants.*;
|
||||
import static org.openhab.binding.weatherunderground.internal.config.WeatherUndergroundConfiguration.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.weatherunderground.internal.handler.WeatherUndergroundHandler;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
import org.openhab.core.i18n.LocationProvider;
|
||||
import org.openhab.core.library.types.PointType;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link WeatherUndergroundDiscoveryService} creates things based on the configured location.
|
||||
*
|
||||
* @author Laurent Garnier - Initial Contribution
|
||||
* @author Laurent Garnier - Consider locale (language) when discovering a new thing
|
||||
*/
|
||||
public class WeatherUndergroundDiscoveryService extends AbstractDiscoveryService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(WeatherUndergroundDiscoveryService.class);
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_WEATHER);
|
||||
private static final int DISCOVER_TIMEOUT_SECONDS = 2;
|
||||
private static final int LOCATION_CHANGED_CHECK_INTERVAL = 60;
|
||||
|
||||
private final LocaleProvider localeProvider;
|
||||
private final LocationProvider locationProvider;
|
||||
private ScheduledFuture<?> discoveryJob;
|
||||
private PointType previousLocation;
|
||||
private String previousLanguage;
|
||||
private String previousCountry;
|
||||
|
||||
private final ThingUID bridgeUID;
|
||||
|
||||
/**
|
||||
* Creates a WeatherUndergroundDiscoveryService with enabled autostart.
|
||||
*/
|
||||
|
||||
public WeatherUndergroundDiscoveryService(ThingUID bridgeUID, LocaleProvider localeProvider,
|
||||
LocationProvider locationProvider) {
|
||||
super(SUPPORTED_THING_TYPES, DISCOVER_TIMEOUT_SECONDS, true);
|
||||
this.bridgeUID = bridgeUID;
|
||||
this.localeProvider = localeProvider;
|
||||
this.locationProvider = locationProvider;
|
||||
}
|
||||
|
||||
/* We override this method to allow a call from the thing handler factory */
|
||||
@Override
|
||||
public void activate(@Nullable Map<@NonNull String, @Nullable Object> configProperties) {
|
||||
super.activate(configProperties);
|
||||
}
|
||||
|
||||
/* We override this method to allow a call from the thing handler factory */
|
||||
@Override
|
||||
public void deactivate() {
|
||||
super.deactivate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
logger.debug("Starting Weather Underground 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, localeProvider.getLocale());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startBackgroundDiscovery() {
|
||||
logger.debug("Starting Weather Underground device background discovery");
|
||||
if (discoveryJob == null || discoveryJob.isCancelled()) {
|
||||
discoveryJob = scheduler.scheduleWithFixedDelay(() -> {
|
||||
PointType currentLocation = locationProvider.getLocation();
|
||||
String currentLanguage = localeProvider.getLocale().getLanguage();
|
||||
String currentCountry = localeProvider.getLocale().getCountry();
|
||||
if (currentLocation != null) {
|
||||
boolean update = false;
|
||||
if (!Objects.equals(currentLocation, previousLocation)) {
|
||||
logger.debug("Location has been changed from {} to {}: Creating new discovery result",
|
||||
previousLocation, currentLocation);
|
||||
update = true;
|
||||
} else if (!Objects.equals(currentLanguage, previousLanguage)) {
|
||||
logger.debug("Language has been changed from {} to {}: Creating new discovery result",
|
||||
previousLanguage, currentLanguage);
|
||||
update = true;
|
||||
} else if (!Objects.equals(currentCountry, previousCountry)) {
|
||||
logger.debug("Country has been changed from {} to {}: Creating new discovery result",
|
||||
previousCountry, currentCountry);
|
||||
update = true;
|
||||
}
|
||||
if (update) {
|
||||
createResults(currentLocation, localeProvider.getLocale());
|
||||
previousLocation = currentLocation;
|
||||
previousLanguage = currentLanguage;
|
||||
previousCountry = currentCountry;
|
||||
}
|
||||
}
|
||||
}, 0, LOCATION_CHANGED_CHECK_INTERVAL, TimeUnit.SECONDS);
|
||||
logger.debug("Scheduled Weather Underground location-changed job every {} seconds",
|
||||
LOCATION_CHANGED_CHECK_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopBackgroundDiscovery() {
|
||||
logger.debug("Stopping Weather Underground device background discovery");
|
||||
if (discoveryJob != null && !discoveryJob.isCancelled()) {
|
||||
discoveryJob.cancel(true);
|
||||
discoveryJob = null;
|
||||
logger.debug("Stopped Weather Underground device background discovery");
|
||||
}
|
||||
}
|
||||
|
||||
private void createResults(PointType location, Locale locale) {
|
||||
ThingUID localWeatherThing = new ThingUID(THING_TYPE_WEATHER, bridgeUID, LOCAL);
|
||||
Map<String, Object> properties = new HashMap<>(3);
|
||||
properties.put(LOCATION, String.format("%s,%s", location.getLatitude(), location.getLongitude()));
|
||||
String lang = WeatherUndergroundHandler.getCodeFromLanguage(locale);
|
||||
if (!lang.isEmpty()) {
|
||||
properties.put(LANGUAGE, lang);
|
||||
}
|
||||
thingDiscovered(DiscoveryResultBuilder.create(localWeatherThing).withLabel("Local Weather")
|
||||
.withProperties(properties).withBridge(bridgeUID).build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/**
|
||||
* 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.weatherunderground.internal.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
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.weatherunderground.internal.WeatherUndergroundBindingConstants;
|
||||
import org.openhab.binding.weatherunderground.internal.json.WeatherUndergroundJsonData;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.io.net.http.HttpUtil;
|
||||
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.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
/**
|
||||
* The {@link WeatherUndergroundBridgeHandler} is responsible for handling the
|
||||
* bridge things created to use the Weather Underground Service. This way, the
|
||||
* API key may be entered only once.
|
||||
*
|
||||
* @author Theo Giovanna - Initial Contribution
|
||||
* @author Laurent Garnier - refactor bridge/thing handling
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WeatherUndergroundBridgeHandler extends BaseBridgeHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(WeatherUndergroundBridgeHandler.class);
|
||||
private final Gson gson;
|
||||
private static final String URL = "http://api.wunderground.com/api/%APIKEY%/";
|
||||
public static final int FETCH_TIMEOUT_MS = 30000;
|
||||
|
||||
@Nullable
|
||||
private ScheduledFuture<?> controlApiKeyJob;
|
||||
|
||||
private String apikey = "";
|
||||
|
||||
public WeatherUndergroundBridgeHandler(Bridge bridge) {
|
||||
super(bridge);
|
||||
gson = new Gson();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing weatherunderground bridge handler.");
|
||||
Configuration config = getThing().getConfiguration();
|
||||
|
||||
// Check if an api key has been provided during the bridge creation
|
||||
Object configApiKey = config.get(WeatherUndergroundBindingConstants.APIKEY);
|
||||
if (configApiKey == null || !(configApiKey instanceof String) || ((String) configApiKey).trim().isEmpty()) {
|
||||
logger.debug("Setting thing '{}' to OFFLINE: Parameter 'apikey' must be configured.", getThing().getUID());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.conf-error-missing-apikey");
|
||||
} else {
|
||||
apikey = ((String) configApiKey).trim();
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
startControlApiKeyJob();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the job controlling the API key
|
||||
*/
|
||||
private void startControlApiKeyJob() {
|
||||
if (controlApiKeyJob == null || controlApiKeyJob.isCancelled()) {
|
||||
Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
WeatherUndergroundJsonData result = null;
|
||||
String errorDetail = null;
|
||||
String error = null;
|
||||
String statusDescr = null;
|
||||
boolean resultOk = false;
|
||||
|
||||
// Check if the provided api key is valid for use with the weatherunderground service
|
||||
try {
|
||||
String urlStr = URL.replace("%APIKEY%", getApikey());
|
||||
// Run the HTTP request and get the JSON response from Weather Underground
|
||||
String response = null;
|
||||
try {
|
||||
response = HttpUtil.executeUrl("GET", urlStr, FETCH_TIMEOUT_MS);
|
||||
logger.debug("apiResponse = {}", response);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Catch Illegal character in path at index XX: http://api.wunderground.com/...
|
||||
error = "Error creating URI";
|
||||
errorDetail = e.getMessage();
|
||||
statusDescr = "@text/offline.uri-error";
|
||||
}
|
||||
// Map the JSON response to an object
|
||||
result = gson.fromJson(response, WeatherUndergroundJsonData.class);
|
||||
if (result.getResponse() == null) {
|
||||
error = "Error in Weather Underground response";
|
||||
errorDetail = "missing response sub-object";
|
||||
statusDescr = "@text/offline.comm-error-response";
|
||||
} else if (result.getResponse().getErrorDescription() != null) {
|
||||
if ("keynotfound".equals(result.getResponse().getErrorType())) {
|
||||
error = "API key has to be fixed";
|
||||
errorDetail = result.getResponse().getErrorDescription();
|
||||
statusDescr = "@text/offline.comm-error-invalid-api-key";
|
||||
} else if ("invalidquery".equals(result.getResponse().getErrorType())) {
|
||||
// The API key provided is valid
|
||||
resultOk = true;
|
||||
} else {
|
||||
error = "Error in Weather Underground response";
|
||||
errorDetail = result.getResponse().getErrorDescription();
|
||||
statusDescr = "@text/offline.comm-error-response";
|
||||
}
|
||||
} else {
|
||||
resultOk = true;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
error = "Error running Weather Underground request";
|
||||
errorDetail = e.getMessage();
|
||||
statusDescr = "@text/offline.comm-error-running-request";
|
||||
} catch (JsonSyntaxException e) {
|
||||
error = "Error parsing Weather Underground response";
|
||||
errorDetail = e.getMessage();
|
||||
statusDescr = "@text/offline.comm-error-parsing-response";
|
||||
}
|
||||
|
||||
// Update the thing status
|
||||
if (resultOk) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} else {
|
||||
logger.debug("Setting thing '{}' to OFFLINE: Error '{}': {}", getThing().getUID(), error,
|
||||
errorDetail);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, statusDescr);
|
||||
}
|
||||
}
|
||||
};
|
||||
controlApiKeyJob = scheduler.schedule(runnable, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
logger.debug("Disposing weatherunderground bridge handler.");
|
||||
|
||||
if (controlApiKeyJob != null && !controlApiKeyJob.isCancelled()) {
|
||||
controlApiKeyJob.cancel(true);
|
||||
controlApiKeyJob = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
// not needed
|
||||
}
|
||||
|
||||
public String getApikey() {
|
||||
return apikey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,681 @@
|
||||
/**
|
||||
* 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.weatherunderground.internal.handler;
|
||||
|
||||
import static org.openhab.core.library.unit.MetricPrefix.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.ZoneId;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.measure.Quantity;
|
||||
import javax.measure.Unit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.weatherunderground.internal.config.WeatherUndergroundConfiguration;
|
||||
import org.openhab.binding.weatherunderground.internal.json.WeatherUndergroundJsonCurrent;
|
||||
import org.openhab.binding.weatherunderground.internal.json.WeatherUndergroundJsonData;
|
||||
import org.openhab.binding.weatherunderground.internal.json.WeatherUndergroundJsonForecast;
|
||||
import org.openhab.binding.weatherunderground.internal.json.WeatherUndergroundJsonForecastDay;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.i18n.UnitProvider;
|
||||
import org.openhab.core.io.net.http.HttpUtil;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.ImperialUnits;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
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.binding.ThingHandler;
|
||||
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;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
/**
|
||||
* The {@link WeatherUndergroundHandler} is responsible for handling the
|
||||
* weather things created to use the Weather Underground Service.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
* @author Theo Giovanna - Added a bridge for the API key
|
||||
* @author Laurent Garnier - refactor bridge/thing handling
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WeatherUndergroundHandler extends BaseThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(WeatherUndergroundHandler.class);
|
||||
|
||||
private static final int DEFAULT_REFRESH_PERIOD = 30;
|
||||
private static final String URL_QUERY = "http://api.wunderground.com/api/%APIKEY%/%FEATURES%/%SETTINGS%/q/%QUERY%.json";
|
||||
private static final String FEATURE_CONDITIONS = "conditions";
|
||||
private static final String FEATURE_FORECAST10DAY = "forecast10day";
|
||||
private static final String FEATURE_GEOLOOKUP = "geolookup";
|
||||
private static final Set<String> USUAL_FEATURES = Stream.of(FEATURE_CONDITIONS, FEATURE_FORECAST10DAY)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
private static final Map<String, @Nullable String> LANG_ISO_TO_WU_CODES = new HashMap<>();
|
||||
// Codes from https://www.wunderground.com/weather/api/d/docs?d=language-support
|
||||
static {
|
||||
LANG_ISO_TO_WU_CODES.put("AF", "AF");
|
||||
LANG_ISO_TO_WU_CODES.put("SQ", "AL");
|
||||
LANG_ISO_TO_WU_CODES.put("AR", "AR");
|
||||
LANG_ISO_TO_WU_CODES.put("HY", "HY");
|
||||
LANG_ISO_TO_WU_CODES.put("AZ", "AZ");
|
||||
LANG_ISO_TO_WU_CODES.put("EU", "EU");
|
||||
LANG_ISO_TO_WU_CODES.put("BE", "BY");
|
||||
LANG_ISO_TO_WU_CODES.put("BG", "BU");
|
||||
LANG_ISO_TO_WU_CODES.put("MY", "MY");
|
||||
LANG_ISO_TO_WU_CODES.put("CA", "CA");
|
||||
// Chinese - Simplified => CN
|
||||
LANG_ISO_TO_WU_CODES.put("ZH", "TW");
|
||||
LANG_ISO_TO_WU_CODES.put("HR", "CR");
|
||||
LANG_ISO_TO_WU_CODES.put("CS", "CZ");
|
||||
LANG_ISO_TO_WU_CODES.put("DA", "DK");
|
||||
LANG_ISO_TO_WU_CODES.put("DV", "DV");
|
||||
LANG_ISO_TO_WU_CODES.put("NL", "NL");
|
||||
LANG_ISO_TO_WU_CODES.put("EN", "EN");
|
||||
LANG_ISO_TO_WU_CODES.put("EO", "EO");
|
||||
LANG_ISO_TO_WU_CODES.put("ET", "ET");
|
||||
LANG_ISO_TO_WU_CODES.put("FA", "FA");
|
||||
LANG_ISO_TO_WU_CODES.put("FI", "FI");
|
||||
LANG_ISO_TO_WU_CODES.put("FR", "FR");
|
||||
LANG_ISO_TO_WU_CODES.put("GL", "GZ");
|
||||
LANG_ISO_TO_WU_CODES.put("DE", "DL");
|
||||
LANG_ISO_TO_WU_CODES.put("KA", "KA");
|
||||
LANG_ISO_TO_WU_CODES.put("EL", "GR");
|
||||
LANG_ISO_TO_WU_CODES.put("GU", "GU");
|
||||
LANG_ISO_TO_WU_CODES.put("HT", "HT");
|
||||
LANG_ISO_TO_WU_CODES.put("HE", "IL");
|
||||
LANG_ISO_TO_WU_CODES.put("HI", "HI");
|
||||
LANG_ISO_TO_WU_CODES.put("HU", "HU");
|
||||
LANG_ISO_TO_WU_CODES.put("IS", "IS");
|
||||
LANG_ISO_TO_WU_CODES.put("IO", "IO");
|
||||
LANG_ISO_TO_WU_CODES.put("ID", "ID");
|
||||
LANG_ISO_TO_WU_CODES.put("GA", "IR");
|
||||
LANG_ISO_TO_WU_CODES.put("IT", "IT");
|
||||
LANG_ISO_TO_WU_CODES.put("JA", "JP");
|
||||
LANG_ISO_TO_WU_CODES.put("JV", "JW");
|
||||
LANG_ISO_TO_WU_CODES.put("KM", "KM");
|
||||
LANG_ISO_TO_WU_CODES.put("KO", "KR");
|
||||
LANG_ISO_TO_WU_CODES.put("KU", "KU");
|
||||
LANG_ISO_TO_WU_CODES.put("LA", "LA");
|
||||
LANG_ISO_TO_WU_CODES.put("LV", "LV");
|
||||
LANG_ISO_TO_WU_CODES.put("LT", "LT");
|
||||
// Low German => ND
|
||||
LANG_ISO_TO_WU_CODES.put("MK", "MK");
|
||||
LANG_ISO_TO_WU_CODES.put("MT", "MT");
|
||||
// Mandinka => GM
|
||||
LANG_ISO_TO_WU_CODES.put("MI", "MI");
|
||||
LANG_ISO_TO_WU_CODES.put("MR", "MR");
|
||||
LANG_ISO_TO_WU_CODES.put("MN", "MN");
|
||||
LANG_ISO_TO_WU_CODES.put("NO", "NO");
|
||||
LANG_ISO_TO_WU_CODES.put("OC", "OC");
|
||||
LANG_ISO_TO_WU_CODES.put("PS", "PS");
|
||||
// Plautdietsch => GN
|
||||
LANG_ISO_TO_WU_CODES.put("PL", "PL");
|
||||
LANG_ISO_TO_WU_CODES.put("PT", "BR");
|
||||
LANG_ISO_TO_WU_CODES.put("PA", "PA");
|
||||
LANG_ISO_TO_WU_CODES.put("RO", "RO");
|
||||
LANG_ISO_TO_WU_CODES.put("RU", "RU");
|
||||
LANG_ISO_TO_WU_CODES.put("SR", "SR");
|
||||
LANG_ISO_TO_WU_CODES.put("SK", "SK");
|
||||
LANG_ISO_TO_WU_CODES.put("SL", "SL");
|
||||
LANG_ISO_TO_WU_CODES.put("ES", "SP");
|
||||
LANG_ISO_TO_WU_CODES.put("SW", "SI");
|
||||
LANG_ISO_TO_WU_CODES.put("SV", "SW");
|
||||
// Swiss => CH
|
||||
LANG_ISO_TO_WU_CODES.put("TL", "TL");
|
||||
LANG_ISO_TO_WU_CODES.put("TT", "TT");
|
||||
LANG_ISO_TO_WU_CODES.put("TH", "TH");
|
||||
LANG_ISO_TO_WU_CODES.put("TR", "TR");
|
||||
LANG_ISO_TO_WU_CODES.put("TK", "TK");
|
||||
LANG_ISO_TO_WU_CODES.put("UK", "UA");
|
||||
LANG_ISO_TO_WU_CODES.put("UZ", "UZ");
|
||||
LANG_ISO_TO_WU_CODES.put("VI", "VU");
|
||||
LANG_ISO_TO_WU_CODES.put("CY", "CY");
|
||||
LANG_ISO_TO_WU_CODES.put("WO", "SN");
|
||||
// Yiddish - transliterated => JI
|
||||
LANG_ISO_TO_WU_CODES.put("YI", "YI");
|
||||
}
|
||||
private static final Map<String, @Nullable String> LANG_COUNTRY_TO_WU_CODES = new HashMap<>();
|
||||
static {
|
||||
LANG_COUNTRY_TO_WU_CODES.put("en-GB", "LI"); // British English
|
||||
LANG_COUNTRY_TO_WU_CODES.put("fr-CA", "FC"); // French Canadian
|
||||
}
|
||||
|
||||
private final LocaleProvider localeProvider;
|
||||
private final UnitProvider unitProvider;
|
||||
private final TimeZoneProvider timeZoneProvider;
|
||||
private final Gson gson;
|
||||
private final Map<String, Integer> forecastMap;
|
||||
|
||||
private @Nullable ScheduledFuture<?> refreshJob;
|
||||
|
||||
private @Nullable WeatherUndergroundJsonData weatherData;
|
||||
|
||||
private @Nullable WeatherUndergroundBridgeHandler bridgeHandler;
|
||||
|
||||
public WeatherUndergroundHandler(Thing thing, LocaleProvider localeProvider, UnitProvider unitProvider,
|
||||
TimeZoneProvider timeZoneProvider) {
|
||||
super(thing);
|
||||
this.localeProvider = localeProvider;
|
||||
this.unitProvider = unitProvider;
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
gson = new Gson();
|
||||
forecastMap = initForecastDayMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing WeatherUnderground handler for thing {}", getThing().getUID());
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
initializeThingHandler(null, null);
|
||||
} else {
|
||||
initializeThingHandler(bridge.getHandler(), bridge.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||
logger.debug("bridgeStatusChanged {}", bridgeStatusInfo);
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
initializeThingHandler(null, bridgeStatusInfo.getStatus());
|
||||
} else {
|
||||
initializeThingHandler(bridge.getHandler(), bridgeStatusInfo.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeThingHandler(@Nullable ThingHandler bridgeHandler, @Nullable ThingStatus bridgeStatus) {
|
||||
logger.debug("initializeThingHandler {}", getThing().getUID());
|
||||
if (bridgeHandler != null && bridgeStatus != null) {
|
||||
if (bridgeStatus == ThingStatus.ONLINE) {
|
||||
this.bridgeHandler = (WeatherUndergroundBridgeHandler) bridgeHandler;
|
||||
|
||||
WeatherUndergroundConfiguration config = getConfigAs(WeatherUndergroundConfiguration.class);
|
||||
|
||||
logger.debug("config location = {}", config.location);
|
||||
logger.debug("config language = {}", config.language);
|
||||
logger.debug("config refresh = {}", config.refresh);
|
||||
|
||||
boolean validConfig = true;
|
||||
String errors = "";
|
||||
String statusDescr = null;
|
||||
|
||||
if (config.location == null || config.location.trim().isEmpty()) {
|
||||
errors += " Parameter 'location' must be configured.";
|
||||
statusDescr = "@text/offline.conf-error-missing-location";
|
||||
validConfig = false;
|
||||
}
|
||||
if (config.language != null) {
|
||||
if (config.language.trim().length() != 2) {
|
||||
errors += " Parameter 'language' must be 2 letters.";
|
||||
statusDescr = "@text/offline.conf-error-syntax-language";
|
||||
validConfig = false;
|
||||
}
|
||||
}
|
||||
if (config.refresh != null && config.refresh < 5) {
|
||||
errors += " Parameter 'refresh' must be at least 5 minutes.";
|
||||
statusDescr = "@text/offline.conf-error-min-refresh";
|
||||
validConfig = false;
|
||||
}
|
||||
errors = errors.trim();
|
||||
|
||||
if (validConfig) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
startAutomaticRefresh();
|
||||
} else {
|
||||
logger.debug("Setting thing '{}' to OFFLINE: {}", getThing().getUID(), errors);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, statusDescr);
|
||||
}
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
}
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the job refreshing the weather data
|
||||
*/
|
||||
private void startAutomaticRefresh() {
|
||||
ScheduledFuture<?> job = refreshJob;
|
||||
if (job == null || job.isCancelled()) {
|
||||
Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Request new weather data to the Weather Underground service
|
||||
updateWeatherData(USUAL_FEATURES);
|
||||
|
||||
// Update all channels from the updated weather data
|
||||
for (Channel channel : getThing().getChannels()) {
|
||||
updateChannel(channel.getUID().getId());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.debug("Exception occurred during execution: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WeatherUndergroundConfiguration config = getConfigAs(WeatherUndergroundConfiguration.class);
|
||||
int period = (config.refresh != null) ? config.refresh.intValue() : DEFAULT_REFRESH_PERIOD;
|
||||
refreshJob = scheduler.scheduleWithFixedDelay(runnable, 0, period, TimeUnit.MINUTES);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
logger.debug("Disposing WeatherUnderground handler.");
|
||||
|
||||
ScheduledFuture<?> job = refreshJob;
|
||||
if (job != null) {
|
||||
job.cancel(true);
|
||||
}
|
||||
refreshJob = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command instanceof RefreshType) {
|
||||
updateChannel(channelUID.getId());
|
||||
} else {
|
||||
logger.debug("The Weather Underground binding is a read-only binding and cannot handle command {}",
|
||||
command);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the channel from the last Weather Underground data retrieved
|
||||
*
|
||||
* @param channelId the id identifying the channel to be updated
|
||||
*/
|
||||
private void updateChannel(String channelId) {
|
||||
if (isLinked(channelId)) {
|
||||
State state = null;
|
||||
WeatherUndergroundJsonData data = weatherData;
|
||||
if (data != null) {
|
||||
if (channelId.startsWith("current")) {
|
||||
state = updateCurrentObservationChannel(channelId, data.getCurrent());
|
||||
} else if (channelId.startsWith("forecast")) {
|
||||
state = updateForecastChannel(channelId, data.getForecast());
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Update channel {} with state {}", channelId, (state == null) ? "null" : state.toString());
|
||||
|
||||
// Update the channel
|
||||
if (state != null) {
|
||||
updateState(channelId, state);
|
||||
} else {
|
||||
updateState(channelId, UnDefType.NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable State updateCurrentObservationChannel(String channelId, WeatherUndergroundJsonCurrent current) {
|
||||
WUQuantity quantity;
|
||||
String channelTypeId = getChannelTypeId(channelId);
|
||||
switch (channelTypeId) {
|
||||
case "location":
|
||||
return undefOrState(current.getLocation(), new StringType(current.getLocation()));
|
||||
case "stationId":
|
||||
return undefOrState(current.getStationId(), new StringType(current.getStationId()));
|
||||
case "observationTime":
|
||||
ZoneId zoneId = timeZoneProvider.getTimeZone();
|
||||
return undefOrState(current.getObservationTime(zoneId),
|
||||
new DateTimeType(current.getObservationTime(zoneId)));
|
||||
case "conditions":
|
||||
return undefOrState(current.getConditions(), new StringType(current.getConditions()));
|
||||
case "temperature":
|
||||
quantity = getTemperature(current.getTemperatureC(), current.getTemperatureF());
|
||||
return undefOrQuantity(quantity);
|
||||
case "relativeHumidity":
|
||||
return undefOrState(current.getRelativeHumidity(),
|
||||
new QuantityType<>(current.getRelativeHumidity(), SmartHomeUnits.PERCENT));
|
||||
case "windDirection":
|
||||
return undefOrState(current.getWindDirection(), new StringType(current.getWindDirection()));
|
||||
case "windDirectionDegrees":
|
||||
return undefOrState(current.getWindDirectionDegrees(),
|
||||
new QuantityType<>(current.getWindDirectionDegrees(), SmartHomeUnits.DEGREE_ANGLE));
|
||||
case "windSpeed":
|
||||
quantity = getSpeed(current.getWindSpeedKmh(), current.getWindSpeedMph());
|
||||
return undefOrQuantity(quantity);
|
||||
case "windGust":
|
||||
quantity = getSpeed(current.getWindGustKmh(), current.getWindGustMph());
|
||||
return undefOrQuantity(quantity);
|
||||
case "pressure":
|
||||
quantity = getPressure(current.getPressureHPa(), current.getPressureInHg());
|
||||
return undefOrQuantity(quantity);
|
||||
case "pressureTrend":
|
||||
return undefOrState(current.getPressureTrend(), new StringType(current.getPressureTrend()));
|
||||
case "dewPoint":
|
||||
quantity = getTemperature(current.getDewPointC(), current.getDewPointF());
|
||||
return undefOrQuantity(quantity);
|
||||
case "heatIndex":
|
||||
quantity = getTemperature(current.getHeatIndexC(), current.getHeatIndexF());
|
||||
return undefOrQuantity(quantity);
|
||||
case "windChill":
|
||||
quantity = getTemperature(current.getWindChillC(), current.getWindChillF());
|
||||
return undefOrQuantity(quantity);
|
||||
case "feelingTemperature":
|
||||
quantity = getTemperature(current.getFeelingTemperatureC(), current.getFeelingTemperatureF());
|
||||
return undefOrQuantity(quantity);
|
||||
case "visibility":
|
||||
quantity = getWUQuantity(KILO(SIUnits.METRE), ImperialUnits.MILE, current.getVisibilityKm(),
|
||||
current.getVisibilityMi());
|
||||
return undefOrQuantity(quantity);
|
||||
case "solarRadiation":
|
||||
return undefOrQuantity(new WUQuantity(current.getSolarRadiation(), SmartHomeUnits.IRRADIANCE));
|
||||
case "UVIndex":
|
||||
return undefOrDecimal(current.getUVIndex());
|
||||
case "precipitationDay":
|
||||
quantity = getPrecipitation(current.getPrecipitationDayMm(), current.getPrecipitationDayIn());
|
||||
return undefOrQuantity(quantity);
|
||||
case "precipitationHour":
|
||||
quantity = getPrecipitation(current.getPrecipitationHourMm(), current.getPrecipitationHourIn());
|
||||
return undefOrQuantity(quantity);
|
||||
case "iconKey":
|
||||
return undefOrState(current.getIconKey(), new StringType(current.getIconKey()));
|
||||
case "icon":
|
||||
State icon = HttpUtil.downloadImage(current.getIcon().toExternalForm());
|
||||
if (icon == null) {
|
||||
logger.debug("Failed to download the content of URL {}", current.getIcon().toExternalForm());
|
||||
return null;
|
||||
}
|
||||
return icon;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable State updateForecastChannel(String channelId, WeatherUndergroundJsonForecast forecast) {
|
||||
WUQuantity quantity;
|
||||
int day = getDay(channelId);
|
||||
WeatherUndergroundJsonForecastDay dayForecast = forecast.getSimpleForecast(day);
|
||||
|
||||
String channelTypeId = getChannelTypeId(channelId);
|
||||
switch (channelTypeId) {
|
||||
case "forecastTime":
|
||||
ZoneId zoneId = timeZoneProvider.getTimeZone();
|
||||
return undefOrState(dayForecast.getForecastTime(zoneId),
|
||||
new DateTimeType(dayForecast.getForecastTime(zoneId)));
|
||||
case "conditions":
|
||||
return undefOrState(dayForecast.getConditions(), new StringType(dayForecast.getConditions()));
|
||||
case "minTemperature":
|
||||
quantity = getTemperature(dayForecast.getMinTemperatureC(), dayForecast.getMinTemperatureF());
|
||||
return undefOrQuantity(quantity);
|
||||
case "maxTemperature":
|
||||
quantity = getTemperature(dayForecast.getMaxTemperatureC(), dayForecast.getMaxTemperatureF());
|
||||
return undefOrQuantity(quantity);
|
||||
case "relativeHumidity":
|
||||
return undefOrState(dayForecast.getRelativeHumidity(),
|
||||
new QuantityType<>(dayForecast.getRelativeHumidity(), SmartHomeUnits.PERCENT));
|
||||
case "probaPrecipitation":
|
||||
return undefOrState(dayForecast.getProbaPrecipitation(),
|
||||
new QuantityType<>(dayForecast.getProbaPrecipitation(), SmartHomeUnits.PERCENT));
|
||||
case "precipitationDay":
|
||||
quantity = getPrecipitation(dayForecast.getPrecipitationDayMm(), dayForecast.getPrecipitationDayIn());
|
||||
return undefOrQuantity(quantity);
|
||||
case "snow":
|
||||
quantity = getWUQuantity(CENTI(SIUnits.METRE), ImperialUnits.INCH, dayForecast.getSnowCm(),
|
||||
dayForecast.getSnowIn());
|
||||
return undefOrQuantity(quantity);
|
||||
case "maxWindDirection":
|
||||
return undefOrState(dayForecast.getMaxWindDirection(),
|
||||
new StringType(dayForecast.getMaxWindDirection()));
|
||||
case "maxWindDirectionDegrees":
|
||||
return undefOrState(dayForecast.getMaxWindDirectionDegrees(),
|
||||
new QuantityType<>(dayForecast.getMaxWindDirectionDegrees(), SmartHomeUnits.DEGREE_ANGLE));
|
||||
case "maxWindSpeed":
|
||||
quantity = getSpeed(dayForecast.getMaxWindSpeedKmh(), dayForecast.getMaxWindSpeedMph());
|
||||
return undefOrQuantity(quantity);
|
||||
case "averageWindDirection":
|
||||
return undefOrState(dayForecast.getAverageWindDirection(),
|
||||
new StringType(dayForecast.getAverageWindDirection()));
|
||||
case "averageWindDirectionDegrees":
|
||||
return undefOrState(dayForecast.getAverageWindDirectionDegrees(),
|
||||
new QuantityType<>(dayForecast.getAverageWindDirectionDegrees(), SmartHomeUnits.DEGREE_ANGLE));
|
||||
case "averageWindSpeed":
|
||||
quantity = getSpeed(dayForecast.getAverageWindSpeedKmh(), dayForecast.getAverageWindSpeedMph());
|
||||
return undefOrQuantity(quantity);
|
||||
case "iconKey":
|
||||
return undefOrState(dayForecast.getIconKey(), new StringType(dayForecast.getIconKey()));
|
||||
case "icon":
|
||||
State icon = HttpUtil.downloadImage(dayForecast.getIcon().toExternalForm());
|
||||
if (icon == null) {
|
||||
logger.debug("Failed to download the content of URL {}", dayForecast.getIcon().toExternalForm());
|
||||
return null;
|
||||
}
|
||||
return icon;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable State undefOrState(@Nullable Object value, State state) {
|
||||
return value == null ? null : state;
|
||||
}
|
||||
|
||||
private @Nullable <T extends Quantity<T>> State undefOrQuantity(WUQuantity quantity) {
|
||||
return quantity.value == null ? null : new QuantityType<>(quantity.value, quantity.unit);
|
||||
}
|
||||
|
||||
private @Nullable State undefOrDecimal(@Nullable Number value) {
|
||||
return value == null ? null : new DecimalType(value.doubleValue());
|
||||
}
|
||||
|
||||
private int getDay(String channelId) {
|
||||
String channel = channelId.split("#")[0];
|
||||
|
||||
return forecastMap.get(channel);
|
||||
}
|
||||
|
||||
private String getChannelTypeId(String channelId) {
|
||||
return channelId.substring(channelId.indexOf("#") + 1);
|
||||
}
|
||||
|
||||
private Map<String, Integer> initForecastDayMap() {
|
||||
Map<String, Integer> forecastMap = new HashMap<>();
|
||||
forecastMap.put("forecastToday", Integer.valueOf(1));
|
||||
forecastMap.put("forecastTomorrow", Integer.valueOf(2));
|
||||
forecastMap.put("forecastDay2", Integer.valueOf(3));
|
||||
forecastMap.put("forecastDay3", Integer.valueOf(4));
|
||||
forecastMap.put("forecastDay4", Integer.valueOf(5));
|
||||
forecastMap.put("forecastDay5", Integer.valueOf(6));
|
||||
forecastMap.put("forecastDay6", Integer.valueOf(7));
|
||||
forecastMap.put("forecastDay7", Integer.valueOf(8));
|
||||
forecastMap.put("forecastDay8", Integer.valueOf(9));
|
||||
forecastMap.put("forecastDay9", Integer.valueOf(10));
|
||||
return forecastMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request new current conditions and forecast 10 days to the Weather Underground service
|
||||
* and store the data in weatherData
|
||||
*
|
||||
* @param features the list of features to be requested
|
||||
* @return true if success or false in case of error
|
||||
*/
|
||||
private boolean updateWeatherData(Set<String> features) {
|
||||
WeatherUndergroundJsonData result = null;
|
||||
boolean resultOk = false;
|
||||
String error = null;
|
||||
String errorDetail = null;
|
||||
String statusDescr = null;
|
||||
|
||||
// Request new weather data to the Weather Underground service
|
||||
|
||||
try {
|
||||
WeatherUndergroundConfiguration config = getConfigAs(WeatherUndergroundConfiguration.class);
|
||||
|
||||
String urlStr = URL_QUERY.replace("%FEATURES%", String.join("/", features));
|
||||
|
||||
String lang = config.language == null ? "" : config.language.trim();
|
||||
if (lang.isEmpty()) {
|
||||
// If language is not set in the configuration, you try deducing it from the system language
|
||||
lang = getCodeFromLanguage(localeProvider.getLocale());
|
||||
logger.debug("Use language deduced from system locale {}: {}", localeProvider.getLocale().getLanguage(),
|
||||
lang);
|
||||
}
|
||||
if (lang.isEmpty()) {
|
||||
urlStr = urlStr.replace("%SETTINGS%", "");
|
||||
} else {
|
||||
urlStr = urlStr.replace("%SETTINGS%", "lang:" + lang.toUpperCase());
|
||||
}
|
||||
|
||||
String location = config.location == null ? "" : config.location.trim();
|
||||
urlStr = urlStr.replace("%QUERY%", location);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("URL = {}", urlStr.replace("%APIKEY%", "***"));
|
||||
}
|
||||
|
||||
urlStr = urlStr.replace("%APIKEY%", bridgeHandler.getApikey());
|
||||
|
||||
// Run the HTTP request and get the JSON response from Weather Underground
|
||||
String response = null;
|
||||
try {
|
||||
response = HttpUtil.executeUrl("GET", urlStr, WeatherUndergroundBridgeHandler.FETCH_TIMEOUT_MS);
|
||||
logger.debug("weatherData = {}", response);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// catch Illegal character in path at index XX: http://api.wunderground.com/...
|
||||
error = "Error creating URI with location parameter: '" + location + "'";
|
||||
errorDetail = e.getMessage();
|
||||
statusDescr = "@text/offline.uri-error";
|
||||
}
|
||||
|
||||
// Map the JSON response to an object
|
||||
result = gson.fromJson(response, WeatherUndergroundJsonData.class);
|
||||
if (result.getResponse() == null) {
|
||||
errorDetail = "missing response sub-object";
|
||||
} else if (result.getResponse().getErrorDescription() != null) {
|
||||
if ("keynotfound".equals(result.getResponse().getErrorType())) {
|
||||
error = "API key has to be fixed";
|
||||
statusDescr = "@text/offline.comm-error-invalid-api-key";
|
||||
}
|
||||
errorDetail = result.getResponse().getErrorDescription();
|
||||
} else {
|
||||
resultOk = true;
|
||||
for (String feature : features) {
|
||||
if (feature.equals(FEATURE_CONDITIONS) && result.getCurrent() == null) {
|
||||
resultOk = false;
|
||||
errorDetail = "missing current_observation sub-object";
|
||||
} else if (feature.equals(FEATURE_FORECAST10DAY) && result.getForecast() == null) {
|
||||
resultOk = false;
|
||||
errorDetail = "missing forecast sub-object";
|
||||
} else if (feature.equals(FEATURE_GEOLOOKUP) && result.getLocation() == null) {
|
||||
resultOk = false;
|
||||
errorDetail = "missing location sub-object";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!resultOk && error == null) {
|
||||
error = "Error in Weather Underground response";
|
||||
statusDescr = "@text/offline.comm-error-response";
|
||||
}
|
||||
} catch (IOException e) {
|
||||
error = "Error running Weather Underground request";
|
||||
errorDetail = e.getMessage();
|
||||
statusDescr = "@text/offline.comm-error-running-request";
|
||||
} catch (JsonSyntaxException e) {
|
||||
error = "Error parsing Weather Underground response";
|
||||
errorDetail = e.getMessage();
|
||||
statusDescr = "@text/offline.comm-error-parsing-response";
|
||||
}
|
||||
|
||||
// Update the thing status
|
||||
if (resultOk) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
weatherData = result;
|
||||
} else {
|
||||
logger.debug("Setting thing '{}' to OFFLINE: Error '{}': {}", getThing().getUID(), error, errorDetail);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, statusDescr);
|
||||
weatherData = null;
|
||||
}
|
||||
|
||||
return resultOk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the WU code associated to a language
|
||||
*
|
||||
* @param locale the locale settings with language and country
|
||||
* @return the associated WU code or an empty string if not found
|
||||
*/
|
||||
public static String getCodeFromLanguage(Locale locale) {
|
||||
String key = locale.getLanguage() + "-" + locale.getCountry();
|
||||
String language = LANG_COUNTRY_TO_WU_CODES.get(key);
|
||||
if (language == null) {
|
||||
language = LANG_ISO_TO_WU_CODES.get(locale.getLanguage().toUpperCase());
|
||||
}
|
||||
return language != null ? language : "";
|
||||
}
|
||||
|
||||
private WUQuantity getTemperature(BigDecimal siValue, BigDecimal imperialValue) {
|
||||
return getWUQuantity(SIUnits.CELSIUS, ImperialUnits.FAHRENHEIT, siValue, imperialValue);
|
||||
}
|
||||
|
||||
private WUQuantity getSpeed(BigDecimal siValue, BigDecimal imperialValue) {
|
||||
return getWUQuantity(SIUnits.KILOMETRE_PER_HOUR, ImperialUnits.MILES_PER_HOUR, siValue, imperialValue);
|
||||
}
|
||||
|
||||
private WUQuantity getPressure(BigDecimal siValue, BigDecimal imperialValue) {
|
||||
return getWUQuantity(HECTO(SIUnits.PASCAL), ImperialUnits.INCH_OF_MERCURY, siValue, imperialValue);
|
||||
}
|
||||
|
||||
private WUQuantity getPrecipitation(BigDecimal siValue, BigDecimal imperialValue) {
|
||||
return getWUQuantity(MILLI(SIUnits.METRE), ImperialUnits.INCH, siValue, imperialValue);
|
||||
}
|
||||
|
||||
private <T extends Quantity<T>> WUQuantity getWUQuantity(Unit<T> siUnit, Unit<T> imperialUnit, BigDecimal siValue,
|
||||
BigDecimal imperialValue) {
|
||||
boolean isSI = unitProvider.getMeasurementSystem().equals(SIUnits.getInstance());
|
||||
return new WUQuantity(isSI ? siValue : imperialValue, isSI ? siUnit : imperialUnit);
|
||||
}
|
||||
|
||||
private class WUQuantity {
|
||||
private WUQuantity(BigDecimal value, Unit<?> unit) {
|
||||
this.value = value;
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
private final Unit<?> unit;
|
||||
private final BigDecimal value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,568 @@
|
||||
/**
|
||||
* 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.weatherunderground.internal.json;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URL;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
/**
|
||||
* The {@link WeatherUndergroundJsonCurrent} is the Java class used
|
||||
* to map the entry "current_observation" from the JSON response to a Weather
|
||||
* Underground request.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
public class WeatherUndergroundJsonCurrent {
|
||||
|
||||
// Commented members indicate properties returned by the API not used by the binding
|
||||
|
||||
// private Object image;
|
||||
|
||||
// private Location display_location;
|
||||
private Location observation_location;
|
||||
// private Object estimated;
|
||||
|
||||
private String station_id;
|
||||
|
||||
// private String observation_time;
|
||||
// private String observation_time_rfc822;
|
||||
private String observation_epoch;
|
||||
// private String local_time_rfc822;
|
||||
// private String local_epoch;
|
||||
// private String local_tz_short;
|
||||
// private String local_tz_long;
|
||||
// private String local_tz_offset;
|
||||
|
||||
private String weather;
|
||||
|
||||
// private String ;temperature_string;
|
||||
private BigDecimal temp_f;
|
||||
private BigDecimal temp_c;
|
||||
|
||||
private String relative_humidity;
|
||||
|
||||
// private String wind_string;
|
||||
private String wind_dir;
|
||||
private BigDecimal wind_degrees;
|
||||
private BigDecimal wind_mph;
|
||||
private String wind_gust_mph;
|
||||
private BigDecimal wind_kph;
|
||||
private String wind_gust_kph;
|
||||
|
||||
private String pressure_mb;
|
||||
private String pressure_in;
|
||||
private String pressure_trend;
|
||||
|
||||
// private String dewpoint_string;
|
||||
private BigDecimal dewpoint_f;
|
||||
private BigDecimal dewpoint_c;
|
||||
|
||||
// private String heat_index_string;
|
||||
private String heat_index_f;
|
||||
private String heat_index_c;
|
||||
|
||||
// private String windchill_string;
|
||||
private String windchill_f;
|
||||
private String windchill_c;
|
||||
|
||||
// private String feelslike_string;
|
||||
private String feelslike_f;
|
||||
private String feelslike_c;
|
||||
|
||||
private String visibility_mi;
|
||||
private String visibility_km;
|
||||
|
||||
private String solarradiation;
|
||||
private String UV;
|
||||
|
||||
// private String precip_1hr_string;
|
||||
private String precip_1hr_in;
|
||||
private String precip_1hr_metric;
|
||||
// private String precip_today_string;
|
||||
private String precip_today_in;
|
||||
private String precip_today_metric;
|
||||
|
||||
private String icon;
|
||||
private String icon_url;
|
||||
// private String forecast_url;
|
||||
// private String history_url;
|
||||
// private String ob_url;
|
||||
|
||||
// private String nowcast;
|
||||
|
||||
public WeatherUndergroundJsonCurrent() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the observation location (full name)
|
||||
*
|
||||
* Used to update the channel current#location
|
||||
*
|
||||
* @return the observation location or null if not defined
|
||||
*/
|
||||
public String getLocation() {
|
||||
return (observation_location == null) ? null : observation_location.getFull();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the station ID
|
||||
*
|
||||
* Used to update the channel current#stationId
|
||||
*
|
||||
* @return the station ID or null if not defined
|
||||
*/
|
||||
public String getStationId() {
|
||||
return station_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the observation date and time
|
||||
*
|
||||
* Used to update the channel current#observationTime
|
||||
*
|
||||
* @return the observation date and time or null if not defined
|
||||
*/
|
||||
public ZonedDateTime getObservationTime(ZoneId zoneId) {
|
||||
return WeatherUndergroundJsonUtils.convertToZonedDateTime(observation_epoch, zoneId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current weather conditions
|
||||
*
|
||||
* Used to update the channel current#conditions
|
||||
*
|
||||
* @return the current weather conditions or null if not defined
|
||||
*/
|
||||
public String getConditions() {
|
||||
return weather;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current temperature in degrees Celsius
|
||||
*
|
||||
* Used to update the channel current#temperature
|
||||
*
|
||||
* @return the current temperature in degrees Celsius or null if not defined
|
||||
*/
|
||||
public BigDecimal getTemperatureC() {
|
||||
return temp_c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current temperature in degrees Fahrenheit
|
||||
*
|
||||
* Used to update the channel current#temperature
|
||||
*
|
||||
* @return the current temperature in degrees Fahrenheit or null if not defined
|
||||
*/
|
||||
public BigDecimal getTemperatureF() {
|
||||
return temp_f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current relative humidity
|
||||
*
|
||||
* Used to update the channel current#relativeHumidity
|
||||
*
|
||||
* @return the current relative humidity or null if not defined
|
||||
*/
|
||||
public Integer getRelativeHumidity() {
|
||||
if (relative_humidity != null && !relative_humidity.isEmpty() && !relative_humidity.equalsIgnoreCase("N/A")) {
|
||||
return WeatherUndergroundJsonUtils.convertToInteger(relative_humidity.replace("%", ""));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wind direction as a text
|
||||
*
|
||||
* Used to update the channel current#windDirection
|
||||
*
|
||||
* @return the wind direction or null if not defined
|
||||
*/
|
||||
public String getWindDirection() {
|
||||
return wind_dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wind direction in degrees
|
||||
*
|
||||
* Used to update the channel current#windDirectionDegrees
|
||||
*
|
||||
* @return the wind direction in degrees or null if not defined
|
||||
*/
|
||||
public BigDecimal getWindDirectionDegrees() {
|
||||
return wind_degrees;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wind speed in km/h
|
||||
*
|
||||
* Used to update the channel current#windSpeed
|
||||
*
|
||||
* @return the wind speed in km/h or null if not defined
|
||||
*/
|
||||
public BigDecimal getWindSpeedKmh() {
|
||||
return wind_kph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wind speed in mph
|
||||
*
|
||||
* Used to update the channel current#windSpeed
|
||||
*
|
||||
* @return the wind speed in mph or null if not defined
|
||||
*/
|
||||
public BigDecimal getWindSpeedMph() {
|
||||
return wind_mph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wind gust in km/h
|
||||
*
|
||||
* Used to update the channel current#windGust
|
||||
*
|
||||
* @return the wind gust in km/h or null if not defined
|
||||
*/
|
||||
public BigDecimal getWindGustKmh() {
|
||||
return WeatherUndergroundJsonUtils.convertToBigDecimal(wind_gust_kph);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wind gust in mph
|
||||
*
|
||||
* Used to update the channel current#windGust
|
||||
*
|
||||
* @return the wind gust in mph or null if not defined
|
||||
*/
|
||||
public BigDecimal getWindGustMph() {
|
||||
return WeatherUndergroundJsonUtils.convertToBigDecimal(wind_gust_mph);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pressure in hPa
|
||||
*
|
||||
* Used to update the channel current#pressure
|
||||
*
|
||||
* @return the pressure in hPa or null if not defined
|
||||
*/
|
||||
public BigDecimal getPressureHPa() {
|
||||
return WeatherUndergroundJsonUtils.convertToBigDecimal(pressure_mb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pressure in inHg
|
||||
*
|
||||
* Used to update the channel current#pressure
|
||||
*
|
||||
* @return the pressure in inHg or null if not defined
|
||||
*/
|
||||
public BigDecimal getPressureInHg() {
|
||||
return WeatherUndergroundJsonUtils.convertToBigDecimal(pressure_in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pressure trend
|
||||
*
|
||||
* Used to update the channel current#pressureTrend
|
||||
*
|
||||
* @return the pressure trend or null if not defined
|
||||
*/
|
||||
public String getPressureTrend() {
|
||||
return WeatherUndergroundJsonUtils.convertToTrend(pressure_trend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the dew point temperature in degrees Celsius
|
||||
*
|
||||
* Used to update the channel current#dewPoint
|
||||
*
|
||||
* @return the dew point temperature in degrees Celsius or null if not defined
|
||||
*/
|
||||
public BigDecimal getDewPointC() {
|
||||
return dewpoint_c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the dew point temperature in degrees Fahrenheit
|
||||
*
|
||||
* Used to update the channel current#dewPoint
|
||||
*
|
||||
* @return the dew point temperature in degrees Fahrenheit or null if not defined
|
||||
*/
|
||||
public BigDecimal getDewPointF() {
|
||||
return dewpoint_f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the heat index in degrees Celsius
|
||||
*
|
||||
* Used to update the channel current#heatIndex
|
||||
*
|
||||
* @return the heat index in degrees Celsius or null if not defined
|
||||
*/
|
||||
public BigDecimal getHeatIndexC() {
|
||||
return WeatherUndergroundJsonUtils.convertToBigDecimal(heat_index_c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the heat index in degrees Fahrenheit
|
||||
*
|
||||
* Used to update the channel current#heatIndex
|
||||
*
|
||||
* @return the heat index in degrees Fahrenheit or null if not defined
|
||||
*/
|
||||
public BigDecimal getHeatIndexF() {
|
||||
return WeatherUndergroundJsonUtils.convertToBigDecimal(heat_index_f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wind chill temperature in degrees Celsius
|
||||
*
|
||||
* Used to update the channel current#windChill
|
||||
*
|
||||
* @return the wind chill temperature in degrees Celsius or null if not defined
|
||||
*/
|
||||
public BigDecimal getWindChillC() {
|
||||
return WeatherUndergroundJsonUtils.convertToBigDecimal(windchill_c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wind chill temperature in degrees Fahrenheit
|
||||
*
|
||||
* Used to update the channel current#windChill
|
||||
*
|
||||
* @return the wind chill temperature in degrees Fahrenheit or null if not defined
|
||||
*/
|
||||
public BigDecimal getWindChillF() {
|
||||
return WeatherUndergroundJsonUtils.convertToBigDecimal(windchill_f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the feeling temperature in degrees Celsius
|
||||
*
|
||||
* Used to update the channel current#feelingTemperature
|
||||
*
|
||||
* @return the feeling temperature in degrees Celsius or null if not defined
|
||||
*/
|
||||
public BigDecimal getFeelingTemperatureC() {
|
||||
return WeatherUndergroundJsonUtils.convertToBigDecimal(feelslike_c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the feeling temperature in degrees Fahrenheit
|
||||
*
|
||||
* Used to update the channel current#feelingTemperature
|
||||
*
|
||||
* @return the feeling temperature in degrees Fahrenheit or null if not defined
|
||||
*/
|
||||
public BigDecimal getFeelingTemperatureF() {
|
||||
return WeatherUndergroundJsonUtils.convertToBigDecimal(feelslike_f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the visibility in kilometers
|
||||
*
|
||||
* Used to update the channel current#visibility
|
||||
*
|
||||
* @return the visibility in kilometers or null if not defined
|
||||
*/
|
||||
public BigDecimal getVisibilityKm() {
|
||||
return WeatherUndergroundJsonUtils.convertToBigDecimal(visibility_km);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the visibility in miles
|
||||
*
|
||||
* Used to update the channel current#visibility
|
||||
*
|
||||
* @return the visibility in miles or null if not defined
|
||||
*/
|
||||
public BigDecimal getVisibilityMi() {
|
||||
return WeatherUndergroundJsonUtils.convertToBigDecimal(visibility_mi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the precipitation in the last hour in millimeters
|
||||
*
|
||||
* Used to update the channel current#precipitationHour
|
||||
*
|
||||
* @return the precipitation in the last hour in millimeters or null if not defined
|
||||
*/
|
||||
public BigDecimal getPrecipitationHourMm() {
|
||||
BigDecimal result = WeatherUndergroundJsonUtils.convertToBigDecimal(precip_1hr_metric);
|
||||
if ((result != null) && (result.doubleValue() < 0.0)) {
|
||||
result = BigDecimal.ZERO;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the precipitation in the last hour in inches
|
||||
*
|
||||
* Used to update the channel current#precipitationHour
|
||||
*
|
||||
* @return the precipitation in the last hour in inches or null if not defined
|
||||
*/
|
||||
public BigDecimal getPrecipitationHourIn() {
|
||||
BigDecimal result = WeatherUndergroundJsonUtils.convertToBigDecimal(precip_1hr_in);
|
||||
if ((result != null) && (result.doubleValue() < 0.0)) {
|
||||
result = BigDecimal.ZERO;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the precipitation for the full day in millimeters
|
||||
*
|
||||
* Used to update the channel current#precipitationDay
|
||||
*
|
||||
* @return the precipitation for the full day in millimeters or null if not defined
|
||||
*/
|
||||
public BigDecimal getPrecipitationDayMm() {
|
||||
return WeatherUndergroundJsonUtils.convertToBigDecimal(precip_today_metric);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the precipitation for the full day in inches
|
||||
*
|
||||
* Used to update the channel current#precipitationDay
|
||||
*
|
||||
* @return the precipitation for the full day in inches or null if not defined
|
||||
*/
|
||||
public BigDecimal getPrecipitationDayIn() {
|
||||
return WeatherUndergroundJsonUtils.convertToBigDecimal(precip_today_in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the solar radiation in Watts/sq. m
|
||||
*
|
||||
* Used to update the channel current#solarRadiation
|
||||
*
|
||||
* @return the solar radiation or null if not defined or negative
|
||||
*/
|
||||
public BigDecimal getSolarRadiation() {
|
||||
BigDecimal value = WeatherUndergroundJsonUtils.convertToBigDecimal(solarradiation);
|
||||
// We check that the index is not negative
|
||||
if (value != null && value.signum() == -1) {
|
||||
value = null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the UV Index
|
||||
*
|
||||
* Used to update the channel current#UVIndex
|
||||
*
|
||||
* @return the UV Index or null if not defined or negative
|
||||
*/
|
||||
public BigDecimal getUVIndex() {
|
||||
BigDecimal value = WeatherUndergroundJsonUtils.convertToBigDecimal(UV);
|
||||
// We check that the index is not negative
|
||||
if (value != null && value.signum() == -1) {
|
||||
value = null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the icon URL representing the current weather conditions
|
||||
*
|
||||
* Used to update the channel current#icon
|
||||
*
|
||||
* @return the icon URL representing the current weather conditions or null if not defined
|
||||
*/
|
||||
public URL getIcon() {
|
||||
return WeatherUndergroundJsonUtils.getValidUrl(icon_url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the icon key used in the URL representing the current weather conditions
|
||||
*
|
||||
* Used to update the channel current#iconKey
|
||||
*
|
||||
* @return the icon key used in the URL representing the current weather conditions
|
||||
*/
|
||||
public String getIconKey() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
class Location {
|
||||
private String full;
|
||||
private String city;
|
||||
private String state;
|
||||
private String state_name;
|
||||
private String country;
|
||||
private String country_iso3166;
|
||||
private String zip;
|
||||
private String magic;
|
||||
private String wmo;
|
||||
private String latitude;
|
||||
private String longitude;
|
||||
private String elevation;
|
||||
|
||||
Location() {
|
||||
}
|
||||
|
||||
public String getFull() {
|
||||
return full;
|
||||
}
|
||||
|
||||
public String getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public String getStateName() {
|
||||
return state_name;
|
||||
}
|
||||
|
||||
public String getCountry() {
|
||||
return country;
|
||||
}
|
||||
|
||||
public String getCountryIso3166() {
|
||||
return country_iso3166;
|
||||
}
|
||||
|
||||
public String getZip() {
|
||||
return zip;
|
||||
}
|
||||
|
||||
public String getMagic() {
|
||||
return magic;
|
||||
}
|
||||
|
||||
public String getWmo() {
|
||||
return wmo;
|
||||
}
|
||||
|
||||
public String getLatitude() {
|
||||
return latitude;
|
||||
}
|
||||
|
||||
public String getLongitude() {
|
||||
return longitude;
|
||||
}
|
||||
|
||||
public String getElevation() {
|
||||
return elevation;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* 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.weatherunderground.internal.json;
|
||||
|
||||
/**
|
||||
* The {@link WeatherUndergroundJsonData} is the Java class used to map the JSON
|
||||
* response to a Weather Underground request.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
public class WeatherUndergroundJsonData {
|
||||
|
||||
private WeatherUndergroundJsonResponse response;
|
||||
private WeatherUndergroundJsonCurrent current_observation;
|
||||
private WeatherUndergroundJsonForecast forecast;
|
||||
private WeatherUndergroundJsonLocation location;
|
||||
|
||||
public WeatherUndergroundJsonData() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link WeatherUndergroundJsonResponse} object
|
||||
*
|
||||
* @return the {@link WeatherUndergroundJsonResponse} object
|
||||
*/
|
||||
public WeatherUndergroundJsonResponse getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link WeatherUndergroundJsonLocation} object
|
||||
*
|
||||
* @return the {@link WeatherUndergroundJsonLocation} object
|
||||
*/
|
||||
public WeatherUndergroundJsonLocation getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link WeatherUndergroundJsonForecast} object
|
||||
*
|
||||
* @return the {@link WeatherUndergroundJsonForecast} object
|
||||
*/
|
||||
public WeatherUndergroundJsonForecast getForecast() {
|
||||
return forecast;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link WeatherUndergroundJsonCurrent} object
|
||||
*
|
||||
* Used to update the channels current#xxx
|
||||
*
|
||||
* @return the {@link WeatherUndergroundJsonCurrent} object
|
||||
*/
|
||||
public WeatherUndergroundJsonCurrent getCurrent() {
|
||||
return current_observation;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.weatherunderground.internal.json;
|
||||
|
||||
/**
|
||||
* The {@link WeatherUndergroundJsonError} is the Java class used
|
||||
* to map the entry "response.error" from the JSON response to a Weather Underground
|
||||
* request.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
public class WeatherUndergroundJsonError {
|
||||
|
||||
private String type;
|
||||
private String description;
|
||||
|
||||
public WeatherUndergroundJsonError() {
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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.weatherunderground.internal.json;
|
||||
|
||||
/**
|
||||
* The {@link WeatherUndergroundJsonForecast} is the Java class used
|
||||
* to map the entry "forecast" from the JSON response to a Weather Underground
|
||||
* request.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
public class WeatherUndergroundJsonForecast {
|
||||
|
||||
// Commented members indicate properties returned by the API not used by the binding
|
||||
|
||||
// private Object txt_forecast;
|
||||
private WeatherUndergroundJsonSimpleForecast simpleforecast;
|
||||
|
||||
public WeatherUndergroundJsonForecast() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link WeatherUndergroundJsonForecastDay} object for a given day
|
||||
*
|
||||
* @return the {@link WeatherUndergroundJsonForecastDay} object for the day
|
||||
*/
|
||||
public WeatherUndergroundJsonForecastDay getSimpleForecast(int day) {
|
||||
return (simpleforecast == null) ? null : simpleforecast.getForecastDay(day);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,405 @@
|
||||
/**
|
||||
* 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.weatherunderground.internal.json;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URL;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
/**
|
||||
* The {@link WeatherUndergroundJsonForecastDay} is the Java class used
|
||||
* to map the list element of the entry "forecast.simpleforecast.forecastday"
|
||||
* from the JSON response to a Weather Underground request.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
public class WeatherUndergroundJsonForecastDay {
|
||||
|
||||
// Commented members indicate properties returned by the API not used by the binding
|
||||
|
||||
private ForecastDate date;
|
||||
private Integer period;
|
||||
|
||||
private ForecastTemperature high;
|
||||
private ForecastTemperature low;
|
||||
|
||||
private String conditions;
|
||||
|
||||
private String icon;
|
||||
private String icon_url;
|
||||
// private String skyicon;
|
||||
|
||||
private Integer pop;
|
||||
|
||||
private ForecastPrecipitation qpf_allday;
|
||||
// private ForecastPrecipitation qpf_day;
|
||||
// private ForecastPrecipitation qpf_night;
|
||||
private ForecastPrecipitation snow_allday;
|
||||
// private ForecastPrecipitation snow_day;
|
||||
// private ForecastPrecipitation snow_night;
|
||||
|
||||
private ForecastWind maxwind;
|
||||
private ForecastWind avewind;
|
||||
|
||||
private Integer avehumidity;
|
||||
// private Integer minhumidity;
|
||||
// private Integer maxhumidity;
|
||||
|
||||
public WeatherUndergroundJsonForecastDay() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the forecast date and time
|
||||
*
|
||||
* Used to update the channel forecastDayX#forecastTime
|
||||
*
|
||||
* @return the forecast date and time or null if not defined
|
||||
*/
|
||||
public ZonedDateTime getForecastTime(ZoneId zoneId) {
|
||||
return WeatherUndergroundJsonUtils.convertToZonedDateTime((date == null) ? null : date.getEpoch(), zoneId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the period number
|
||||
*
|
||||
* @return the period number
|
||||
*/
|
||||
public Integer getPeriod() {
|
||||
return period;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the weather forecast conditions
|
||||
*
|
||||
* Used to update the channel forecastDayX#conditions
|
||||
*
|
||||
* @return the weather forecast conditions or null if not defined
|
||||
*/
|
||||
public String getConditions() {
|
||||
return conditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the icon URL representing the weather forecast conditions
|
||||
*
|
||||
* Used to update the channel forecastDayX#icon
|
||||
*
|
||||
* @return the icon URL representing the weather forecast conditions or null if not defined
|
||||
*/
|
||||
public URL getIcon() {
|
||||
return WeatherUndergroundJsonUtils.getValidUrl(icon_url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the icon key used in the URL representing the weather forecast conditions
|
||||
*
|
||||
* Used to update the channel forecastDayX#iconKey
|
||||
*
|
||||
* @return the icon key used in the URL representing the weather forecast conditions
|
||||
*/
|
||||
public String getIconKey() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the minimum temperature in degrees Celsius
|
||||
*
|
||||
* Used to update the channel forecastDayX#minTemperature
|
||||
*
|
||||
* @return the minimum temperature in degrees Celsius or null if not defined
|
||||
*/
|
||||
public BigDecimal getMinTemperatureC() {
|
||||
return (low == null) ? null : WeatherUndergroundJsonUtils.convertToBigDecimal(low.getCelsius());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the minimum temperature in degrees Fahrenheit
|
||||
*
|
||||
* Used to update the channel forecastDayX#minTemperature
|
||||
*
|
||||
* @return the minimum temperature in degrees Fahrenheit or null if not defined
|
||||
*/
|
||||
public BigDecimal getMinTemperatureF() {
|
||||
return (low == null) ? null : WeatherUndergroundJsonUtils.convertToBigDecimal(low.getFahrenheit());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum temperature in degrees Celsius
|
||||
*
|
||||
* Used to update the channel forecastDayX#maxTemperature
|
||||
*
|
||||
* @return the maximum temperature in degrees Celsius or null if not defined
|
||||
*/
|
||||
public BigDecimal getMaxTemperatureC() {
|
||||
return (high == null) ? null : WeatherUndergroundJsonUtils.convertToBigDecimal(high.getCelsius());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum temperature in degrees Fahrenheit
|
||||
*
|
||||
* Used to update the channel forecastDayX#maxTemperature
|
||||
*
|
||||
* @return the maximum temperature in degrees Fahrenheit or null if not defined
|
||||
*/
|
||||
public BigDecimal getMaxTemperatureF() {
|
||||
return (high == null) ? null : WeatherUndergroundJsonUtils.convertToBigDecimal(high.getFahrenheit());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the relative humidity
|
||||
*
|
||||
* Used to update the channel forecastDayX#relativeHumidity
|
||||
*
|
||||
* @return the relative humidity or null if not defined
|
||||
*/
|
||||
public Integer getRelativeHumidity() {
|
||||
return avehumidity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the probability of precipitation
|
||||
*
|
||||
* Used to update the channel forecastDayX#probaPrecipitation
|
||||
*
|
||||
* @return the probability of precipitation or null if not defined
|
||||
*/
|
||||
public Integer getProbaPrecipitation() {
|
||||
return pop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the precipitation for the full day in millimeters
|
||||
*
|
||||
* Used to update the channel forecastDayX#precipitationDay
|
||||
*
|
||||
* @return the precipitation for the full day in millimeters or null if not defined
|
||||
*/
|
||||
public BigDecimal getPrecipitationDayMm() {
|
||||
return (qpf_allday == null) ? null : qpf_allday.mm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the precipitation for the full day in inches
|
||||
*
|
||||
* Used to update the channel forecastDayX#precipitation
|
||||
*
|
||||
* @return the precipitation for the full day in inches or null if not defined
|
||||
*/
|
||||
public BigDecimal getPrecipitationDayIn() {
|
||||
return (qpf_allday == null) ? null : qpf_allday.in;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the amount of snow for the full day in centimeters
|
||||
*
|
||||
* Used to update the channel forecastDayX#snow
|
||||
*
|
||||
* @return the amount of snow for the full day in centimeters or null if not defined
|
||||
*/
|
||||
public BigDecimal getSnowCm() {
|
||||
return (snow_allday == null) ? null : snow_allday.cm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the amount of snow for the full day in inches
|
||||
*
|
||||
* Used to update the channel forecastDayX#snow
|
||||
*
|
||||
* @return the amount of snow for the full day in inches or null if not defined
|
||||
*/
|
||||
public BigDecimal getSnowIn() {
|
||||
return (snow_allday == null) ? null : snow_allday.in;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum wind direction as a text
|
||||
*
|
||||
* Used to update the channel forecastDayX#maxWindDirection
|
||||
*
|
||||
* @return the maximum wind direction or null if not defined
|
||||
*/
|
||||
public String getMaxWindDirection() {
|
||||
return (maxwind == null) ? null : maxwind.getDir();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum wind direction in degrees
|
||||
*
|
||||
* Used to update the channel forecastDayX#maxWindDirectionDegrees
|
||||
*
|
||||
* @return the maximum wind direction in degrees or null if not defined
|
||||
*/
|
||||
public BigDecimal getMaxWindDirectionDegrees() {
|
||||
return (maxwind == null) ? null : WeatherUndergroundJsonUtils.convertToBigDecimal(maxwind.getDegrees());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum wind speed in km/h
|
||||
*
|
||||
* Used to update the channel forecastDayX#maxWindSpeed
|
||||
*
|
||||
* @return the maximum wind speed in km/h or null if not defined
|
||||
*/
|
||||
public BigDecimal getMaxWindSpeedKmh() {
|
||||
return (maxwind == null) ? null : maxwind.getKph();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum wind speed in mph
|
||||
*
|
||||
* Used to update the channel forecastDayX#maxWindSpeed
|
||||
*
|
||||
* @return the maximum wind speed in mph or null if not defined
|
||||
*/
|
||||
public BigDecimal getMaxWindSpeedMph() {
|
||||
return (maxwind == null) ? null : maxwind.getMph();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the average wind direction as a text
|
||||
*
|
||||
* Used to update the channel forecastDayX#averageWindDirection
|
||||
*
|
||||
* @return the average wind direction or null if not defined
|
||||
*/
|
||||
public String getAverageWindDirection() {
|
||||
return (avewind == null) ? null : avewind.getDir();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the average wind direction in degrees
|
||||
*
|
||||
* Used to update the channel forecastDayX#averageWindDirectionDegrees
|
||||
*
|
||||
* @return the average wind direction in degrees or null if not defined
|
||||
*/
|
||||
public BigDecimal getAverageWindDirectionDegrees() {
|
||||
return (avewind == null) ? null : WeatherUndergroundJsonUtils.convertToBigDecimal(avewind.getDegrees());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the average wind speed in km/h
|
||||
*
|
||||
* Used to update the channel forecastDayX#averageWindSpeed
|
||||
*
|
||||
* @return the average wind speed in km/h or null if not defined
|
||||
*/
|
||||
public BigDecimal getAverageWindSpeedKmh() {
|
||||
return (avewind == null) ? null : avewind.getKph();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the average wind speed in mph
|
||||
*
|
||||
* Used to update the channel forecastDayX#averageWindSpeed
|
||||
*
|
||||
* @return the average wind speed in mph or null if not defined
|
||||
*/
|
||||
public BigDecimal getAverageWindSpeedMph() {
|
||||
return (avewind == null) ? null : avewind.getMph();
|
||||
}
|
||||
|
||||
class ForecastDate {
|
||||
|
||||
// Commented members indicate properties returned by the API not used by the binding
|
||||
|
||||
private String epoch;
|
||||
// private String pretty;
|
||||
// private Integer day;
|
||||
// private Integer month;
|
||||
// private Integer year;
|
||||
// private Integer yday;
|
||||
// private Integer hour;
|
||||
// private String min;
|
||||
// private Integer sec;
|
||||
// private String isdst;
|
||||
// private String monthname;
|
||||
// private String monthname_short;
|
||||
// private String weekday_short;
|
||||
// private String weekday;
|
||||
// private String ampm;
|
||||
// private String tz_short;
|
||||
// private String tz_long;
|
||||
|
||||
ForecastDate() {
|
||||
}
|
||||
|
||||
public String getEpoch() {
|
||||
return epoch;
|
||||
}
|
||||
}
|
||||
|
||||
class ForecastTemperature {
|
||||
private String fahrenheit;
|
||||
private String celsius;
|
||||
|
||||
ForecastTemperature() {
|
||||
}
|
||||
|
||||
public String getFahrenheit() {
|
||||
return fahrenheit;
|
||||
}
|
||||
|
||||
public String getCelsius() {
|
||||
return celsius;
|
||||
}
|
||||
}
|
||||
|
||||
class ForecastPrecipitation {
|
||||
private BigDecimal in;
|
||||
private BigDecimal mm;
|
||||
private BigDecimal cm;
|
||||
|
||||
ForecastPrecipitation() {
|
||||
}
|
||||
|
||||
public BigDecimal getIn() {
|
||||
return in;
|
||||
}
|
||||
|
||||
public BigDecimal getMm() {
|
||||
return mm;
|
||||
}
|
||||
|
||||
public BigDecimal getCm() {
|
||||
return cm;
|
||||
}
|
||||
}
|
||||
|
||||
class ForecastWind {
|
||||
private BigDecimal mph;
|
||||
private BigDecimal kph;
|
||||
private String dir;
|
||||
private String degrees;
|
||||
|
||||
ForecastWind() {
|
||||
}
|
||||
|
||||
public BigDecimal getMph() {
|
||||
return mph;
|
||||
}
|
||||
|
||||
public BigDecimal getKph() {
|
||||
return kph;
|
||||
}
|
||||
|
||||
public String getDir() {
|
||||
return dir;
|
||||
}
|
||||
|
||||
public String getDegrees() {
|
||||
return degrees;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* 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.weatherunderground.internal.json;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* The {@link WeatherUndergroundJsonLocation} is the Java class used
|
||||
* to map the entry "location" from the JSON response to a Weather
|
||||
* Underground request.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
public class WeatherUndergroundJsonLocation {
|
||||
|
||||
// Commented members indicate properties returned by the API not used by the binding
|
||||
|
||||
private String type;
|
||||
private String country;
|
||||
private String country_iso3166;
|
||||
private String country_name;
|
||||
private String state;
|
||||
private String city;
|
||||
private String tz_short;
|
||||
private String tz_long;
|
||||
private String lat;
|
||||
private String lon;
|
||||
private String zip;
|
||||
private String magic;
|
||||
private String wmo;
|
||||
private String l;
|
||||
private String requesturl;
|
||||
private String wuiurl;
|
||||
// private Object nearby_weather_stations;
|
||||
|
||||
public WeatherUndergroundJsonLocation() {
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getCountry() {
|
||||
return country;
|
||||
}
|
||||
|
||||
public String getCountryIso3166() {
|
||||
return country_iso3166;
|
||||
}
|
||||
|
||||
public String getCountryName() {
|
||||
return country_name;
|
||||
}
|
||||
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public String getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
public String getTzShort() {
|
||||
return tz_short;
|
||||
}
|
||||
|
||||
public String getTzLong() {
|
||||
return tz_long;
|
||||
}
|
||||
|
||||
public String getLat() {
|
||||
return lat;
|
||||
}
|
||||
|
||||
public String getLon() {
|
||||
return lon;
|
||||
}
|
||||
|
||||
public String getZip() {
|
||||
return zip;
|
||||
}
|
||||
|
||||
public String getMagic() {
|
||||
return magic;
|
||||
}
|
||||
|
||||
public String getWmo() {
|
||||
return wmo;
|
||||
}
|
||||
|
||||
public String getL() {
|
||||
return l;
|
||||
}
|
||||
|
||||
public URL getRequesturl() {
|
||||
return WeatherUndergroundJsonUtils.getValidUrl(requesturl);
|
||||
}
|
||||
|
||||
public URL getWuiurl() {
|
||||
return WeatherUndergroundJsonUtils.getValidUrl(wuiurl);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* 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.weatherunderground.internal.json;
|
||||
|
||||
/**
|
||||
* The {@link WeatherUndergroundJsonResponse} is the Java class used
|
||||
* to map the entry "response" from the JSON response to a Weather Underground
|
||||
* request.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
public class WeatherUndergroundJsonResponse {
|
||||
|
||||
// Commented members indicate properties returned by the API not used by the binding
|
||||
|
||||
// private String version;
|
||||
// private String termsofService;
|
||||
// private Object features;
|
||||
private WeatherUndergroundJsonError error;
|
||||
|
||||
public WeatherUndergroundJsonResponse() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error type returned by the Weather Underground service
|
||||
*
|
||||
* @return the error type or null if no error
|
||||
*/
|
||||
public String getErrorType() {
|
||||
return (error == null) ? null : error.getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error description returned by the Weather Underground service
|
||||
*
|
||||
* @return the error description or null if no error
|
||||
*/
|
||||
public String getErrorDescription() {
|
||||
return (error == null) ? null : error.getDescription();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 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.weatherunderground.internal.json;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The {@link WeatherUndergroundJsonSimpleForecast} is the Java class used
|
||||
* to map the entry "forecast.simpleforecast" from the JSON response
|
||||
* to a Weather Underground request.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
public class WeatherUndergroundJsonSimpleForecast {
|
||||
|
||||
private List<WeatherUndergroundJsonForecastDay> forecastday;
|
||||
|
||||
public WeatherUndergroundJsonSimpleForecast() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link WeatherUndergroundJsonForecastDay} object for a given day
|
||||
*
|
||||
* @return the {@link WeatherUndergroundJsonForecastDay} object for the day
|
||||
*/
|
||||
public WeatherUndergroundJsonForecastDay getForecastDay(int day) {
|
||||
for (WeatherUndergroundJsonForecastDay forecast : forecastday) {
|
||||
if (forecast.getPeriod().intValue() == day) {
|
||||
return forecast;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* 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.weatherunderground.internal.json;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.time.DateTimeException;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link WeatherUndergroundJsonUtils} class contains utilities methods.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
public class WeatherUndergroundJsonUtils {
|
||||
|
||||
private static final String TREND_UP = "up";
|
||||
private static final String TREND_DOWN = "down";
|
||||
private static final String TREND_STABLE = "stable";
|
||||
|
||||
/**
|
||||
* Convert a string representing an Epoch value into a Calendar object
|
||||
*
|
||||
* @param value the Epoch value as a string
|
||||
*
|
||||
* @return the ZonedDateTime object representing the date and time of the Epoch
|
||||
* or null in case of conversion error
|
||||
*/
|
||||
public static ZonedDateTime convertToZonedDateTime(String value, ZoneId zoneId) {
|
||||
if (isValid(value)) {
|
||||
try {
|
||||
Instant epochSeconds = Instant.ofEpochSecond(Long.valueOf(value));
|
||||
return ZonedDateTime.ofInstant(epochSeconds, zoneId);
|
||||
} catch (DateTimeException e) {
|
||||
LoggerFactory.getLogger(WeatherUndergroundJsonUtils.class).debug("Cannot convert {} to ZonedDateTime",
|
||||
value);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string representing an integer value into an Integer object
|
||||
*
|
||||
* @param value the integer value as a string
|
||||
*
|
||||
* @return the Integer object representing the value or null in case of conversion error
|
||||
*/
|
||||
public static Integer convertToInteger(String value) {
|
||||
Integer result = null;
|
||||
if (isValid(value)) {
|
||||
try {
|
||||
result = Integer.valueOf(value.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
LoggerFactory.getLogger(WeatherUndergroundJsonUtils.class).debug("Cannot convert {} to Integer", value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string representing a decimal value into a BigDecimal object
|
||||
*
|
||||
* @param value the decimal value as a string
|
||||
*
|
||||
* @return the BigDecimal object representing the value or null in case of conversion error
|
||||
*/
|
||||
public static BigDecimal convertToBigDecimal(String value) {
|
||||
BigDecimal result = null;
|
||||
if (isValid(value)) {
|
||||
result = new BigDecimal(value.trim());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean isValid(String value) {
|
||||
return (value != null) && !value.isEmpty() && !value.equalsIgnoreCase("N/A") && !value.equalsIgnoreCase("NA")
|
||||
&& !value.equals("-") && !value.equals("--");
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string representing a URL into a URL object
|
||||
*
|
||||
* @param url the URL as a string
|
||||
*
|
||||
* @return the URL object representing the URL or null in case of invalid URL
|
||||
*/
|
||||
public static URL getValidUrl(String url) {
|
||||
URL validUrl = null;
|
||||
if (url != null && !url.trim().isEmpty()) {
|
||||
try {
|
||||
validUrl = new URL(url.trim());
|
||||
} catch (MalformedURLException e) {
|
||||
}
|
||||
}
|
||||
return validUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string representing a decimal value into a pressure trend constant
|
||||
*
|
||||
* @param value the decimal value as a string
|
||||
*
|
||||
* @return the pressure trend constant representing the value or null in case of conversion error
|
||||
*/
|
||||
public static String convertToTrend(String value) {
|
||||
String result = null;
|
||||
if (isValid(value)) {
|
||||
try {
|
||||
int val = Integer.valueOf(value.trim());
|
||||
if (val < 0) {
|
||||
result = TREND_DOWN;
|
||||
} else if (val > 0) {
|
||||
result = TREND_UP;
|
||||
} else {
|
||||
result = TREND_STABLE;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
LoggerFactory.getLogger(WeatherUndergroundJsonUtils.class).debug("Cannot convert {} to Integer", value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="weatherunderground" 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>WeatherUnderground Binding</name>
|
||||
<description>The Weather Underground Binding requests the Weather Underground Service to show weather data.</description>
|
||||
<author>Laurent Garnier</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,13 @@
|
||||
# Thing status description
|
||||
offline.comm-error-invalid-api-key = The API key is invalid.
|
||||
offline.comm-error-running-request = An error occurred while running the Weather Underground request.
|
||||
offline.comm-error-parsing-response = An error occurred while parsing the response to the Weather Underground request.
|
||||
offline.comm-error-response = An error was detected in the response to the Weather Underground request; please check the logs for more details.
|
||||
offline.conf-error-missing-apikey = The "API key" parameter must be configured.
|
||||
offline.conf-error-missing-location = The "location" parameter must be configured.
|
||||
offline.conf-error-syntax-language = The value of the "language" parameter must contain 2 letters.
|
||||
offline.conf-error-min-refresh = The minimum value of the "refresh interval" parameter must be 5 minutes.
|
||||
offline.uri-error = Could not create URI to fetch data, please check if the location parameter is correct.
|
||||
|
||||
# Discovery result
|
||||
discovery.weatherunderground.weather.local.label = Local Weather
|
||||
@@ -0,0 +1,138 @@
|
||||
# binding
|
||||
binding.weatherunderground.name = Extension WeatherUnderground
|
||||
binding.weatherunderground.description = L'extension Weather Underground interroge le service Weather Underground pour récupérer des données météo.
|
||||
|
||||
# thing types
|
||||
thing-type.weatherunderground.bridge.label = Connexion au service
|
||||
thing-type.weatherunderground.bridge.description = Repr<E9>sente une connexion au service Weather Underground.
|
||||
thing-type.weatherunderground.weather.label = Informations météo
|
||||
thing-type.weatherunderground.weather.description = Présente diverses données météo fournies par le service Weather Underground.
|
||||
|
||||
# thing type configuration
|
||||
thing-type.config.weatherunderground.bridge.apikey.label = Clé d'accès
|
||||
thing-type.config.weatherunderground.bridge.apikey.description = La clé d'accès au service Weather Underground.
|
||||
thing-type.config.weatherunderground.weather.location.label = Emplacement des données météo
|
||||
thing-type.config.weatherunderground.weather.location.description = Plusieurs syntaxes sont possibles. Merci de consulter la documentation de l'extension pour plus d'information.
|
||||
thing-type.config.weatherunderground.weather.language.label = Langue
|
||||
thing-type.config.weatherunderground.weather.language.description = La langue à utiliser par le service Weather Underground.
|
||||
thing-type.config.weatherunderground.weather.language.option.LI = Anglais britannique
|
||||
thing-type.config.weatherunderground.weather.language.option.NL = Néerlandais
|
||||
thing-type.config.weatherunderground.weather.language.option.EN = Anglais
|
||||
thing-type.config.weatherunderground.weather.language.option.FR = Français
|
||||
thing-type.config.weatherunderground.weather.language.option.FC = Français canadien
|
||||
thing-type.config.weatherunderground.weather.language.option.DL = Allemand
|
||||
thing-type.config.weatherunderground.weather.language.option.IT = Italien
|
||||
thing-type.config.weatherunderground.weather.language.option.BR = Portugais
|
||||
thing-type.config.weatherunderground.weather.language.option.RU = Russe
|
||||
thing-type.config.weatherunderground.weather.language.option.SP = Espagnol
|
||||
thing-type.config.weatherunderground.weather.refresh.label = Fréquence de rafraîchissement
|
||||
thing-type.config.weatherunderground.weather.refresh.description = La fréquence de rafraîchissement des données en minutes.
|
||||
|
||||
# channel group types
|
||||
channel-group-type.weatherunderground.current.label = Météo actuelle
|
||||
channel-group-type.weatherunderground.current.description = La météo actuelle.
|
||||
channel-group-type.weatherunderground.forecast.label = Météo prévue
|
||||
channel-group-type.weatherunderground.forecast.description = La météo prévue.
|
||||
|
||||
# channel groups
|
||||
thing-type.weatherunderground.weather.group.forecastToday.label = Météo d'aujourd'hui
|
||||
thing-type.weatherunderground.weather.group.forecastToday.description = La météo prévue aujourd'hui.
|
||||
thing-type.weatherunderground.weather.group.forecastTomorrow.label = Météo de demain
|
||||
thing-type.weatherunderground.weather.group.forecastTomorrow.description = La météo prévue demain.
|
||||
thing-type.weatherunderground.weather.group.forecastDay2.label = Météo dans 2 jours
|
||||
thing-type.weatherunderground.weather.group.forecastDay2.description = La météo prévue dans 2 jours.
|
||||
thing-type.weatherunderground.weather.group.forecastDay3.label = Météo dans 3 jours
|
||||
thing-type.weatherunderground.weather.group.forecastDay3.description = La météo prévue dans 3 jours.
|
||||
thing-type.weatherunderground.weather.group.forecastDay4.label = Météo dans 4 jours
|
||||
thing-type.weatherunderground.weather.group.forecastDay4.description = La météo prévue dans 4 jours.
|
||||
thing-type.weatherunderground.weather.group.forecastDay5.label = Météo dans 5 jours
|
||||
thing-type.weatherunderground.weather.group.forecastDay5.description = La météo prévue dans 5 jours.
|
||||
thing-type.weatherunderground.weather.group.forecastDay6.label = Météo dans 6 jours
|
||||
thing-type.weatherunderground.weather.group.forecastDay6.description = La météo prévue dans 6 jours.
|
||||
thing-type.weatherunderground.weather.group.forecastDay7.label = Météo dans 7 jours
|
||||
thing-type.weatherunderground.weather.group.forecastDay7.description = La météo prévue dans 7 jours.
|
||||
thing-type.weatherunderground.weather.group.forecastDay8.label = Météo dans 8 jours
|
||||
thing-type.weatherunderground.weather.group.forecastDay8.description = La météo prévue dans 8 jours.
|
||||
thing-type.weatherunderground.weather.group.forecastDay9.label = Météo dans 9 jours
|
||||
thing-type.weatherunderground.weather.group.forecastDay9.description = La météo prévue dans 9 jours.
|
||||
|
||||
# channel types
|
||||
channel-type.weatherunderground.location.label = Emplacement d'observation
|
||||
channel-type.weatherunderground.location.description = L'emplacement d'observation de la météo.
|
||||
channel-type.weatherunderground.stationId.label = ID station
|
||||
channel-type.weatherunderground.stationId.description = L'identifiant de la station météo.
|
||||
channel-type.weatherunderground.observationTime.label = Heure d'observation
|
||||
channel-type.weatherunderground.observationTime.description = La date et l'heure d'observation de la météo.
|
||||
channel-type.weatherunderground.forecastTime.label = Heure des prévisions
|
||||
channel-type.weatherunderground.forecastTime.description = La date et l'heure des prévisions météo.
|
||||
channel-type.weatherunderground.currentConditions.label = Conditions actuelles
|
||||
channel-type.weatherunderground.currentConditions.description = Les conditions météo actuelles.
|
||||
channel-type.weatherunderground.forecastConditions.label = Conditions prévues
|
||||
channel-type.weatherunderground.forecastConditions.description = Les conditions météo prévues.
|
||||
channel-type.weatherunderground.minTemperature.label = Température minimale
|
||||
channel-type.weatherunderground.minTemperature.description = La température minimale.
|
||||
channel-type.weatherunderground.maxTemperature.label = Température maximale
|
||||
channel-type.weatherunderground.maxTemperature.description = La température maximale.
|
||||
channel-type.weatherunderground.dewPoint.label = Température du point de rosée
|
||||
channel-type.weatherunderground.dewPoint.description = La température du point de rosée.
|
||||
channel-type.weatherunderground.heatIndex.label = Indice de chaleur
|
||||
channel-type.weatherunderground.heatIndex.description = L'indice de chaleur.
|
||||
channel-type.weatherunderground.windChill.label = Température de refroidissement du vent
|
||||
channel-type.weatherunderground.windChill.description = La température de refroidissement du vent.
|
||||
channel-type.weatherunderground.feelingTemperature.label = Température ressentie
|
||||
channel-type.weatherunderground.feelingTemperature.description = La température ressentie.
|
||||
channel-type.weatherunderground.relativeHumidity.label = Humidité relative
|
||||
channel-type.weatherunderground.relativeHumidity.description = l'humidité relative prévue.
|
||||
channel-type.weatherunderground.windDirection.label = Direction du vent
|
||||
channel-type.weatherunderground.windDirection.description = La direction du vent.
|
||||
channel-type.weatherunderground.maxWindDirection.label = Direction du vent max
|
||||
channel-type.weatherunderground.maxWindDirection.description = La direction du vent le plus fort.
|
||||
channel-type.weatherunderground.averageWindDirection.label = Direction moyenne du vent
|
||||
channel-type.weatherunderground.averageWindDirection.description = La direction moyenne du vent.
|
||||
channel-type.weatherunderground.maxWindDirection-degrees.label = Direction du vent max (angle)
|
||||
channel-type.weatherunderground.maxWindDirection-degrees.description = La direction du vent le plus fort sous la forme d'un angle.
|
||||
channel-type.weatherunderground.averageWindDirection-degrees.label = Direction moyenne du vent (angle)
|
||||
channel-type.weatherunderground.averageWindDirection-degrees.description = La direction moyenne du vent sous la forme d'un angle.
|
||||
channel-type.weatherunderground.maxWindSpeed.label = Vitesse max du vent
|
||||
channel-type.weatherunderground.maxWindSpeed.description = La vitesse maximum du vent.
|
||||
channel-type.weatherunderground.averageWindSpeed.label = Vitesse moyenne du vent
|
||||
channel-type.weatherunderground.averageWindSpeed.description = La vitesse moyenne du vent.
|
||||
channel-type.weatherunderground.windGust.label = Vitesse du vent en rafale
|
||||
channel-type.weatherunderground.windGust.description = La vitesse du vent en rafale.
|
||||
channel-type.weatherunderground.pressureTrend.label = Tendance pression atmosphérique
|
||||
channel-type.weatherunderground.pressureTrend.description = Tendance d'évolution de la pression atmosphérique
|
||||
channel-type.weatherunderground.pressureTrend.state.option.up = A la hausse
|
||||
channel-type.weatherunderground.pressureTrend.state.option.stable = Stable
|
||||
channel-type.weatherunderground.pressureTrend.state.option.down = A la baisse
|
||||
channel-type.weatherunderground.visibility.label = Distance de visibilité
|
||||
channel-type.weatherunderground.visibility.description = La distance de visibilité.
|
||||
channel-type.weatherunderground.solarRadiation.label = Rayonnement solaire
|
||||
channel-type.weatherunderground.solarRadiation.description = L'énergie rayonnante émise par le soleil.
|
||||
channel-type.weatherunderground.UVIndex.label = Indice UV
|
||||
channel-type.weatherunderground.UVIndex.description = L'indice UV.
|
||||
channel-type.weatherunderground.rainDay.label = Chutes de pluie dans la journée
|
||||
channel-type.weatherunderground.rainDay.description = Les chutes de pluie sur toute la journée.
|
||||
channel-type.weatherunderground.rainHour.label = Chutes de pluie dans l'heure
|
||||
channel-type.weatherunderground.rainHour.description = Les chutes de pluie sur la dernière heure.
|
||||
channel-type.weatherunderground.snow.label = Chutes de neige dans la journée
|
||||
channel-type.weatherunderground.snow.description = Les chutes de neige sur toute la journée.
|
||||
channel-type.weatherunderground.probaPrecipitation.label = Probabilité de précipitation
|
||||
channel-type.weatherunderground.probaPrecipitation.description = La probabilité de précipitation.
|
||||
channel-type.weatherunderground.icon.label = Icône météo
|
||||
channel-type.weatherunderground.icon.description = L'icône représentant les conditions météo.
|
||||
channel-type.weatherunderground.iconKey.label = Clé de l'icône météo
|
||||
channel-type.weatherunderground.iconKey.description = La clé utilisée dans l'URL de l'icône météo.
|
||||
|
||||
# Thing status description
|
||||
offline.comm-error-invalid-api-key = La clé d''accès est invalide.
|
||||
offline.comm-error-running-request = Une erreur est survenue lors de l''exécution de la requête Weather Underground.
|
||||
offline.comm-error-parsing-response = Une erreur est survenue lors de l''analyse de la réponse à la requête Weather Underground.
|
||||
offline.comm-error-response = Une erreur a été détectée dans la réponse à la requête Weather Underground; merci de consulter les logs pour plus de détails.
|
||||
offline.conf-error-missing-apikey = Le paramètre "clé d''accès" doit être configuré.
|
||||
offline.conf-error-missing-location = Le paramètre "emplacement des données météo" doit être configuré.
|
||||
offline.conf-error-syntax-language = La valeur du paramètre "Langue" doit contenir 2 lettres.
|
||||
offline.conf-error-min-refresh = La valeur minimale du paramètre "fréquence de rafraîchissement" doit être de 5 minutes.
|
||||
offline.uri-error = Echec création de l'URI de récupération des données; merci de vérifier la syntaxe du paramètre "emplacement des données météo".
|
||||
|
||||
# Discovery result
|
||||
discovery.weatherunderground.weather.local.label = Météo locale
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="weatherunderground"
|
||||
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">
|
||||
<!-- Weatherunderground Bridge -->
|
||||
<bridge-type id="bridge">
|
||||
<label>Weatherunderground Bridge</label>
|
||||
<description>The Weather Underground bridge represents a connection to the Weather Underground service</description>
|
||||
|
||||
<config-description>
|
||||
<parameter name="apikey" type="text" required="true">
|
||||
<context>password</context>
|
||||
<label>API Key</label>
|
||||
<description>API key to access the Weather Underground service</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</bridge-type>
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,459 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="weatherunderground"
|
||||
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">
|
||||
|
||||
<!-- WeatherUnderground Binding -->
|
||||
<thing-type id="weather">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="bridge"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>Weather Information</label>
|
||||
<description>Provides various weather data from the Weather Underground service</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="current" typeId="current"/>
|
||||
<channel-group id="forecastToday" typeId="forecast">
|
||||
<label>Weather Forecast Today</label>
|
||||
<description>This is the weather forecast for today</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastTomorrow" typeId="forecast">
|
||||
<label>Weather Forecast Tomorrow</label>
|
||||
<description>This is the weather forecast for tomorrow</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay2" typeId="forecast">
|
||||
<label>Weather Forecast Day 2</label>
|
||||
<description>This is the weather forecast in two days</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay3" typeId="forecast">
|
||||
<label>Weather Forecast Day 3</label>
|
||||
<description>This is the weather forecast in three days</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay4" typeId="forecast">
|
||||
<label>Weather Forecast Day 4</label>
|
||||
<description>This is the weather forecast in four days</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay5" typeId="forecast">
|
||||
<label>Weather Forecast Day 5</label>
|
||||
<description>This is the weather forecast in five days</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay6" typeId="forecast">
|
||||
<label>Weather Forecast Day 6</label>
|
||||
<description>This is the weather forecast in six days</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay7" typeId="forecast">
|
||||
<label>Weather Forecast Day 7</label>
|
||||
<description>This is the weather forecast in seven days</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay8" typeId="forecast">
|
||||
<label>Weather Forecast Day 8</label>
|
||||
<description>This is the weather forecast in eight days</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay9" typeId="forecast">
|
||||
<label>Weather Forecast Day 9</label>
|
||||
<description>This is the weather forecast in nine days</description>
|
||||
</channel-group>
|
||||
</channel-groups>
|
||||
|
||||
<config-description>
|
||||
<parameter name="location" type="text" required="true">
|
||||
<label>Location of Weather Information</label>
|
||||
<description>Multiple syntaxes are supported. Please read the binding documentation for more information</description>
|
||||
</parameter>
|
||||
<parameter name="language" type="text" required="false">
|
||||
<label>Language</label>
|
||||
<description>Language to be used by the Weather Underground service</description>
|
||||
<options>
|
||||
<option value="AF">Afrikaans</option>
|
||||
<option value="AL">Albanian</option>
|
||||
<option value="AR">Arabic</option>
|
||||
<option value="HY">Armenian</option>
|
||||
<option value="AZ">Azerbaijan</option>
|
||||
<option value="EU">Basque</option>
|
||||
<option value="BY">Belarusian</option>
|
||||
<option value="BU">Bulgarian</option>
|
||||
<option value="LI">British English</option>
|
||||
<option value="MY">Burmese</option>
|
||||
<option value="CA">Catalan</option>
|
||||
<option value="CN">Chinese - Simplified</option>
|
||||
<option value="TW">Chinese - Traditional</option>
|
||||
<option value="CR">Croatian</option>
|
||||
<option value="CZ">Czech</option>
|
||||
<option value="DK">Danish</option>
|
||||
<option value="DV">Dhivehi</option>
|
||||
<option value="NL">Dutch</option>
|
||||
<option value="EN">English</option>
|
||||
<option value="EO">Esperanto</option>
|
||||
<option value="ET">Estonian</option>
|
||||
<option value="FA">Farsi</option>
|
||||
<option value="FI">Finnish</option>
|
||||
<option value="FR">French</option>
|
||||
<option value="FC">French Canadian</option>
|
||||
<option value="GZ">Galician</option>
|
||||
<option value="DL">German</option>
|
||||
<option value="KA">Georgian</option>
|
||||
<option value="GR">Greek</option>
|
||||
<option value="GU">Gujarati</option>
|
||||
<option value="HT">Haitian Creole</option>
|
||||
<option value="IL">Hebrew</option>
|
||||
<option value="HI">Hindi</option>
|
||||
<option value="HU">Hungarian</option>
|
||||
<option value="IS">Icelandic</option>
|
||||
<option value="IO">Ido</option>
|
||||
<option value="ID">Indonesian</option>
|
||||
<option value="IR">Irish Gaelic</option>
|
||||
<option value="IT">Italian</option>
|
||||
<option value="JP">Japanese</option>
|
||||
<option value="JW">Javanese</option>
|
||||
<option value="KM">Khmer</option>
|
||||
<option value="KR">Korean</option>
|
||||
<option value="KU">Kurdish</option>
|
||||
<option value="LA">Latin</option>
|
||||
<option value="LV">Latvian</option>
|
||||
<option value="LT">Lithuanian</option>
|
||||
<option value="ND">Low German</option>
|
||||
<option value="MK">Macedonian</option>
|
||||
<option value="MT">Maltese</option>
|
||||
<option value="GM">Mandinka</option>
|
||||
<option value="MI">Maori</option>
|
||||
<option value="MR">Marathi</option>
|
||||
<option value="MN">Mongolian</option>
|
||||
<option value="NO">Norwegian</option>
|
||||
<option value="OC">Occitan</option>
|
||||
<option value="PS">Pashto</option>
|
||||
<option value="GN">Plautdietsch</option>
|
||||
<option value="PL">Polish</option>
|
||||
<option value="BR">Portuguese</option>
|
||||
<option value="PA">Punjabi</option>
|
||||
<option value="RO">Romanian</option>
|
||||
<option value="RU">Russian</option>
|
||||
<option value="SR">Serbian</option>
|
||||
<option value="SK">Slovak</option>
|
||||
<option value="SL">Slovenian</option>
|
||||
<option value="SP">Spanish</option>
|
||||
<option value="SI">Swahili</option>
|
||||
<option value="SW">Swedish</option>
|
||||
<option value="CH">Swiss</option>
|
||||
<option value="TL">Tagalog</option>
|
||||
<option value="TT">Tatarish</option>
|
||||
<option value="TH">Thai</option>
|
||||
<option value="TR">Turkish</option>
|
||||
<option value="TK">Turkmen</option>
|
||||
<option value="UA">Ukrainian</option>
|
||||
<option value="UZ">Uzbek</option>
|
||||
<option value="VU">Vietnamese</option>
|
||||
<option value="CY">Welsh</option>
|
||||
<option value="SN">Wolof</option>
|
||||
<option value="JI">Yiddish - transliterated</option>
|
||||
<option value="YI">Yiddish - unicode</option>
|
||||
</options>
|
||||
</parameter>
|
||||
<parameter name="refresh" type="integer" min="5" required="false" unit="min">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Specifies the refresh interval in minutes.</description>
|
||||
<default>30</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<channel-group-type id="current">
|
||||
<label>Current Weather</label>
|
||||
<description>This is the current weather</description>
|
||||
<channels>
|
||||
<channel id="location" typeId="location"/>
|
||||
<channel id="stationId" typeId="stationId"/>
|
||||
<channel id="observationTime" typeId="observationTime"/>
|
||||
<channel id="conditions" typeId="currentConditions"/>
|
||||
<channel id="temperature" typeId="system.outdoor-temperature"/>
|
||||
<channel id="relativeHumidity" typeId="system.atmospheric-humidity"/>
|
||||
<channel id="windDirection" typeId="windDirection"/>
|
||||
<channel id="windDirectionDegrees" typeId="system.wind-direction"/>
|
||||
<channel id="windSpeed" typeId="system.wind-speed"/>
|
||||
<channel id="windGust" typeId="windGust"/>
|
||||
<channel id="pressure" typeId="system.barometric-pressure"/>
|
||||
<channel id="pressureTrend" typeId="pressureTrend"/>
|
||||
<channel id="dewPoint" typeId="dewPoint"/>
|
||||
<channel id="heatIndex" typeId="heatIndex"/>
|
||||
<channel id="windChill" typeId="windChill"/>
|
||||
<channel id="feelingTemperature" typeId="feelingTemperature"/>
|
||||
<channel id="visibility" typeId="visibility"/>
|
||||
<channel id="solarRadiation" typeId="solarRadiation"/>
|
||||
<channel id="UVIndex" typeId="UVIndex"/>
|
||||
<channel id="precipitationDay" typeId="rainDay"/>
|
||||
<channel id="precipitationHour" typeId="rainHour"/>
|
||||
<channel id="icon" typeId="icon"/>
|
||||
<channel id="iconKey" typeId="iconKey"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="forecast">
|
||||
<label>Weather Forecast</label>
|
||||
<description>This is the weather forecast</description>
|
||||
<channels>
|
||||
<channel id="forecastTime" typeId="forecastTime"/>
|
||||
<channel id="conditions" typeId="forecastConditions"/>
|
||||
<channel id="minTemperature" typeId="minTemperature"/>
|
||||
<channel id="maxTemperature" typeId="maxTemperature"/>
|
||||
<channel id="relativeHumidity" typeId="relativeHumidity"/>
|
||||
<channel id="probaPrecipitation" typeId="probaPrecipitation"/>
|
||||
<channel id="precipitationDay" typeId="rainDay"/>
|
||||
<channel id="snow" typeId="snow"/>
|
||||
<channel id="maxWindDirection" typeId="maxWindDirection"/>
|
||||
<channel id="maxWindDirectionDegrees" typeId="maxWindDirection-degrees"/>
|
||||
<channel id="maxWindSpeed" typeId="maxWindSpeed"/>
|
||||
<channel id="averageWindDirection" typeId="averageWindDirection"/>
|
||||
<channel id="averageWindDirectionDegrees" typeId="averageWindDirection-degrees"/>
|
||||
<channel id="averageWindSpeed" typeId="averageWindSpeed"/>
|
||||
<channel id="icon" typeId="icon"/>
|
||||
<channel id="iconKey" typeId="iconKey"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-type id="location" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Observation Location</label>
|
||||
<description>Weather observation location</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="stationId" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Station Id</label>
|
||||
<description>Weather station identifier</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="observationTime" advanced="true">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Observation Time</label>
|
||||
<description>Observation date and time</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="forecastTime" advanced="true">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Forecast Time</label>
|
||||
<description>Forecast date and time</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="currentConditions">
|
||||
<item-type>String</item-type>
|
||||
<label>Current Conditions</label>
|
||||
<description>Weather current conditions</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="forecastConditions">
|
||||
<item-type>String</item-type>
|
||||
<label>Forecast Conditions</label>
|
||||
<description>Weather forecast conditions</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="minTemperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Minimum Temperature</label>
|
||||
<description>Minimum temperature</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="maxTemperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Maximum Temperature</label>
|
||||
<description>Maximum temperature</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="dewPoint" advanced="true">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Dew Point Temperature</label>
|
||||
<description>Dew Point temperature</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heatIndex" advanced="true">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Heat Index</label>
|
||||
<description>Heat index</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windChill" advanced="true">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Wind Chill Temperature</label>
|
||||
<description>Wind chill temperature</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="feelingTemperature" advanced="true">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Feeling Temperature</label>
|
||||
<description>Feeling temperature</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="relativeHumidity">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Relative Humidity</label>
|
||||
<description>Forecast relative humidity</description>
|
||||
<category>Humidity</category>
|
||||
<state readOnly="true" min="0" max="100" pattern="%d %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windDirection">
|
||||
<item-type>String</item-type>
|
||||
<label>Wind Direction</label>
|
||||
<description>Wind direction</description>
|
||||
<state readOnly="true" pattern="%s">
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="maxWindDirection" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Maximum Wind Direction</label>
|
||||
<description>Maximum wind direction</description>
|
||||
<state readOnly="true" pattern="%s">
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="averageWindDirection" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Average Wind Direction</label>
|
||||
<description>Average wind direction</description>
|
||||
<state readOnly="true" pattern="%s">
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="maxWindDirection-degrees" advanced="true">
|
||||
<item-type>Number:Angle</item-type>
|
||||
<label>Maximum Wind Direction (angle)</label>
|
||||
<description>Maximum wind direction as an angle</description>
|
||||
<category>Wind</category>
|
||||
<state readOnly="true" min="0" max="360" pattern="%.0f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="averageWindDirection-degrees" advanced="true">
|
||||
<item-type>Number:Angle</item-type>
|
||||
<label>Average Wind Direction (angle)</label>
|
||||
<description>Average wind direction as an angle</description>
|
||||
<category>Wind</category>
|
||||
<state readOnly="true" min="0" max="360" pattern="%.0f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="maxWindSpeed">
|
||||
<item-type>Number:Speed</item-type>
|
||||
<label>Maximum Wind Speed</label>
|
||||
<description>Maximum wind speed</description>
|
||||
<category>Wind</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="averageWindSpeed">
|
||||
<item-type>Number:Speed</item-type>
|
||||
<label>Average Wind Speed</label>
|
||||
<description>Average wind speed</description>
|
||||
<category>Wind</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windGust">
|
||||
<item-type>Number:Speed</item-type>
|
||||
<label>Wind Gust</label>
|
||||
<description>Wind gust</description>
|
||||
<category>Wind</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="pressureTrend" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Pressure Trend</label>
|
||||
<description>Pressure evolution trend (up, down, stable)</description>
|
||||
<category>Pressure</category>
|
||||
<state readOnly="true" pattern="%s">
|
||||
<options>
|
||||
<option value="up">up</option>
|
||||
<option value="stable">stable</option>
|
||||
<option value="down">down</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="visibility" advanced="true">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Visibility</label>
|
||||
<description>Visibility</description>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="solarRadiation" advanced="true">
|
||||
<item-type>Number:Intensity</item-type>
|
||||
<label>Solar Radiation</label>
|
||||
<description>Solar radiation</description>
|
||||
<state readOnly="true" pattern="%.2f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="UVIndex" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>UV Index</label>
|
||||
<description>UV Index</description>
|
||||
<state readOnly="true" pattern="%.1f">
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="rainDay">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Rain Fall Day</label>
|
||||
<description>Rain fall during the day</description>
|
||||
<category>Rain</category>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="rainHour">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Rain Fall Hour</label>
|
||||
<description>Rain fall during the last hour</description>
|
||||
<category>Rain</category>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="snow">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Snow Fall</label>
|
||||
<description>Snow fall</description>
|
||||
<category>Rain</category>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="probaPrecipitation">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Precipitation Probability</label>
|
||||
<description>Probability of precipitation</description>
|
||||
<state readOnly="true" min="0" max="100" pattern="%d %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="icon" advanced="true">
|
||||
<item-type>Image</item-type>
|
||||
<label>Weather Icon</label>
|
||||
<description>Icon representing the weather conditions</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="iconKey" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Weather Icon Key</label>
|
||||
<description>Key used in the icon URL</description>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user