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.meteoblue-${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-meteoblue" description="meteoblue Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.meteoblue/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,563 @@
|
||||
/**
|
||||
* 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.meteoblue.internal;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Image;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.openhab.binding.meteoblue.internal.json.JsonDataDay;
|
||||
import org.openhab.binding.meteoblue.internal.json.JsonMetadata;
|
||||
import org.openhab.binding.meteoblue.internal.json.JsonUnits;
|
||||
import org.openhab.core.config.core.ConfigConstants;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Model of forecast data.
|
||||
*
|
||||
* @author Chris Carman - Initial contribution
|
||||
*/
|
||||
public class Forecast {
|
||||
private final Logger logger = LoggerFactory.getLogger(Forecast.class);
|
||||
|
||||
// metadata fields
|
||||
private Double latitude;
|
||||
private Double longitude;
|
||||
private Integer height;
|
||||
private String timeZoneAbbreviation;
|
||||
|
||||
// units fields
|
||||
private String timeUnits;
|
||||
private String predictabilityUnits;
|
||||
private String precipitationProbabilityUnits;
|
||||
private String pressureUnits;
|
||||
private String relativeHumidityUnits;
|
||||
private String temperatureUnits;
|
||||
private String windDirectionUnits;
|
||||
private String precipitationUnits;
|
||||
private String windSpeedUnits;
|
||||
|
||||
// data_day fields
|
||||
private Calendar forecastDate;
|
||||
private Integer pictocode;
|
||||
private Integer UVIndex;
|
||||
private Double minTemperature;
|
||||
private Double maxTemperature;
|
||||
private Double meanTemperature;
|
||||
private Double feltTemperatureMin;
|
||||
private Double feltTemperatureMax;
|
||||
private Integer windDirection;
|
||||
private Integer precipitationProbability;
|
||||
private String rainSpot;
|
||||
private Integer predictabilityClass;
|
||||
private Integer predictability;
|
||||
private Double precipitation;
|
||||
private Double snowFraction;
|
||||
private Integer minSeaLevelPressure;
|
||||
private Integer maxSeaLevelPressure;
|
||||
private Integer meanSeaLevelPressure;
|
||||
private Double minWindSpeed;
|
||||
private Double maxWindSpeed;
|
||||
private Double meanWindSpeed;
|
||||
private Integer relativeHumidityMin;
|
||||
private Integer relativeHumidityMax;
|
||||
private Integer relativeHumidityMean;
|
||||
private Double convectivePrecipitation;
|
||||
private Double precipitationHours;
|
||||
private Double humidityGreater90Hours;
|
||||
|
||||
// derived fields
|
||||
private String cardinalWindDirection;
|
||||
private String iconName;
|
||||
private Image icon;
|
||||
private Image rainArea;
|
||||
private Double snowFall;
|
||||
|
||||
public Forecast(int whichDay, JsonMetadata metadata, JsonUnits units, JsonDataDay dataDay)
|
||||
throws IllegalArgumentException {
|
||||
if (metadata == null) {
|
||||
throw new IllegalArgumentException("Received no metadata information.");
|
||||
}
|
||||
if (dataDay == null) {
|
||||
throw new IllegalArgumentException("Received no data_day information.");
|
||||
}
|
||||
if (whichDay > dataDay.getTime().length - 1) {
|
||||
throw new IndexOutOfBoundsException("No data received for day " + whichDay);
|
||||
}
|
||||
|
||||
// extract the metadata fields
|
||||
latitude = metadata.getLatitude();
|
||||
longitude = metadata.getLongitude();
|
||||
height = metadata.getHeight();
|
||||
timeZoneAbbreviation = metadata.getTimeZoneAbbreviation();
|
||||
|
||||
logger.trace("Metadata:");
|
||||
logger.trace(" Latitude: {}", latitude);
|
||||
logger.trace(" Longitude: {}", longitude);
|
||||
logger.trace(" Height: {}", height);
|
||||
logger.trace(" TZ Abbrev: {}", timeZoneAbbreviation);
|
||||
|
||||
// extract the units fields
|
||||
timeUnits = units.getTime();
|
||||
predictabilityUnits = units.getPredictability();
|
||||
precipitationProbabilityUnits = units.getPrecipitationProbability();
|
||||
pressureUnits = units.getPressure();
|
||||
relativeHumidityUnits = units.getRelativeHumidity();
|
||||
temperatureUnits = units.getTemperature();
|
||||
windDirectionUnits = units.getWindDirection();
|
||||
precipitationUnits = units.getPrecipitation();
|
||||
windSpeedUnits = units.getWindSpeed();
|
||||
|
||||
logger.trace("Units:");
|
||||
logger.trace(" Time: {}", timeUnits);
|
||||
logger.trace(" Predictability: {}", predictabilityUnits);
|
||||
logger.trace(" Precipitation Probability: {}", precipitationProbabilityUnits);
|
||||
logger.trace(" Pressure: {}", pressureUnits);
|
||||
logger.trace(" Relative Humidity: {}", relativeHumidityUnits);
|
||||
logger.trace(" Temperature: {}", temperatureUnits);
|
||||
logger.trace(" Wind Direction: {}", windDirectionUnits);
|
||||
logger.trace(" Precipitation: {}", precipitationUnits);
|
||||
logger.trace(" Wind Speed: {}", windSpeedUnits);
|
||||
|
||||
// extract the data_day fields
|
||||
String timeString = dataDay.getTime()[whichDay];
|
||||
try {
|
||||
Calendar c = Calendar.getInstance(TimeZone.getTimeZone(timeZoneAbbreviation));
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
|
||||
c.setTime(formatter.parse(timeString));
|
||||
forecastDate = c;
|
||||
} catch (ParseException e) {
|
||||
logger.debug("Failed to parse the value '{}' as a date.", timeString);
|
||||
}
|
||||
|
||||
pictocode = dataDay.getPictocode()[whichDay];
|
||||
iconName = getIconNameForPictocode(pictocode);
|
||||
icon = loadImageIcon(iconName);
|
||||
UVIndex = dataDay.getUVIndex()[whichDay];
|
||||
maxTemperature = dataDay.getTemperatureMax()[whichDay];
|
||||
minTemperature = dataDay.getTemperatureMin()[whichDay];
|
||||
meanTemperature = dataDay.getTemperatureMean()[whichDay];
|
||||
feltTemperatureMax = dataDay.getFeltTemperatureMax()[whichDay];
|
||||
feltTemperatureMin = dataDay.getFeltTemperatureMin()[whichDay];
|
||||
windDirection = dataDay.getWindDirection()[whichDay];
|
||||
cardinalWindDirection = getCardinalDirection(windDirection);
|
||||
precipitationProbability = dataDay.getPrecipitationProbability()[whichDay];
|
||||
rainSpot = dataDay.getRainspot()[whichDay];
|
||||
rainArea = generateRainAreaImage();
|
||||
predictabilityClass = dataDay.getPredictabilityClass()[whichDay];
|
||||
predictability = dataDay.getPredictability()[whichDay];
|
||||
precipitation = dataDay.getPrecipitation()[whichDay];
|
||||
snowFraction = dataDay.getSnowFraction()[whichDay];
|
||||
maxSeaLevelPressure = dataDay.getSeaLevelPressureMax()[whichDay];
|
||||
minSeaLevelPressure = dataDay.getSeaLevelPressureMin()[whichDay];
|
||||
meanSeaLevelPressure = dataDay.getSeaLevelPressureMean()[whichDay];
|
||||
maxWindSpeed = dataDay.getWindSpeedMax()[whichDay];
|
||||
meanWindSpeed = dataDay.getWindSpeedMean()[whichDay];
|
||||
minWindSpeed = dataDay.getWindSpeedMin()[whichDay];
|
||||
relativeHumidityMax = dataDay.getRelativeHumidityMax()[whichDay];
|
||||
relativeHumidityMin = dataDay.getRelativeHumidityMin()[whichDay];
|
||||
relativeHumidityMean = dataDay.getRelativeHumidityMean()[whichDay];
|
||||
convectivePrecipitation = dataDay.getConvectivePrecipitation()[whichDay];
|
||||
snowFall = snowFraction * precipitation;
|
||||
precipitationHours = dataDay.getPrecipitationHours()[whichDay];
|
||||
humidityGreater90Hours = dataDay.getHumidityGreater90Hours()[whichDay];
|
||||
|
||||
logger.trace("DataDay:");
|
||||
logger.trace(" Time: {}", forecastDate);
|
||||
logger.trace(" Pictocode: {}", pictocode);
|
||||
logger.trace(" Icon: {}", icon);
|
||||
logger.trace(" UV Index: {}", UVIndex);
|
||||
logger.trace(" Max Temperature: {}", maxTemperature);
|
||||
logger.trace(" Min Temperature: {}", minTemperature);
|
||||
logger.trace(" Mean Temperature: {}", meanTemperature);
|
||||
logger.trace(" Felt Temperature Max: {}", feltTemperatureMax);
|
||||
logger.trace(" Felt Temperature Min: {}", feltTemperatureMin);
|
||||
logger.trace(" Wind Direction: {}", windDirection);
|
||||
logger.trace(" Precipitation Probability: {}", precipitationProbability);
|
||||
logger.trace(" Rainspot: {}", rainSpot);
|
||||
logger.trace(" Rain Area: {}", rainArea);
|
||||
logger.trace(" Predictability Class: {}", predictabilityClass);
|
||||
logger.trace(" Predictability: {}", predictability);
|
||||
logger.trace(" Precipitation: {}", precipitation);
|
||||
logger.trace(" Snow Fraction: {}", snowFraction);
|
||||
logger.trace(" Max Sea-level Pressure: {}", maxSeaLevelPressure);
|
||||
logger.trace(" Min Sea-level Pressure: {}", minSeaLevelPressure);
|
||||
logger.trace(" Mean Sea-level Pressure: {}", meanSeaLevelPressure);
|
||||
logger.trace(" Max Wind Speed: {}", maxWindSpeed);
|
||||
logger.trace(" Mean Wind Speed: {}", meanWindSpeed);
|
||||
logger.trace(" Min Wind Speed: {}", minWindSpeed);
|
||||
logger.trace(" Relative Humidity Max: {}", relativeHumidityMax);
|
||||
logger.trace(" Relative Humidity Min: {}", relativeHumidityMin);
|
||||
logger.trace(" Relative Humidity Mean: {}", relativeHumidityMean);
|
||||
logger.trace(" Convective Precipitation: {}", convectivePrecipitation);
|
||||
logger.trace(" Snowfall: {}", snowFall);
|
||||
logger.trace(" Precipitation Hours: {}", precipitationHours);
|
||||
logger.trace(" Humidity > 90 Hours: {}", humidityGreater90Hours);
|
||||
}
|
||||
|
||||
// generic getter
|
||||
public Object getDatapoint(String datapointName) {
|
||||
if (datapointName.equals("condition")) {
|
||||
return String.valueOf(pictocode);
|
||||
}
|
||||
|
||||
try {
|
||||
Field field = getClass().getDeclaredField(datapointName);
|
||||
field.setAccessible(true);
|
||||
return field.get(this);
|
||||
} catch (NoSuchFieldException e) {
|
||||
logger.warn("Unable to find a datapoint declared for the name '{}'", datapointName);
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
logger.warn("An unexpected error occurred while trying to access the datapoint '{}'", datapointName, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// metadata getters
|
||||
public Integer getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
// units getters
|
||||
public String getTimeUnits() {
|
||||
return timeUnits;
|
||||
}
|
||||
|
||||
public String getPredictabilityUnits() {
|
||||
return predictabilityUnits;
|
||||
}
|
||||
|
||||
public String getPrecipitationProbabilityUnits() {
|
||||
return precipitationProbabilityUnits;
|
||||
}
|
||||
|
||||
public String getPressureUnits() {
|
||||
return pressureUnits;
|
||||
}
|
||||
|
||||
public String getRelativeHumidityUnits() {
|
||||
return relativeHumidityUnits;
|
||||
}
|
||||
|
||||
public String getTemperatureUnits() {
|
||||
return temperatureUnits;
|
||||
}
|
||||
|
||||
public String getWindDirectionUnits() {
|
||||
return windDirectionUnits;
|
||||
}
|
||||
|
||||
public String getPrecipitationUnits() {
|
||||
return precipitationUnits;
|
||||
}
|
||||
|
||||
public String getWindSpeedUnits() {
|
||||
return windSpeedUnits;
|
||||
}
|
||||
|
||||
// data_day getters
|
||||
public Calendar getForecastDate() {
|
||||
return forecastDate;
|
||||
}
|
||||
|
||||
public Integer getPictocode() {
|
||||
return pictocode;
|
||||
}
|
||||
|
||||
public Integer getUVIndex() {
|
||||
return UVIndex;
|
||||
}
|
||||
|
||||
public Double getTemperatureMin() {
|
||||
return minTemperature;
|
||||
}
|
||||
|
||||
public Double getTemperatureMax() {
|
||||
return maxTemperature;
|
||||
}
|
||||
|
||||
public Double getTemperatureMean() {
|
||||
return meanTemperature;
|
||||
}
|
||||
|
||||
public Double getFeltTemperatureMin() {
|
||||
return feltTemperatureMin;
|
||||
}
|
||||
|
||||
public Double feltTemperatureMax() {
|
||||
return feltTemperatureMax;
|
||||
}
|
||||
|
||||
public Integer getWindDirection() {
|
||||
return windDirection;
|
||||
}
|
||||
|
||||
public String getCardinalWindDirection() {
|
||||
return cardinalWindDirection;
|
||||
}
|
||||
|
||||
public Integer getPrecipitationProbability() {
|
||||
return precipitationProbability;
|
||||
}
|
||||
|
||||
public String getRainSpot() {
|
||||
return rainSpot;
|
||||
}
|
||||
|
||||
public Image getRainArea() {
|
||||
return rainArea;
|
||||
}
|
||||
|
||||
public Integer getPredictabilityClass() {
|
||||
return predictabilityClass;
|
||||
}
|
||||
|
||||
public Integer getPredictability() {
|
||||
return predictability;
|
||||
}
|
||||
|
||||
public Double getPrecipitation() {
|
||||
return precipitation;
|
||||
}
|
||||
|
||||
public Double getSnowFraction() {
|
||||
return snowFraction;
|
||||
}
|
||||
|
||||
public Integer getSeaLevelPressureMin() {
|
||||
return minSeaLevelPressure;
|
||||
}
|
||||
|
||||
public Integer getSeaLevelPressureMax() {
|
||||
return maxSeaLevelPressure;
|
||||
}
|
||||
|
||||
public Integer getSeaLevelPressureMean() {
|
||||
return meanSeaLevelPressure;
|
||||
}
|
||||
|
||||
public Double getWindSpeedMin() {
|
||||
return minWindSpeed;
|
||||
}
|
||||
|
||||
public Double getWindSpeedMax() {
|
||||
return maxWindSpeed;
|
||||
}
|
||||
|
||||
public Double getWindSpeedMean() {
|
||||
return meanWindSpeed;
|
||||
}
|
||||
|
||||
public Integer getRelativeHumidityMin() {
|
||||
return relativeHumidityMin;
|
||||
}
|
||||
|
||||
public Integer getRelativeHumidityMax() {
|
||||
return relativeHumidityMax;
|
||||
}
|
||||
|
||||
public Integer getRelativeHumidityMean() {
|
||||
return relativeHumidityMean;
|
||||
}
|
||||
|
||||
public Double getConvectivePrecipitation() {
|
||||
return convectivePrecipitation;
|
||||
}
|
||||
|
||||
public Double getPrecipitationHours() {
|
||||
return precipitationHours;
|
||||
}
|
||||
|
||||
public Double getHumidityGreater90Hours() {
|
||||
return humidityGreater90Hours;
|
||||
}
|
||||
|
||||
// derived getters
|
||||
private String getCardinalDirection(int degrees) {
|
||||
/*
|
||||
* 8 directions @ 45 deg. each, centered
|
||||
* N = 337.5-360,0-22.5
|
||||
* NE = 22.5-67.5
|
||||
* E = 67.5-112.5
|
||||
* SE = 112.5-157.5
|
||||
* S = 157.5-202.5
|
||||
* SW = 202.5-247.5
|
||||
* W = 247.5-292.5
|
||||
* NW = 292.5-337.5
|
||||
*/
|
||||
|
||||
if (degrees > 337 || degrees < 23) {
|
||||
return "N";
|
||||
}
|
||||
|
||||
if (degrees > 22 && degrees < 68) {
|
||||
return "NE";
|
||||
}
|
||||
|
||||
if (degrees > 67 && degrees < 113) {
|
||||
return "E";
|
||||
}
|
||||
|
||||
if (degrees > 112 && degrees < 158) {
|
||||
return "SE";
|
||||
}
|
||||
|
||||
if (degrees > 157 && degrees < 203) {
|
||||
return "S";
|
||||
}
|
||||
|
||||
if (degrees > 202 && degrees < 248) {
|
||||
return "SW";
|
||||
}
|
||||
|
||||
if (degrees > 247 && degrees < 293) {
|
||||
return "W";
|
||||
}
|
||||
|
||||
return "NW";
|
||||
}
|
||||
|
||||
public Double getSnowFall() {
|
||||
return snowFall;
|
||||
}
|
||||
|
||||
public Image getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
private String getIconNameForPictocode(int which) {
|
||||
if (which < 1 || which > 17) {
|
||||
return "iday.png";
|
||||
}
|
||||
|
||||
return "iday-" + which + ".png";
|
||||
}
|
||||
|
||||
private Image loadImageIcon(String imageFileName) {
|
||||
BufferedImage buf = null;
|
||||
String configDirectory = ConfigConstants.getConfigFolder();
|
||||
File dataFile = new File(new File(configDirectory, "icons/classic/"), imageFileName);
|
||||
if (!dataFile.exists()) {
|
||||
logger.debug("Image file '{}' does not exist. Unable to create imageIcon.", dataFile.getAbsolutePath());
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
buf = ImageIO.read(dataFile);
|
||||
logger.trace("Returning image data: {}", buf);
|
||||
return buf;
|
||||
} catch (FileNotFoundException e) {
|
||||
logger.trace("Image file '{}' not found during read attempt", dataFile, e);
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
logger.debug("Failed to load image file '{}' for weather icon.", dataFile, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Image generateRainAreaImage() {
|
||||
if (rainSpot == null) {
|
||||
logger.debug("No rainspot data exists. Can't generate rain area image.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
/*
|
||||
* Grid position -> <- String position
|
||||
* [ 0 1 2 3 4 5 6 ] [ 42 43 44 45 46 47 48 ]
|
||||
* [ 7 8 9 10 11 12 13 ] [ 35 36 37 38 39 40 41 ]
|
||||
* [ 14 15 16 17 18 19 20 ] [ 28 29 30 31 32 33 34 ]
|
||||
* [ 21 22 23 24 25 26 27 ] [ 21 22 23 24 25 26 27 ]
|
||||
* [ 28 29 30 31 32 33 34 ] [ 14 15 16 17 18 19 20 ]
|
||||
* [ 35 36 37 38 39 40 41 ] [ 7 8 9 10 11 12 13 ]
|
||||
* [ 42 43 44 45 46 47 48 ] [ 0 1 2 3 4 5 6 ]
|
||||
*/
|
||||
// @formatter:on
|
||||
|
||||
String s42 = rainSpot.substring(42);
|
||||
String s35 = rainSpot.substring(35, 42);
|
||||
String s28 = rainSpot.substring(28, 35);
|
||||
String s21 = rainSpot.substring(21, 28);
|
||||
String s14 = rainSpot.substring(14, 21);
|
||||
String s07 = rainSpot.substring(7, 14);
|
||||
String s00 = rainSpot.substring(0, 7);
|
||||
char[] values = new char[49];
|
||||
s00.getChars(0, s00.length(), values, 42);
|
||||
s07.getChars(0, s07.length(), values, 35);
|
||||
s14.getChars(0, s14.length(), values, 28);
|
||||
s21.getChars(0, s21.length(), values, 21);
|
||||
s28.getChars(0, s28.length(), values, 14);
|
||||
s35.getChars(0, s35.length(), values, 7);
|
||||
s42.getChars(0, s42.length(), values, 0);
|
||||
logger.trace("Final grid: {}", values);
|
||||
|
||||
BufferedImage buf = new BufferedImage(350, 350, BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
int gridCell = 0;
|
||||
Color color;
|
||||
|
||||
for (int y = 0; y < 7; y++) {
|
||||
for (int x = 0; x < 7; x++) {
|
||||
gridCell = y * 7 + x;
|
||||
color = getColorForIndex(values[gridCell]);
|
||||
writeGrid(gridCell, buf, color);
|
||||
}
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
private void writeGrid(int cell, BufferedImage img, Color color) {
|
||||
int r = color.getRed();
|
||||
int g = color.getGreen();
|
||||
int b = color.getBlue();
|
||||
int p = (r << 16) | (g << 8) | b;
|
||||
|
||||
int startX = cell % 7 * 50;
|
||||
int startY = cell / 7 * 50;
|
||||
for (int i = startY; i < startY + 50; i++) {
|
||||
for (int j = startX; j < startX + 50; j++) {
|
||||
img.setRGB(j, i, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Color getColorForIndex(char c) {
|
||||
switch (c) {
|
||||
case '0':
|
||||
return Color.WHITE;
|
||||
case '1':
|
||||
return Color.GREEN;
|
||||
case '2':
|
||||
return Color.YELLOW;
|
||||
case '3':
|
||||
return Color.RED;
|
||||
case '9':
|
||||
return Color.decode("0x00FF00");
|
||||
default:
|
||||
return Color.BLACK;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.meteoblue.internal;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* Defines common constants.
|
||||
*
|
||||
* @author Chris Carman - Initial contribution
|
||||
*/
|
||||
public class MeteoBlueBindingConstants {
|
||||
|
||||
private static final String BINDING_ID = "meteoblue";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
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));
|
||||
|
||||
// Bridge configuration settings
|
||||
public static final String APIKEY = "apiKey";
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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.meteoblue.internal;
|
||||
|
||||
/**
|
||||
* Model for the meteoblue bridge configuration.
|
||||
*
|
||||
* @author Chris Carman - Initial contribution
|
||||
*/
|
||||
public class MeteoBlueBridgeConfig {
|
||||
private String apiKey;
|
||||
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* 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.meteoblue.internal;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
/**
|
||||
* Model for the meteoblue binding configuration.
|
||||
*
|
||||
* @author Chris Carman - Initial contribution
|
||||
*/
|
||||
public class MeteoBlueConfiguration {
|
||||
|
||||
// default values
|
||||
public static final int DEFAULT_REFRESH = 240;
|
||||
|
||||
// constants
|
||||
public static final String SERVICETYPE_COMM = "Commercial";
|
||||
public static final String SERVICETYPE_NONCOMM = "NonCommercial";
|
||||
public static final String COMM_BASE_URL = "http://my.meteoblue.com/dataApi/dispatch.pl?type=json_7day_3h_firstday&";
|
||||
public static final String NONCOMM_BASE_URL = "http://my.meteoblue.com/packages/basic-day?";
|
||||
public static final String URL_MINIMAL_PARAMS = "apikey=#API_KEY#&lat=#LATITUDE#&lon=#LONGITUDE#";
|
||||
|
||||
// required parameters
|
||||
// servicetype - either Commercial or NonCommercial
|
||||
public String serviceType;
|
||||
// location - lat., long., and alt. in a single string
|
||||
public String location;
|
||||
|
||||
// optional parameters
|
||||
// refresh - time period in minutes between pulls
|
||||
public Integer refresh;
|
||||
// latitude - the latitude of this location in degrees (-90 to 90)
|
||||
public Double latitude;
|
||||
// longitude - the longitude of this location in degrees (-180 to 180)
|
||||
public Double longitude;
|
||||
// altitude - the height above sea level of the location, in meters
|
||||
public Double altitude;
|
||||
// timeZone - the timezone of the location (see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)
|
||||
public String timeZone;
|
||||
|
||||
// returns the URL for the specified serviceType
|
||||
public static String getURL(String serviceType) {
|
||||
if (SERVICETYPE_COMM.equals(serviceType)) {
|
||||
return COMM_BASE_URL + URL_MINIMAL_PARAMS + "#FORMAT_PARAMS#";
|
||||
} else {
|
||||
return NONCOMM_BASE_URL + URL_MINIMAL_PARAMS + "#FORMAT_PARAMS#";
|
||||
}
|
||||
}
|
||||
|
||||
public void parseLocation() {
|
||||
String[] split = location.split(",");
|
||||
String a1 = split.length > 0 ? split[0] : null;
|
||||
String a2 = split.length > 1 ? split[1] : null;
|
||||
String a3 = split.length > 2 ? split[2] : null;
|
||||
|
||||
if (!StringUtils.isBlank(a1)) {
|
||||
latitude = tryGetDouble(a1);
|
||||
}
|
||||
|
||||
if (!StringUtils.isBlank(a2)) {
|
||||
longitude = tryGetDouble(a2);
|
||||
}
|
||||
|
||||
if (!StringUtils.isBlank(a3)) {
|
||||
altitude = tryGetDouble(a3);
|
||||
}
|
||||
}
|
||||
|
||||
private Double tryGetDouble(String toParse) {
|
||||
try {
|
||||
return Double.parseDouble(toParse);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 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.meteoblue.internal;
|
||||
|
||||
import static org.openhab.binding.meteoblue.internal.MeteoBlueBindingConstants.*;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.openhab.binding.meteoblue.internal.handler.MeteoBlueBridgeHandler;
|
||||
import org.openhab.binding.meteoblue.internal.handler.MeteoBlueHandler;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
/**
|
||||
* The {@link MeteoBlueHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Chris Carman - Initial contribution
|
||||
*/
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.meteoblue")
|
||||
public class MeteoBlueHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Stream
|
||||
.concat(BRIDGE_THING_TYPES_UIDS.stream(), MeteoBlueBindingConstants.SUPPORTED_THING_TYPES_UIDS.stream())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (thingTypeUID.equals(THING_TYPE_WEATHER)) {
|
||||
return new MeteoBlueHandler(thing);
|
||||
}
|
||||
|
||||
if (thingTypeUID.equals(THING_TYPE_BRIDGE)) {
|
||||
return new MeteoBlueBridgeHandler((Bridge) thing);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* 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.meteoblue.internal.handler;
|
||||
|
||||
import static org.openhab.binding.meteoblue.internal.MeteoBlueBindingConstants.THING_TYPE_BRIDGE;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.openhab.binding.meteoblue.internal.MeteoBlueBridgeConfig;
|
||||
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.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link MeteoBlueBridgeHandler} is responsible for handling the
|
||||
* bridge things created to use the meteoblue weather service.
|
||||
*
|
||||
* @author Chris Carman - Initial contribution
|
||||
*/
|
||||
public class MeteoBlueBridgeHandler extends BaseBridgeHandler {
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_BRIDGE);
|
||||
private final Logger logger = LoggerFactory.getLogger(MeteoBlueBridgeHandler.class);
|
||||
|
||||
private String apiKey;
|
||||
|
||||
public MeteoBlueBridgeHandler(Bridge bridge) {
|
||||
super(bridge);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the bridge.
|
||||
*/
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing meteoblue bridge");
|
||||
|
||||
MeteoBlueBridgeConfig config = getConfigAs(MeteoBlueBridgeConfig.class);
|
||||
String apiKeyTemp = config.getApiKey();
|
||||
if (StringUtils.isBlank(apiKeyTemp)) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Cannot initialize meteoblue bridge. No apiKey provided.");
|
||||
return;
|
||||
}
|
||||
|
||||
apiKey = apiKeyTemp;
|
||||
|
||||
healthCheck();
|
||||
}
|
||||
|
||||
/**
|
||||
* No commands are supported here.
|
||||
*/
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
}
|
||||
|
||||
public String getApiKey() {
|
||||
return new String(apiKey);
|
||||
}
|
||||
|
||||
private void healthCheck() {
|
||||
String url = "http://my.meteoblue.com/packages/";
|
||||
try {
|
||||
HttpUtil.executeUrl("GET", url, 30 * 1000);
|
||||
logger.trace("HealthCheck succeeded.");
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} catch (Exception e) {
|
||||
logger.trace("HealthCheck failed", e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "HealthCheck failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
/**
|
||||
* 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.meteoblue.internal.handler;
|
||||
|
||||
import static org.openhab.core.library.unit.MetricPrefix.*;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Calendar;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.openhab.binding.meteoblue.internal.Forecast;
|
||||
import org.openhab.binding.meteoblue.internal.MeteoBlueConfiguration;
|
||||
import org.openhab.binding.meteoblue.internal.json.JsonData;
|
||||
import org.openhab.core.io.net.http.HttpUtil;
|
||||
import org.openhab.core.library.items.ImageItem;
|
||||
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.RawType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
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.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* The {@link MeteoBlueHandler} is responsible for handling commands
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Chris Carman - Initial contribution
|
||||
*/
|
||||
public class MeteoBlueHandler extends BaseThingHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(MeteoBlueHandler.class);
|
||||
|
||||
private Bridge bridge;
|
||||
private Forecast[] forecasts;
|
||||
private Gson gson;
|
||||
private JsonData weatherData;
|
||||
private ScheduledFuture<?> refreshJob;
|
||||
private boolean properlyConfigured;
|
||||
|
||||
public MeteoBlueHandler(Thing thing) {
|
||||
super(thing);
|
||||
gson = new Gson();
|
||||
forecasts = new Forecast[7];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (properlyConfigured) {
|
||||
logger.debug("Received command '{}' for channel '{}'", command, channelUID);
|
||||
updateChannel(channelUID.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing the meteoblue handler...");
|
||||
|
||||
bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
logger.warn("Unable to initialize meteoblue. No bridge was configured.");
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Bridge not configured.");
|
||||
return;
|
||||
}
|
||||
|
||||
MeteoBlueConfiguration config = getConfigAs(MeteoBlueConfiguration.class);
|
||||
|
||||
if (StringUtils.isBlank(config.serviceType)) {
|
||||
config.serviceType = MeteoBlueConfiguration.SERVICETYPE_NONCOMM;
|
||||
logger.debug("Using default service type ({}).", config.serviceType);
|
||||
return;
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(config.location)) {
|
||||
flagBadConfig("The location was not configured.");
|
||||
return;
|
||||
}
|
||||
|
||||
config.parseLocation();
|
||||
|
||||
if (config.latitude == null) {
|
||||
flagBadConfig(String.format("Could not determine latitude from the defined location setting (%s).",
|
||||
config.location));
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.latitude > 90.0 || config.latitude < -90.0) {
|
||||
flagBadConfig(String.format("Specified latitude value (%d) is not valid.", config.latitude));
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.longitude == null) {
|
||||
flagBadConfig(String.format("Could not determine longitude from the defined location setting (%s).",
|
||||
config.location));
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.longitude > 180.0 || config.longitude < -180.0) {
|
||||
flagBadConfig(String.format("Specified longitude value (%d) is not valid.", config.longitude));
|
||||
return;
|
||||
}
|
||||
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
startAutomaticRefresh(config);
|
||||
properlyConfigured = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the configuration as invalid.
|
||||
*/
|
||||
private void flagBadConfig(String message) {
|
||||
properlyConfigured = false;
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a job to periodically refresh the weather data.
|
||||
*/
|
||||
private void startAutomaticRefresh(MeteoBlueConfiguration config) {
|
||||
if (refreshJob != null && !refreshJob.isCancelled()) {
|
||||
logger.trace("Refresh job already exists.");
|
||||
return;
|
||||
}
|
||||
|
||||
Runnable runnable = () -> {
|
||||
boolean updateSuccessful = false;
|
||||
|
||||
try {
|
||||
// Request new weather data
|
||||
updateSuccessful = updateWeatherData();
|
||||
|
||||
if (updateSuccessful) {
|
||||
// build forecasts from the data
|
||||
for (int i = 0; i < 7; i++) {
|
||||
forecasts[i] = new Forecast(i, weatherData.getMetadata(), weatherData.getUnits(),
|
||||
weatherData.getDataDay());
|
||||
}
|
||||
|
||||
// Update all channels from the updated weather data
|
||||
for (Channel channel : getThing().getChannels()) {
|
||||
updateChannel(channel.getUID().getId());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("Exception occurred during weather update: {}", e.getMessage(), e);
|
||||
}
|
||||
};
|
||||
|
||||
int period = config.refresh != null ? config.refresh : MeteoBlueConfiguration.DEFAULT_REFRESH;
|
||||
refreshJob = scheduler.scheduleWithFixedDelay(runnable, 0, period, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
logger.debug("Disposing meteoblue handler.");
|
||||
|
||||
if (refreshJob != null && !refreshJob.isCancelled()) {
|
||||
refreshJob.cancel(true);
|
||||
refreshJob = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the channel from the last weather data retrieved.
|
||||
*
|
||||
* @param channelId the id of the channel to be updated
|
||||
*/
|
||||
private void updateChannel(String channelId) {
|
||||
Channel channel = getThing().getChannel(channelId);
|
||||
if (channel == null || !isLinked(channelId)) {
|
||||
logger.trace("Channel '{}' was null or not linked! Not updated.", channelId);
|
||||
return;
|
||||
}
|
||||
|
||||
// get the set of channel parameters.
|
||||
// the first will be the forecast day (eg. forecastToday),
|
||||
// and the second will be the datapoint (eg. snowFraction)
|
||||
String[] channelParts = channelId.split("#");
|
||||
String forecastDay = channelParts[0];
|
||||
String datapointName = channelParts[1];
|
||||
if (channelParts.length != 2) {
|
||||
logger.debug("Skipped invalid channelId '{}'", channelId);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("Updating channel '{}'", channelId);
|
||||
Forecast forecast = getForecast(forecastDay);
|
||||
if (forecast == null) {
|
||||
logger.debug("No forecast found for '{}'. Not updating.", forecastDay);
|
||||
return;
|
||||
}
|
||||
|
||||
Object datapoint = forecast.getDatapoint(datapointName);
|
||||
logger.debug("Value for datapoint '{}' is '{}'", datapointName, datapoint);
|
||||
if (datapoint == null) {
|
||||
logger.debug("Couldn't get datapoint '{}' for '{}'. Not updating.", datapointName, forecastDay);
|
||||
return;
|
||||
}
|
||||
|
||||
// Build a State from this value
|
||||
State state = null;
|
||||
if (datapoint instanceof Calendar) {
|
||||
state = new DateTimeType(
|
||||
ZonedDateTime.ofInstant(((Calendar) datapoint).toInstant(), ZoneId.systemDefault()));
|
||||
} else if (datapoint instanceof Integer) {
|
||||
state = getStateForType(channel.getAcceptedItemType(), (Integer) datapoint);
|
||||
} else if (datapoint instanceof Number) {
|
||||
BigDecimal decimalValue = new BigDecimal(datapoint.toString()).setScale(2, RoundingMode.HALF_UP);
|
||||
state = getStateForType(channel.getAcceptedItemType(), decimalValue);
|
||||
} else if (datapoint instanceof String) {
|
||||
state = new StringType(datapoint.toString());
|
||||
} else if (datapoint instanceof BufferedImage) {
|
||||
ImageItem item = new ImageItem("rain area");
|
||||
state = new RawType(renderImage((BufferedImage) datapoint), "image/png");
|
||||
item.setState(state);
|
||||
} else {
|
||||
logger.debug("Unsupported value type {}", datapoint.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
// Update the channel
|
||||
if (state != null) {
|
||||
logger.trace("Updating channel with state value {}. (object type {})", state,
|
||||
datapoint.getClass().getSimpleName());
|
||||
updateState(channelId, state);
|
||||
}
|
||||
}
|
||||
|
||||
private State getStateForType(String type, Integer value) {
|
||||
return getStateForType(type, new BigDecimal(value));
|
||||
}
|
||||
|
||||
private State getStateForType(String type, BigDecimal value) {
|
||||
State state = new DecimalType(value);
|
||||
|
||||
if (type.equals("Number:Temperature")) {
|
||||
state = new QuantityType<>(value, SIUnits.CELSIUS);
|
||||
} else if (type.equals("Number:Length")) {
|
||||
state = new QuantityType<>(value, MILLI(SIUnits.METRE));
|
||||
} else if (type.equals("Number:Pressure")) {
|
||||
state = new QuantityType<>(value, HECTO(SIUnits.PASCAL));
|
||||
} else if (type.equals("Number:Speed")) {
|
||||
state = new QuantityType<>(value, SmartHomeUnits.METRE_PER_SECOND);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
// Request new weather data from the service
|
||||
private boolean updateWeatherData() {
|
||||
if (bridge == null) {
|
||||
logger.debug("Unable to update weather data. Bridge missing.");
|
||||
return false;
|
||||
}
|
||||
|
||||
MeteoBlueBridgeHandler handler = (MeteoBlueBridgeHandler) bridge.getHandler();
|
||||
if (handler == null) {
|
||||
logger.debug("Unable to update weather data. Handler missing.");
|
||||
return false;
|
||||
}
|
||||
|
||||
String apiKey = handler.getApiKey();
|
||||
|
||||
logger.debug("Updating weather data...");
|
||||
MeteoBlueConfiguration config = getConfigAs(MeteoBlueConfiguration.class);
|
||||
config.parseLocation();
|
||||
String serviceType = config.serviceType;
|
||||
|
||||
if (serviceType.equals(MeteoBlueConfiguration.SERVICETYPE_COMM)) {
|
||||
logger.debug("Fetching weather data using Commercial API.");
|
||||
} else {
|
||||
logger.debug("Fetching weather data using NonCommercial API.");
|
||||
}
|
||||
|
||||
// get the base url for the HTTP query
|
||||
String url = MeteoBlueConfiguration.getURL(serviceType);
|
||||
url = url.replace("#API_KEY#", apiKey);
|
||||
url = url.replace("#LATITUDE#", String.valueOf(config.latitude)).replace("#LONGITUDE#",
|
||||
String.valueOf(config.longitude));
|
||||
|
||||
// fill in any optional parameters for the HTTP query
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
if (config.altitude != null) {
|
||||
builder.append("&asl=" + config.altitude);
|
||||
}
|
||||
if (StringUtils.isNotBlank(config.timeZone)) {
|
||||
builder.append("&tz=" + config.timeZone);
|
||||
}
|
||||
url = url.replace("#FORMAT_PARAMS#", builder.toString());
|
||||
logger.trace("Using URL '{}'", url);
|
||||
|
||||
// Run the HTTP request and get the JSON response
|
||||
String httpResponse = getWeatherData(url);
|
||||
if (httpResponse == null) {
|
||||
return false;
|
||||
}
|
||||
JsonData jsonResult = translateJson(httpResponse, serviceType);
|
||||
logger.trace("json object: {}", jsonResult);
|
||||
|
||||
if (jsonResult == null) {
|
||||
logger.warn("No data was received from the weather service");
|
||||
return false;
|
||||
}
|
||||
|
||||
String errorMessage = jsonResult.getErrorMessage();
|
||||
if (errorMessage != null) {
|
||||
if (errorMessage.equals("MB_REQUEST::DISPATCH: Invalid api key")) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Invalid API Key");
|
||||
} else if (errorMessage.equals("MB_REQUEST::DISPATCH: This datafeed is not authorized for your api key")) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"API Key not authorized for this datafeed");
|
||||
} else {
|
||||
logger.warn("Failed to retrieve weather data due to unexpected error. Error message: {}", errorMessage);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, errorMessage);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
weatherData = jsonResult;
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Run the HTTP request and get the JSON response
|
||||
private String getWeatherData(String url) {
|
||||
try {
|
||||
String httpResponse = HttpUtil.executeUrl("GET", url, 30 * 1000);
|
||||
logger.trace("http response: {}", httpResponse);
|
||||
return httpResponse;
|
||||
} catch (IOException e) {
|
||||
logger.debug("I/O Exception occurred while retrieving weather data.", e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"I/O Exception occurred while retrieving weather data.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert a json string response into a json data object
|
||||
private JsonData translateJson(String stringData, String serviceType) {
|
||||
// JsonData weatherData = null;
|
||||
|
||||
// For now, no distinction is made between commercial and non-commercial data;
|
||||
// This may need to be changed later based on user feedback.
|
||||
/*
|
||||
* if (serviceType.equals(MeteoBlueConfiguration.SERVICETYPE_COMM)) {
|
||||
* weatherData = gson.fromJson(httpResponse, JsonCommercialData.class);
|
||||
* }
|
||||
* else {
|
||||
* weatherData = gson.fromJson(httpResponse, JsonNonCommercialData.class);
|
||||
* }
|
||||
*/
|
||||
|
||||
return gson.fromJson(stringData, JsonData.class);
|
||||
}
|
||||
|
||||
private Forecast getForecast(String which) {
|
||||
switch (which) {
|
||||
case "forecastToday":
|
||||
return forecasts[0];
|
||||
case "forecastTomorrow":
|
||||
return forecasts[1];
|
||||
case "forecastDay2":
|
||||
return forecasts[2];
|
||||
case "forecastDay3":
|
||||
return forecasts[3];
|
||||
case "forecastDay4":
|
||||
return forecasts[4];
|
||||
case "forecastDay5":
|
||||
return forecasts[5];
|
||||
case "forecastDay6":
|
||||
return forecasts[6];
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] renderImage(BufferedImage image) {
|
||||
byte[] data = null;
|
||||
|
||||
try {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ImageIO.write(image, "png", out);
|
||||
out.flush();
|
||||
data = out.toByteArray();
|
||||
out.close();
|
||||
} catch (IOException ioe) {
|
||||
logger.debug("I/O exception occurred converting image data", ioe);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -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.meteoblue.internal.json;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* The {@link JsonData} is the Java class used to model the JSON
|
||||
* response to a weather request.
|
||||
*
|
||||
* @author Chris Carman - Initial contribution
|
||||
*/
|
||||
public class JsonData {
|
||||
|
||||
private JsonMetadata metadata;
|
||||
private JsonUnits units;
|
||||
|
||||
@SerializedName("data_day")
|
||||
private JsonDataDay dataDay;
|
||||
|
||||
@SerializedName("error_message")
|
||||
private String errorMessage;
|
||||
|
||||
public JsonData() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link JsonMetadata} object
|
||||
*
|
||||
* @return the JsonMetadata object
|
||||
*/
|
||||
public JsonMetadata getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link JsonUnits} object
|
||||
*
|
||||
* @return the JsonUnits object
|
||||
*/
|
||||
public JsonUnits getUnits() {
|
||||
return units;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link JsonDataDay} object
|
||||
*
|
||||
* @return the JsonDataDay object
|
||||
*/
|
||||
public JsonDataDay getDataDay() {
|
||||
return dataDay;
|
||||
}
|
||||
|
||||
// get the error message
|
||||
public String getErrorMessage() {
|
||||
return errorMessage;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
/**
|
||||
* 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.meteoblue.internal.json;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* {@link JsonDataDay} models the 'data_day' portion of the JSON
|
||||
* response to a weather request.
|
||||
*
|
||||
* @author Chris Carman - Initial contribution
|
||||
*/
|
||||
public class JsonDataDay {
|
||||
|
||||
private String[] time;
|
||||
private Integer[] pictocode;
|
||||
|
||||
@SerializedName("uvindex")
|
||||
private Integer[] uvIndex;
|
||||
|
||||
@SerializedName("temperature_max")
|
||||
private Double[] temperatureMax;
|
||||
|
||||
@SerializedName("temperature_min")
|
||||
private Double[] temperatureMin;
|
||||
|
||||
@SerializedName("temperature_mean")
|
||||
private Double[] temperatureMean;
|
||||
|
||||
@SerializedName("felttemperature_max")
|
||||
private Double[] feltTemperatureMax;
|
||||
|
||||
@SerializedName("felttemperature_min")
|
||||
private Double[] feltTemperatureMin;
|
||||
|
||||
@SerializedName("winddirection")
|
||||
private Integer[] windDirection;
|
||||
|
||||
@SerializedName("precipitation_probability")
|
||||
private Integer[] precipitationProbability;
|
||||
|
||||
private String[] rainspot;
|
||||
|
||||
@SerializedName("predictability_class")
|
||||
private Integer[] predictabilityClass;
|
||||
|
||||
private Integer[] predictability;
|
||||
|
||||
private Double[] precipitation;
|
||||
|
||||
@SerializedName("snowfraction")
|
||||
private Double[] snowFraction;
|
||||
|
||||
@SerializedName("sealevelpressure_max")
|
||||
private Integer[] seaLevelPressureMax;
|
||||
|
||||
@SerializedName("sealevelpressure_min")
|
||||
private Integer[] seaLevelPressureMin;
|
||||
|
||||
@SerializedName("sealevelpressure_mean")
|
||||
private Integer[] seaLevelPressureMean;
|
||||
|
||||
@SerializedName("windspeed_max")
|
||||
private Double[] windSpeedMax;
|
||||
|
||||
@SerializedName("windspeed_mean")
|
||||
private Double[] windSpeedMean;
|
||||
|
||||
@SerializedName("windspeed_min")
|
||||
private Double[] windSpeedMin;
|
||||
|
||||
@SerializedName("relativehumidity_max")
|
||||
private Integer[] relativeHumidityMax;
|
||||
|
||||
@SerializedName("relativehumidity_min")
|
||||
private Integer[] relativeHumidityMin;
|
||||
|
||||
@SerializedName("relativehumidity_mean")
|
||||
private Integer[] relativeHumidityMean;
|
||||
|
||||
@SerializedName("convective_precipitation")
|
||||
private Double[] convectivePrecipitation;
|
||||
|
||||
@SerializedName("precipitation_hours")
|
||||
private Double[] precipitationHours;
|
||||
|
||||
@SerializedName("humiditygreater90_hours")
|
||||
private Double[] humidityGreater90Hours;
|
||||
|
||||
public JsonDataDay() {
|
||||
}
|
||||
|
||||
public String[] getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public Integer[] getPictocode() {
|
||||
return pictocode;
|
||||
}
|
||||
|
||||
public Integer[] getUVIndex() {
|
||||
return uvIndex;
|
||||
}
|
||||
|
||||
public Double[] getTemperatureMax() {
|
||||
return temperatureMax;
|
||||
}
|
||||
|
||||
public Double[] getTemperatureMin() {
|
||||
return temperatureMin;
|
||||
}
|
||||
|
||||
public Double[] getTemperatureMean() {
|
||||
return temperatureMean;
|
||||
}
|
||||
|
||||
public Double[] getFeltTemperatureMax() {
|
||||
return feltTemperatureMax;
|
||||
}
|
||||
|
||||
public Double[] getFeltTemperatureMin() {
|
||||
return feltTemperatureMin;
|
||||
}
|
||||
|
||||
public Integer[] getWindDirection() {
|
||||
return windDirection;
|
||||
}
|
||||
|
||||
public Integer[] getPrecipitationProbability() {
|
||||
return precipitationProbability;
|
||||
}
|
||||
|
||||
public String[] getRainspot() {
|
||||
return rainspot;
|
||||
}
|
||||
|
||||
public Integer[] getPredictabilityClass() {
|
||||
return predictabilityClass;
|
||||
}
|
||||
|
||||
public Integer[] getPredictability() {
|
||||
return predictability;
|
||||
}
|
||||
|
||||
public Double[] getPrecipitation() {
|
||||
return precipitation;
|
||||
}
|
||||
|
||||
public Double[] getSnowFraction() {
|
||||
return snowFraction;
|
||||
}
|
||||
|
||||
public Integer[] getSeaLevelPressureMax() {
|
||||
return seaLevelPressureMax;
|
||||
}
|
||||
|
||||
public Integer[] getSeaLevelPressureMin() {
|
||||
return seaLevelPressureMin;
|
||||
}
|
||||
|
||||
public Integer[] getSeaLevelPressureMean() {
|
||||
return seaLevelPressureMean;
|
||||
}
|
||||
|
||||
public Double[] getWindSpeedMax() {
|
||||
return windSpeedMax;
|
||||
}
|
||||
|
||||
public Double[] getWindSpeedMean() {
|
||||
return windSpeedMean;
|
||||
}
|
||||
|
||||
public Double[] getWindSpeedMin() {
|
||||
return windSpeedMin;
|
||||
}
|
||||
|
||||
public Integer[] getRelativeHumidityMax() {
|
||||
return relativeHumidityMax;
|
||||
}
|
||||
|
||||
public Integer[] getRelativeHumidityMin() {
|
||||
return relativeHumidityMin;
|
||||
}
|
||||
|
||||
public Integer[] getRelativeHumidityMean() {
|
||||
return relativeHumidityMean;
|
||||
}
|
||||
|
||||
public Double[] getConvectivePrecipitation() {
|
||||
return convectivePrecipitation;
|
||||
}
|
||||
|
||||
public Double[] getPrecipitationHours() {
|
||||
return precipitationHours;
|
||||
}
|
||||
|
||||
public Double[] getHumidityGreater90Hours() {
|
||||
return humidityGreater90Hours;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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.meteoblue.internal.json;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* {@link JsonUnits} models the 'metadata' portion of the JSON
|
||||
* response to a weather request.
|
||||
*
|
||||
* @author Chris Carman - Initial contribution
|
||||
*/
|
||||
public class JsonMetadata {
|
||||
|
||||
private String name;
|
||||
private Double latitude;
|
||||
private Double longitude;
|
||||
private Integer height;
|
||||
|
||||
// [sic]
|
||||
@SerializedName("timezone_abbrevation")
|
||||
private String timeZoneAbbreviation;
|
||||
|
||||
public JsonMetadata() {
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Double getLatitude() {
|
||||
return latitude;
|
||||
}
|
||||
|
||||
public Double getLongitude() {
|
||||
return longitude;
|
||||
}
|
||||
|
||||
public Integer getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public String getTimeZoneAbbreviation() {
|
||||
return timeZoneAbbreviation;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* 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.meteoblue.internal.json;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* {@link JsonUnits} models the 'units' portion of the JSON
|
||||
* response to a weather request.
|
||||
*
|
||||
* @author Chris Carman - Initial contribution
|
||||
*/
|
||||
public class JsonUnits {
|
||||
private String time;
|
||||
private String predictability;
|
||||
|
||||
@SerializedName("precipitation_probability")
|
||||
private String precipitationProbability;
|
||||
|
||||
private String pressure;
|
||||
|
||||
@SerializedName("relativehumidity")
|
||||
private String relativeHumidity;
|
||||
|
||||
private String temperature;
|
||||
|
||||
@SerializedName("winddirection")
|
||||
private String windDirection;
|
||||
|
||||
private String precipitation;
|
||||
|
||||
@SerializedName("windspeed")
|
||||
private String windSpeed;
|
||||
|
||||
public JsonUnits() {
|
||||
}
|
||||
|
||||
public String getPredictability() {
|
||||
return predictability;
|
||||
}
|
||||
|
||||
public String getPrecipitationProbability() {
|
||||
return precipitationProbability;
|
||||
}
|
||||
|
||||
public String getPressure() {
|
||||
return pressure;
|
||||
}
|
||||
|
||||
public String getRelativeHumidity() {
|
||||
return relativeHumidity;
|
||||
}
|
||||
|
||||
public String getTemperature() {
|
||||
return temperature;
|
||||
}
|
||||
|
||||
public String getWindDirection() {
|
||||
return windDirection;
|
||||
}
|
||||
|
||||
public String getPrecipitation() {
|
||||
return precipitation;
|
||||
}
|
||||
|
||||
public String getWindSpeed() {
|
||||
return windSpeed;
|
||||
}
|
||||
|
||||
public String getTime() {
|
||||
return time;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="meteoblue" 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>meteoblue Binding</name>
|
||||
<description>The meteoblue binding retrieves weather information from the meteoblue weather service.</description>
|
||||
<author>Chris Carman</author>
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,166 @@
|
||||
# binding
|
||||
binding.meteoblue.name = Extension meteoblue
|
||||
binding.meteoblue.description = L'extension meteoblue interroge le service meteoblue pour récupérer les prévisions météorologiqes.
|
||||
|
||||
# bridge types
|
||||
thing-type.meteoblue.bridge.label = Compte meteoblue
|
||||
thing-type.meteoblue.bridge.description = Fournit un accès à l'API du service meteoblue.
|
||||
|
||||
# bridge types configuration
|
||||
thing-type.config.meteoblue.bridge.apiKey.label = Clé API
|
||||
thing-type.config.meteoblue.bridge.apiKey.description = Clé pour accéder à l'API du service meteoblue.
|
||||
|
||||
# thing types
|
||||
thing-type.meteoblue.weather.label = Prévisions météorologiques
|
||||
thing-type.meteoblue.weather.description = Fournit les prévisions météorologiques du service méteoblue.
|
||||
|
||||
# thing type configuration
|
||||
thing-type.config.meteoblue.weather.serviceType.label = Type de service
|
||||
thing-type.config.meteoblue.weather.serviceType.description = Type d'API à utiliser.
|
||||
thing-type.config.meteoblue.weather.serviceType.option.Commercial = Commercial
|
||||
thing-type.config.meteoblue.weather.serviceType.option.NonCommercial = Non Commercial
|
||||
|
||||
thing-type.config.meteoblue.weather.location.label = Localisation
|
||||
thing-type.config.meteoblue.weather.location.description = Localisation en coordonnées géographiques (latitude, longitude[, altitude]).
|
||||
|
||||
thing-type.config.meteoblue.weather.timeZone.label = Fuseau horaire
|
||||
thing-type.config.meteoblue.weather.timeZone.description = Fuseau horaire de cet emplacement (Europe/Paris par exemple). Voir https://en.wikipedia.org/wiki/List_of_tz_database_time_zones .
|
||||
|
||||
thing-type.config.meteoblue.weather.refresh.label = Intervalle d'actualisation
|
||||
thing-type.config.meteoblue.weather.refresh.description = Spécifie l'intervalle d'actualisation (en minutes).
|
||||
|
||||
# channel groups
|
||||
thing-type.meteoblue.weather.group.forecastToday.label = Prévisions pour aujourd'hui
|
||||
thing-type.meteoblue.weather.group.forecastToday.description = Représente les prévisions météorologiques pour aujourd'hui.
|
||||
|
||||
thing-type.meteoblue.weather.group.forecastTomorrow.label = Prévisions pour demain
|
||||
thing-type.meteoblue.weather.group.forecastTomorrow.description = Représente les prévisions météorologiques pour demain.
|
||||
|
||||
thing-type.meteoblue.weather.group.forecastDay2.label = Prévisions dans 2 jours
|
||||
thing-type.meteoblue.weather.group.forecastDay2.description = Représente les prévisions météorologiques dans deux jours.
|
||||
|
||||
thing-type.meteoblue.weather.group.forecastDay3.label = Prévisions dans 3 jours
|
||||
thing-type.meteoblue.weather.group.forecastDay3.description = Représente les prévisions météorologiques dans trois jours.
|
||||
|
||||
thing-type.meteoblue.weather.group.forecastDay4.label = Prévisions dans 4 jours
|
||||
thing-type.meteoblue.weather.group.forecastDay4.description = Représente les prévisions météorologiques dans quatre jours.
|
||||
|
||||
thing-type.meteoblue.weather.group.forecastDay5.label = Prévisions dans 5 jours
|
||||
thing-type.meteoblue.weather.group.forecastDay5.description = Représente les prévisions météorologiques dans cinq jours.
|
||||
|
||||
thing-type.meteoblue.weather.group.forecastDay6.label = Prévisions dans 6 jours
|
||||
thing-type.meteoblue.weather.group.forecastDay6.description = Représente les prévisions météorologiques dans six jours.
|
||||
|
||||
# channel types
|
||||
channel-type.meteoblue.height.label = Altitude
|
||||
channel-type.meteoblue.height.description = L'altitude au-dessus de la mer de l'emplacement (en mètres).
|
||||
|
||||
channel-type.meteoblue.forecastDate.label = Date des prévisions
|
||||
channel-type.meteoblue.forecastDate.description = La date et l'heure des prévisions.
|
||||
|
||||
channel-type.meteoblue.UVIndex.label = Indice UV
|
||||
channel-type.meteoblue.UVIndex.description = L'indice UV prévu.
|
||||
|
||||
channel-type.meteoblue.minTemperature.label = Température minimale
|
||||
channel-type.meteoblue.minTemperature.description = La température extérieure minimale prévue.
|
||||
|
||||
channel-type.meteoblue.maxTemperature.label = Température maximale
|
||||
channel-type.meteoblue.maxTemperature.description = La température extérieure maximale prévue.
|
||||
|
||||
channel-type.meteoblue.meanTemperature.label = Température moyenne
|
||||
channel-type.meteoblue.meanTemperature.description = La température extérieure moyenne prévue.
|
||||
|
||||
channel-type.meteoblue.feltTemperatureMin.label = Température minimale ressentie
|
||||
channel-type.meteoblue.feltTemperatureMin.description = La température minimale ressentie.
|
||||
|
||||
channel-type.meteoblue.feltTemperatureMax.label = Température maximale ressentie
|
||||
channel-type.meteoblue.feltTemperatureMax.description = La température maximale ressentie.
|
||||
|
||||
channel-type.meteoblue.relativeHumidityMin.label = Humidité atmosphérique minimale
|
||||
channel-type.meteoblue.relativeHumidityMin.description = L'humidité relative atmosphérique minimale prévue.
|
||||
|
||||
channel-type.meteoblue.relativeHumidityMax.label = Humidité atmosphérique maximale
|
||||
channel-type.meteoblue.relativeHumidityMax.description = L'humidité relative atmosphérique maximale prévue.
|
||||
|
||||
channel-type.meteoblue.relativeHumidityMean.label = Humidité atmosphérique moyenne
|
||||
channel-type.meteoblue.relativeHumidityMean.description = L'humidité relative atmosphérique moyenne prévue.
|
||||
|
||||
channel-type.meteoblue.precipitationProbability.label = Probabilité de précipitations
|
||||
channel-type.meteoblue.precipitationProbability.description = La probabilité de précipitations.
|
||||
|
||||
channel-type.meteoblue.precipitation.label = Précipitations
|
||||
channel-type.meteoblue.precipitation.description = La quantité prévue de précipitations.
|
||||
|
||||
channel-type.meteoblue.convectivePrecipitation.label = Précipitations convectives
|
||||
channel-type.meteoblue.convectivePrecipitation.description = La quantité prévue de précipitations de type convective.
|
||||
|
||||
channel-type.meteoblue.rainSpot.label = Distribution des précipitations
|
||||
channel-type.meteoblue.rainSpot.description = Distribution des précipitations autour de l'emplacement sous forme de matrice 7x7, codé du sud au nord et d'ouest en est.
|
||||
|
||||
channel-type.meteoblue.rainArea.label = Zone des précipitations
|
||||
channel-type.meteoblue.rainArea.description = Image représentant la distribution des précipitations.
|
||||
|
||||
channel-type.meteoblue.snowFraction.label = Proportion de neige
|
||||
channel-type.meteoblue.snowFraction.description = La proportion prévue de neige sur l'ensemble des précipitations.
|
||||
|
||||
channel-type.meteoblue.snowFall.label = Chutes de neige
|
||||
channel-type.meteoblue.snowFall.description = La quantité prévue de neige.
|
||||
|
||||
channel-type.meteoblue.cardinalWindDirection.label = Direction cardinale du vent
|
||||
channel-type.meteoblue.cardinalWindDirection.description = La direction cardinale prévue du vent.
|
||||
|
||||
channel-type.meteoblue.windDirection.label = Direction du vent
|
||||
channel-type.meteoblue.windDirection.description = La direction prévue du vent exprimée sous forme d'angle.
|
||||
|
||||
channel-type.meteoblue.minWindSpeed.label = Vitesse minimale du vent
|
||||
channel-type.meteoblue.minWindSpeed.description = La vitesse minimale prévue du vent.
|
||||
|
||||
channel-type.meteoblue.maxWindSpeed.label = Vitesse maximale du vent
|
||||
channel-type.meteoblue.maxWindSpeed.description = La vitesse maximale prévue du vent.
|
||||
|
||||
channel-type.meteoblue.meanWindSpeed.label = Vitesse moyenne du vent
|
||||
channel-type.meteoblue.meanWindSpeed.description = La vitesse moyenne prévue du vent.
|
||||
|
||||
channel-type.meteoblue.pressureMin.label = Pression barométrique minimale
|
||||
channel-type.meteoblue.pressureMin.description = La pression barométrique minimale prévue.
|
||||
|
||||
channel-type.meteoblue.pressureMax.label = Pression barométrique maximale
|
||||
channel-type.meteoblue.pressureMax.description = La pression barométrique maximale prévue.
|
||||
|
||||
channel-type.meteoblue.pressureMean.label = Pression barométrique moyenne
|
||||
channel-type.meteoblue.pressureMean.description = La pression barométrique moyenne prévue.
|
||||
|
||||
channel-type.meteoblue.condition.label = Conditions météorologiques
|
||||
channel-type.meteoblue.condition.description = Les conditions météorologiques prévues.
|
||||
channel-type.meteoblue.condition.state.option.1 = Ciel ensoleillé
|
||||
channel-type.meteoblue.condition.state.option.2 = Ensoleillé et quelques nuages
|
||||
channel-type.meteoblue.condition.state.option.3 = Partiellement nuageux
|
||||
channel-type.meteoblue.condition.state.option.4 = Couvert
|
||||
channel-type.meteoblue.condition.state.option.5 = Brouillard
|
||||
channel-type.meteoblue.condition.state.option.6 = Couvert avec pluie
|
||||
channel-type.meteoblue.condition.state.option.7 = Variable avec averses
|
||||
channel-type.meteoblue.condition.state.option.8 = Averses, orages probables
|
||||
channel-type.meteoblue.condition.state.option.9 = Couvert avec neige
|
||||
channel-type.meteoblue.condition.state.option.10 = Variable avec des averses de neige
|
||||
channel-type.meteoblue.condition.state.option.11 = Nuageux avec un mélange de neige et de pluie
|
||||
channel-type.meteoblue.condition.state.option.12 = Couvert avec faible pluie
|
||||
channel-type.meteoblue.condition.state.option.13 = Couvert avec quelques chutes de neige
|
||||
channel-type.meteoblue.condition.state.option.14 = Nuageux avec pluie
|
||||
channel-type.meteoblue.condition.state.option.15 = Nuageux avec neige
|
||||
channel-type.meteoblue.condition.state.option.16 = Nuageux avec avec faible pluie
|
||||
channel-type.meteoblue.condition.state.option.17 = Nuageux avec quelques chutes de neige
|
||||
|
||||
channel-type.meteoblue.icon.label = Icône
|
||||
channel-type.meteoblue.icon.description = L'icône représentant les conditions météorologiques.
|
||||
|
||||
channel-type.meteoblue.predictability.label = Confiance des prévisions
|
||||
channel-type.meteoblue.predictability.description = Le niveau de confiance des prévisions (en %).
|
||||
|
||||
channel-type.meteoblue.predictabilityClass.label = Classe de confiance des prévisions
|
||||
channel-type.meteoblue.predictabilityClass.description = La classe de confiance des prévisions (0 = très faible, 5 = très élevé).
|
||||
|
||||
channel-type.meteoblue.precipitationHours.label = Heures avec précipitations
|
||||
channel-type.meteoblue.precipitationHours.description = Le nombre prévu d'heures avec des précipitations.
|
||||
|
||||
channel-type.meteoblue.humidityGreater90Hours.label = Heures avec humidité élevée
|
||||
channel-type.meteoblue.humidityGreater90Hours.description = Le nombre prévu d'heures avec une humidité supérieure à 90%.
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="meteoblue"
|
||||
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">
|
||||
<!-- meteoblue Bridge -->
|
||||
<bridge-type id="bridge">
|
||||
<label>meteoblue Account</label>
|
||||
<description>The meteoblue account holds settings for use with the meteoblue weather service.</description>
|
||||
|
||||
<config-description>
|
||||
<parameter name="apiKey" type="text" required="true">
|
||||
<context>password</context>
|
||||
<label>API Key</label>
|
||||
<description>API key to access the meteoblue service</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</bridge-type>
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,390 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="meteoblue"
|
||||
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">
|
||||
|
||||
<!-- meteoblue Binding things -->
|
||||
<thing-type id="weather">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="bridge"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Weather Information</label>
|
||||
<description>Weather data from the meteoblue service</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="forecastToday" typeId="forecast">
|
||||
<label>Weather Forecast Today</label>
|
||||
<description>Weather forecast for today</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastTomorrow" typeId="forecast">
|
||||
<label>Weather Forecast Tomorrow</label>
|
||||
<description>Weather forecast for tomorrow</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay2" typeId="forecast">
|
||||
<label>Weather Forecast Day 2</label>
|
||||
<description>Weather forecast two days from now</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay3" typeId="forecast">
|
||||
<label>Weather Forecast Day 3</label>
|
||||
<description>Weather forecast three days from now</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay4" typeId="forecast">
|
||||
<label>Weather Forecast Day 4</label>
|
||||
<description>Weather forecast four days from now</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay5" typeId="forecast">
|
||||
<label>Weather Forecast Day 5</label>
|
||||
<description>Weather forecast five days from now</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay6" typeId="forecast">
|
||||
<label>Weather Forecast Day 6</label>
|
||||
<description>Weather forecast six days from now</description>
|
||||
</channel-group>
|
||||
</channel-groups>
|
||||
|
||||
<config-description>
|
||||
<parameter name="serviceType" type="text">
|
||||
<label>Service Type</label>
|
||||
<description>The type of API to be used</description>
|
||||
<options>
|
||||
<option value="Commercial">Commercial</option>
|
||||
<option value="NonCommercial">NonCommercial</option>
|
||||
</options>
|
||||
<limitToOptions>true</limitToOptions>
|
||||
<default>NonCommercial</default>
|
||||
</parameter>
|
||||
<parameter name="location" type="text" required="true">
|
||||
<context>location</context>
|
||||
<label>Location</label>
|
||||
<description>The latitude, longitude and optionally altitude, separated by commas (lat,long,[alt])</description>
|
||||
</parameter>
|
||||
<parameter name="timeZone" type="text">
|
||||
<label>Time Zone</label>
|
||||
<description>Time Zone of this location (e.g. America/Louisville). See
|
||||
https://en.wikipedia.org/wiki/List_of_tz_database_time_zones</description>
|
||||
</parameter>
|
||||
<parameter name="refresh" type="integer" min="5" unit="min">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Refresh interval (in minutes)</description>
|
||||
<default>240</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<channel-group-type id="forecast">
|
||||
<label>Weather Forecast</label>
|
||||
<description>This is the weather forecast</description>
|
||||
<channels>
|
||||
<channel id="height" typeId="height"/>
|
||||
<channel id="forecastDate" typeId="forecastDate"/>
|
||||
<channel id="UVIndex" typeId="UVIndex"/>
|
||||
<channel id="minTemperature" typeId="minTemperature"/>
|
||||
<channel id="maxTemperature" typeId="maxTemperature"/>
|
||||
<channel id="meanTemperature" typeId="meanTemperature"/>
|
||||
<channel id="feltTemperatureMin" typeId="feltTemperatureMin"/>
|
||||
<channel id="feltTemperatureMax" typeId="feltTemperatureMax"/>
|
||||
<channel id="relativeHumidityMin" typeId="relativeHumidityMin"/>
|
||||
<channel id="relativeHumidityMax" typeId="relativeHumidityMax"/>
|
||||
<channel id="relativeHumidityMean" typeId="relativeHumidityMean"/>
|
||||
<channel id="precipitationProbability" typeId="precipitationProbability"/>
|
||||
<channel id="precipitation" typeId="precipitation"/>
|
||||
<channel id="convectivePrecipitation" typeId="convectivePrecipitation"/>
|
||||
<channel id="rainSpot" typeId="rainSpot"/>
|
||||
<channel id="rainArea" typeId="rainArea"/>
|
||||
<channel id="snowFraction" typeId="snowFraction"/>
|
||||
<channel id="snowFall" typeId="snowFall"/>
|
||||
<channel id="cardinalWindDirection" typeId="cardinalWindDirection"/>
|
||||
<channel id="windDirection" typeId="windDirection"/>
|
||||
<channel id="minWindSpeed" typeId="minWindSpeed"/>
|
||||
<channel id="maxWindSpeed" typeId="maxWindSpeed"/>
|
||||
<channel id="meanWindSpeed" typeId="meanWindSpeed"/>
|
||||
<channel id="minSeaLevelPressure" typeId="pressureMin"/>
|
||||
<channel id="maxSeaLevelPressure" typeId="pressureMax"/>
|
||||
<channel id="meanSeaLevelPressure" typeId="pressureMean"/>
|
||||
<channel id="condition" typeId="condition"/>
|
||||
<channel id="icon" typeId="icon"/>
|
||||
<channel id="predictability" typeId="predictability"/>
|
||||
<channel id="predictabilityClass" typeId="predictabilityClass"/>
|
||||
<channel id="precipitationHours" typeId="precipitationHours"/>
|
||||
<channel id="humidityGreater90Hours" typeId="humidityGreater90Hours"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-type id="height" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>Altitude</label>
|
||||
<description>Altitude above sea-level of the location (in meters)</description>
|
||||
<state readOnly="true" pattern="%d"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="forecastDate">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Forecast Date</label>
|
||||
<description>Forecast date</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="minTemperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Minimum Temperature</label>
|
||||
<description>The low 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>The high temperature</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="meanTemperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Mean Temperature</label>
|
||||
<description>The average temperature</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="feltTemperatureMin" advanced="true">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Minimum Felt Temperature</label>
|
||||
<description>The low "feels like" temperature</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="feltTemperatureMax" advanced="true">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Maximum Felt Temperature</label>
|
||||
<description>The high "feels like" temperature</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="relativeHumidityMin">
|
||||
<item-type>Number</item-type>
|
||||
<label>Minimum Relative Humidity</label>
|
||||
<description>The minimum relative humidity in %</description>
|
||||
<category>Humidity</category>
|
||||
<state readOnly="true" min="0" max="100" pattern="%d %%">
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="relativeHumidityMax">
|
||||
<item-type>Number</item-type>
|
||||
<label>Maximum Relative Humidity</label>
|
||||
<description>The maximum relative humidity in %</description>
|
||||
<category>Humidity</category>
|
||||
<state readOnly="true" min="0" max="100" pattern="%d %%">
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="relativeHumidityMean">
|
||||
<item-type>Number</item-type>
|
||||
<label>Mean Relative Humidity</label>
|
||||
<description>The average relative humidity in %</description>
|
||||
<category>Humidity</category>
|
||||
<state readOnly="true" min="0" max="100" pattern="%d %%">
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="cardinalWindDirection">
|
||||
<item-type>String</item-type>
|
||||
<label>Cardinal Wind Direction</label>
|
||||
<state readOnly="true" pattern="%s">
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windDirection" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>Wind Direction</label>
|
||||
<description>Wind direction (in degrees)</description>
|
||||
<category>Wind</category>
|
||||
<state readOnly="true" min="0" max="360" pattern="%.0f °">
|
||||
</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="minWindSpeed">
|
||||
<item-type>Number:Speed</item-type>
|
||||
<label>Minimum Wind Speed</label>
|
||||
<category>Wind</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="meanWindSpeed">
|
||||
<item-type>Number:Speed</item-type>
|
||||
<label>Mean Wind Speed</label>
|
||||
<category>Wind</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="pressureMin">
|
||||
<item-type>Number:Pressure</item-type>
|
||||
<label>Low Pressure</label>
|
||||
<description>Low Pressure</description>
|
||||
<category>Pressure</category>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="pressureMax">
|
||||
<item-type>Number:Pressure</item-type>
|
||||
<label>High Pressure</label>
|
||||
<description>High Pressure</description>
|
||||
<category>Pressure</category>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="pressureMean">
|
||||
<item-type>Number:Pressure</item-type>
|
||||
<label>Mean Pressure</label>
|
||||
<description>Mean Pressure</description>
|
||||
<category>Pressure</category>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</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="precipitation">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Precipitation</label>
|
||||
<description>Prediction of total precipitation</description>
|
||||
<category>Rain</category>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="convectivePrecipitation">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Convective Precipitation</label>
|
||||
<description>Prediction of convective precipitation (rainfall)</description>
|
||||
<category>Rain</category>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="rainSpot">
|
||||
<item-type>String</item-type>
|
||||
<label>Rain Spot</label>
|
||||
<description>Precipitation distribution around the location as a 7x7 array,
|
||||
encoded from south to north and west to
|
||||
east
|
||||
(e.g. 2112211093222219212110112229922133311912239022192).</description>
|
||||
<category>Rain</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="rainArea">
|
||||
<item-type>Image</item-type>
|
||||
<label>Rain Area</label>
|
||||
<description>Image representing the Rain Spot precipitation distribution</description>
|
||||
<category>Rain</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="snowFraction">
|
||||
<item-type>Number</item-type>
|
||||
<label>Snow Fraction</label>
|
||||
<description>Prediction of the fraction of total precipitation that will fall as snow</description>
|
||||
<category>Rain</category>
|
||||
<state readOnly="true" min="0" max="100" pattern="%d %%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="snowFall">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Snowfall</label>
|
||||
<description>Prediction of snowfall</description>
|
||||
<category>Rain</category>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="precipitationProbability">
|
||||
<item-type>Number</item-type>
|
||||
<label>Precipitation Probability</label>
|
||||
<description>Probability of precipitation (as %)</description>
|
||||
<state readOnly="true" min="0" max="100" pattern="%d %%">
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="predictability">
|
||||
<item-type>Number</item-type>
|
||||
<label>Forecast Predictability</label>
|
||||
<description>The predictability of the forecast (as %)</description>
|
||||
<state readOnly="true" min="0" max="100" pattern="%d %%">
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="precipitationHours">
|
||||
<item-type>Number</item-type>
|
||||
<label>Precipitation Hours</label>
|
||||
<description>The predicted number of hours having precipitation</description>
|
||||
<state readOnly="true" min="0" max="24" pattern="%d">
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="humidityGreater90Hours">
|
||||
<item-type>Number</item-type>
|
||||
<label>Humidity Greater Than 90 Hours</label>
|
||||
<description>The predicted number of hours having humidity greater than 90%</description>
|
||||
<state readOnly="true" min="0" max="24" pattern="%d">
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="predictabilityClass">
|
||||
<item-type>Number</item-type>
|
||||
<label>Forecast Predictability Class</label>
|
||||
<description>The estimated certainty of the forecast (0 = very low, 5 = very high)</description>
|
||||
<state readOnly="true" min="0" max="5" pattern="%d"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="condition">
|
||||
<item-type>String</item-type>
|
||||
<label>Condition</label>
|
||||
<description>A brief description of the forecast weather condition (e.g. 'Overcast')</description>
|
||||
<state readOnly="true" min="1" max="17" pattern="%s">
|
||||
<options>
|
||||
<!-- Source: https://content.meteoblue.com/nl/service-specifications/standards/symbols-and-pictograms#eztoc14635_1_6 -->
|
||||
<option value="1">Sunny, cloudless sky</option>
|
||||
<option value="2">Sunny and few clouds</option>
|
||||
<option value="3">Partly cloudy</option>
|
||||
<option value="4">Overcast</option>
|
||||
<option value="5">Fog</option>
|
||||
<option value="6">Overcast with rain</option>
|
||||
<option value="7">Mixed with showers</option>
|
||||
<option value="8">Showers, thunderstorms likely</option>
|
||||
<option value="9">Overcast with snow</option>
|
||||
<option value="10">Mixed with snow showers</option>
|
||||
<option value="11">Mostly cloudy with a mixture of snow and rain</option>
|
||||
<option value="12">Overcast with light rain</option>
|
||||
<option value="13">Overcast with light snow</option>
|
||||
<option value="14">Mostly cloudy with rain</option>
|
||||
<option value="15">Mostly cloudy with snow</option>
|
||||
<option value="16">Mostly cloudy with light rain</option>
|
||||
<option value="17">Mostly cloudy with light snow</option>
|
||||
</options>
|
||||
</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>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user