[wundergroundupdatereceiver] Initial contribution (#10105)

* [wundergroundupdatereceiver] Initial implementation

Signed-off-by: Daniel Demus <daniel-github@demus.dk>

* [wundergroundupdatereceiver] Code review

Signed-off-by: Daniel Demus <daniel-github@demus.dk>

* [wundergroundupdatereceiver] Fix some description formatting

Signed-off-by: Daniel Demus <daniel-github@demus.dk>

* [wundergroundupdatereceiver] Add some more channel types per request

Signed-off-by: Daniel Demus <daniel-github@demus.dk>

* [wundergroundupdatereceiver] Add more headers to response



Signed-off-by: Daniel Demus <daniel-github@demus.dk>
Based on the observed headers from actual traffic to wunderground.com.

* [wundergroundupdatereceiver] Discovery service

Signed-off-by: Daniel Demus <daniel-github@demus.dk>

* [wundergroundupdatereceiver] Upgrade to 3.2, fix group name constants

Signed-off-by: Daniel Demus <daniel-github@demus.dk>

* [wundergroundupdatereceiver] Add a list of channel types

Signed-off-by: Daniel Demus <daniel-github@demus.dk>

* [wundergroundupdatereceiver] Add dateutc as synthetic DateTime channel

Also add som emore constants
Signed-off-by: Daniel Demus <daniel-github@demus.dk>

* [wundergroundupdatereceiver] Do more programmatic configuration

Signed-off-by: Daniel Demus <daniel-github@demus.dk>

* [wundergroundupdatereceiver] More readme

Signed-off-by: Daniel Demus <daniel-github@demus.dk>

* [wundergroundupdatereceiver] Update copyright year

Signed-off-by: Daniel Demus <daniel-github@demus.dk>

* [wundergroundupdatereceiver] Handle multiple instance request parameters

Signed-off-by: Daniel Demus <daniel-github@demus.dk>
Ie. those that can have an index number in the name, fx. temp1f, temp2f

* [wundergroundupdatereceiver] Add unmapped but mappable channels

To support multiple devices any new parameters that are submitted cause a new channel to be created
Signed-off-by: Daniel Demus <daniel-github@demus.dk>

* [wundergroundupdatereceiver] Don't pass superfluous config to channels

Also documentation additions
Signed-off-by: Daniel Demus <daniel-github@demus.dk>

* [wundergroundupdatereceiver] Add generated i18n file

Signed-off-by: Daniel Demus <daniel-github@demus.dk>

* [wundergroundupdatereceiver] Re-case test method names

Signed-off-by: Daniel Demus <daniel-github@demus.dk>

* [wundergroundupdatereceiver] Re-word thing file section of README

Signed-off-by: Daniel Demus <daniel-github@demus.dk>

Co-authored-by: Daniel Demus <dde@nine.dk>
This commit is contained in:
Daniel Demus
2022-06-19 15:01:54 +00:00
committed by GitHub
parent 3a632ca659
commit 56f4c8943b
25 changed files with 3245 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.wundergroundupdatereceiver-${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-wundergroundupdatereceiver" description="Wunderground Update Receiver Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.wundergroundupdatereceiver/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,228 @@
/**
* Copyright (c) 2010-2022 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.wundergroundupdatereceiver.internal;
import static org.openhab.core.library.unit.SIUnits.METRE;
import java.util.Set;
import javax.measure.Unit;
import javax.measure.quantity.Length;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.DefaultSystemChannelTypeProvider;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.type.ChannelTypeUID;
import tech.units.indriya.format.SimpleUnitFormat;
import tech.units.indriya.function.MultiplyConverter;
import tech.units.indriya.unit.TransformedUnit;
/**
* The {@link WundergroundUpdateReceiverBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Daniel Demus - Initial contribution
*/
@NonNullByDefault
public class WundergroundUpdateReceiverBindingConstants {
public static final String BINDING_ID = "wundergroundupdatereceiver";
public static final String STATION_ID_PARAMETER = "ID";
public static final String REPRESENTATION_PROPERTY = "stationId";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_UPDATE_RECEIVER = new ThingTypeUID(BINDING_ID,
"wundergroundUpdateReceiver");
static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_UPDATE_RECEIVER);
public static final String NOW = "now";
// Excluded technical paramter names
public static final String REALTIME_MARKER = "realtime";
public static final String PASSWORD = "PASSWORD";
public static final String ACTION = "action";
// List of default synthetic channeltypes added to a new thing
public static final String DATEUTC_DATETIME = "dateutc-datetime";
public static final String LAST_RECEIVED_DATETIME = "last-received-datetime";
public static final String LAST_RECEIVED = "last-received";
public static final String LAST_QUERY = "last-query";
public static final String LAST_QUERY_STATE = LAST_QUERY + "-state";
public static final String LAST_QUERY_TRIGGER = LAST_QUERY + "-trigger";
// Channel groups
public static final String METADATA_GROUP = "metadata";
public static final String WIND_GROUP = "wind";
public static final String TEMPERATURE_GROUP = "temperature";
public static final String HUMIDITY_GROUP = "humidity";
public static final String RAIN_GROUP = "rain";
public static final String SUNLIGHT_GROUP = "sunlight";
public static final String PRESSURE_GROUP = "pressure";
public static final String POLLUTION_GROUP = "pollution";
// Known or observed request paramters received from weather stations submitting to wunderground.com
public static final String DATEUTC = "dateutc";
public static final String SOFTWARE_TYPE = "softwaretype";
public static final String LOW_BATTERY = "lowbatt";
public static final String REALTIME_FREQUENCY = "rtfreq";
public static final String WIND_DIRECTION = "winddir";
public static final String WIND_SPEED = "windspeedmph";
public static final String GUST_SPEED = "windgustmph";
public static final String GUST_DIRECTION = "windgustdir";
public static final String WIND_SPEED_AVG_2MIN = "windspdmph_avg2m";
public static final String WIND_DIRECTION_AVG_2MIN = "winddir_avg2m";
public static final String GUST_SPEED_AVG_10MIN = "windgustmph_10m";
public static final String GUST_DIRECTION_AVG_10MIN = "windgustdir_10m";
public static final String TEMPERATURE = "tempf";
public static final String TEMPERATURE_INDEXED = "^temp(\\d+)f$";
public static final String WIND_CHILL = "windchillf";
public static final String INDOOR_TEMPERATURE = "indoortempf";
public static final String SOIL_TEMPERATURE = "soiltempf";
public static final String SOIL_TEMPERATURE_INDEXED = "^soiltemp(\\d+)f$";
public static final String HUMIDITY = "humidity";
public static final String INDOOR_HUMIDITY = "indoorhumidity";
public static final String DEWPOINT = "dewptf";
public static final String SOIL_MOISTURE = "soilmoisture";
public static final String SOIL_MOISTURE_INDEXED = "^soilmoisture(\\d+)$";
public static final String LEAF_WETNESS = "leafwetness";
public static final String LEAF_WETNESS_INDEXED = "^leafwetness(\\d+)$";
public static final String RAIN_IN = "rainin";
public static final String DAILY_RAIN_IN = "dailyrainin";
public static final String WEEKLY_RAIN_IN = "weeklyrainin";
public static final String MONTHLY_RAIN_IN = "monthlyrainin";
public static final String YEARLY_RAIN_IN = "yearlyrainin";
public static final String SOLAR_RADIATION = "solarradiation";
public static final String UV = "UV";
public static final String VISIBILITY = "visibility";
public static final String WEATHER = "weather";
public static final String CLOUDS = "clouds";
public static final String BAROM_IN = "baromin";
public static final String AQ_NO = "AqNO";
public static final String AQ_NO2T = "AqNO2T";
public static final String AQ_NO2 = "AqNO2";
public static final String AQ_NO2Y = "AqNO2Y";
public static final String AQ_NOX = "AqNOX";
public static final String AQ_NOY = "AqNOY";
public static final String AQ_NO3 = "AqNO3";
public static final String AQ_SO4 = "AqSO4";
public static final String AQ_SO2 = "AqSO2";
public static final String AQ_SO2T = "AqSO2T";
public static final String AQ_CO = "AqCO";
public static final String AQ_COT = "AqCOT";
public static final String AQ_EC = "AqEC";
public static final String AQ_OC = "AqOC";
public static final String AQ_BC = "AqBC";
public static final String AQ_UV_AETH = "AqUV-AETH";
public static final String AQ_PM2_5 = "AqPM2.5";
public static final String AQ_PM10 = "AqPM10";
public static final String AQ_OZONE = "AqOZONE";
public static final ChannelTypeUID LAST_RECEIVED_DATETIME_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID,
LAST_RECEIVED_DATETIME);
public static final ChannelTypeUID LAST_QUERY_STATE_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID,
LAST_QUERY_STATE);
public static final ChannelTypeUID LAST_QUERY_TRIGGER_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID,
LAST_QUERY_TRIGGER);
public static final ChannelTypeUID DATEUTC_DATETIME_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID,
DATEUTC_DATETIME);
public static final ChannelTypeUID DATEUTC_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, DATEUTC);
public static final ChannelTypeUID LOW_BATTERY_CHANNELTYPEUID = DefaultSystemChannelTypeProvider.SYSTEM_CHANNEL_LOW_BATTERY
.getUID();
public static final ChannelTypeUID SOFTWARETYPE_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "softwaretype");
public static final ChannelTypeUID REALTIME_FREQUENCY_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID,
"realtime-frequency");
public static final ChannelTypeUID WIND_SPEED_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "wind-speed");
public static final ChannelTypeUID WIND_DIRECTION_CHANNELTYPEUID = DefaultSystemChannelTypeProvider.SYSTEM_WIND_DIRECTION
.getUID();
public static final ChannelTypeUID GUST_SPEED_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "wind-gust-speed");
public static final ChannelTypeUID GUST_DIRECTION_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID,
"wind-gust-direction");
public static final ChannelTypeUID WIND_SPEED_AVG_2MIN_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID,
"wind-speed-avg-2min");
public static final ChannelTypeUID WIND_DIRECTION_AVG_2MIN_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID,
"wind-direction-avg-2min");
public static final ChannelTypeUID GUST_SPEED_10MIN_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID,
"wind-gust-speed-10min");
public static final ChannelTypeUID GUST_DIRECTION_10MIN_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID,
"wind-gust-direction-10min");
public static final ChannelTypeUID TEMPERATURE_CHANNELTYPEUID = DefaultSystemChannelTypeProvider.SYSTEM_OUTDOOR_TEMPERATURE
.getUID();
public static final ChannelTypeUID HUMIDITY_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "humidity");
public static final ChannelTypeUID INDOOR_HUMIDITY_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID,
"indoor-humidity");
public static final ChannelTypeUID DEW_POINT_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "dew-point");
public static final ChannelTypeUID WIND_CHILL_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "wind-chill");
// for extra outdoor sensors use temp2f, temp3f, and so on
public static final ChannelTypeUID INDOOR_TEMPERATURE_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID,
"indoor-temperature");
// for sensors 2,3,4 use soiltemp2f, soiltemp3f, and soiltemp4f
public static final ChannelTypeUID SOIL_TEMPERATURE_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID,
"soil-temperature");
public static final ChannelTypeUID RAIN_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "rain");
public static final ChannelTypeUID RAIN_DAILY_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "rain-daily");
public static final ChannelTypeUID RAIN_WEEKLY_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "rain-weekly");
public static final ChannelTypeUID RAIN_MONTHLY_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "rain-monthly");
public static final ChannelTypeUID RAIN_YEARLY_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "rain-yearly");
public static final ChannelTypeUID METAR_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "metar");
public static final ChannelTypeUID CLOUDS_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "clouds");
// for sensors 2,3,4 use soilmoisture2, soilmoisture3, and soilmoisture4
public static final ChannelTypeUID SOIL_MOISTURE_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "soil-moisture");
// for sensor 2 use leafwetness2
public static final ChannelTypeUID LEAFWETNESS_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "leafwetness");
public static final ChannelTypeUID SOLARRADIATION_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "solarradiation");
public static final ChannelTypeUID UV_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "uv");
public static final ChannelTypeUID VISIBILITY_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "visibility");
public static final ChannelTypeUID BAROMETRIC_PRESSURE_CHANNELTYPEUID = DefaultSystemChannelTypeProvider.SYSTEM_BAROMETRIC_PRESSURE
.getUID();
public static final ChannelTypeUID NITRIC_OXIDE_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "nitric-oxide");
public static final ChannelTypeUID NITROGEN_DIOXIDE_MEASURED_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID,
"nitrogen-dioxide-measured");
public static final ChannelTypeUID NITROGEN_DIOXIDE_NOX_NO_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID,
"nitrogen-dioxide-nox-no");
public static final ChannelTypeUID NITROGEN_DIOXIDE_NOY_NO_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID,
"nitrogen-dioxide-noy-no");
public static final ChannelTypeUID NITROGEN_OXIDES_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID,
"nitrogen-oxides");
public static final ChannelTypeUID TOTAL_REACTIVE_NITROGEN_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID,
"total-reactive-nitrogen");
public static final ChannelTypeUID NO3_ION_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "no3-ion");
public static final ChannelTypeUID SO4_ION_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "so4-ion");
public static final ChannelTypeUID SULFUR_DIOXIDE_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "sulfur-dioxide");
public static final ChannelTypeUID SULFUR_DIOXIDE_TRACE_LEVELS_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID,
"sulfur-dioxide-trace-levels");
public static final ChannelTypeUID CARBON_MONOXIDE_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID,
"carbon-monoxide");
public static final ChannelTypeUID CARBON_MONOXIDE_TRACE_LEVELS_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID,
"carbon-monoxide-trace-levels");
public static final ChannelTypeUID ELEMENTAL_CARBON_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID,
"elemental-carbon");
public static final ChannelTypeUID ORGANIC_CARBON_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "organic-carbon");
public static final ChannelTypeUID BLACK_CARBON_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "black-carbon");
public static final ChannelTypeUID AETHALOMETER_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "aethalometer");
public static final ChannelTypeUID PM2_5_MASS_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "pm2_5-mass");
public static final ChannelTypeUID PM10_MASS_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "pm10-mass");
public static final ChannelTypeUID OZONE_CHANNELTYPEUID = new ChannelTypeUID(BINDING_ID, "ozone");
public static final Unit<Length> NAUTICAL_MILE = addUnit(
new TransformedUnit<>("NM", METRE, MultiplyConverter.of(1852.0)));
static {
SimpleUnitFormat.getInstance().label(NAUTICAL_MILE, "NM");
}
private static <U extends Unit<?>> U addUnit(U unit) {
return unit;
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2022 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.wundergroundupdatereceiver.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link WundergroundUpdateReceiverConfiguration} class contains fields mapping thing configuration parameters.
*
* @author Daniel Demus - Initial contribution
*/
@NonNullByDefault
public class WundergroundUpdateReceiverConfiguration {
/**
* The station id as registered by wunderground.com or any string if not forwarding data to wunderground.com.
* This value will be used as the representation property.
*/
public String stationId = "";
}

View File

@@ -0,0 +1,129 @@
/**
* Copyright (c) 2010-2022 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.wundergroundupdatereceiver.internal;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.ThingUID;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified;
/**
* @author Daniel Demus - Initial contribution
*/
@NonNullByDefault
@Component(service = { DiscoveryService.class,
WundergroundUpdateReceiverDiscoveryService.class }, configurationPid = "discovery.wundergroundupdatereceiver")
public class WundergroundUpdateReceiverDiscoveryService extends AbstractDiscoveryService {
@Nullable
WundergroundUpdateReceiverServletControls servletControls;
private static final int TIMEOUT_SEC = 1;
private final HashMap<String, Map<String, String[]>> thinglessStationIds = new HashMap<>();
private boolean servletWasInactive = false;
private boolean scanning = false;
@Activate
public WundergroundUpdateReceiverDiscoveryService() throws IllegalArgumentException {
this(true);
}
public WundergroundUpdateReceiverDiscoveryService(boolean useBackgroundDiscovery) throws IllegalArgumentException {
super(WundergroundUpdateReceiverBindingConstants.SUPPORTED_THING_TYPES_UIDS, TIMEOUT_SEC,
useBackgroundDiscovery);
}
public void removeUnhandledStationId(String stationId) {
thinglessStationIds.remove(stationId);
}
public void addUnhandledStationId(@Nullable String stationId, Map<String, String[]> request) {
if (stationId == null || stationId.isEmpty()) {
return;
}
if (!this.thinglessStationIds.containsKey(stationId)) {
this.thinglessStationIds.put(stationId, request);
if (isBackgroundDiscoveryEnabled()) {
createDiscoveryResult(stationId);
}
}
}
public boolean isDiscovering() {
return isBackgroundDiscoveryEnabled() || isScanning();
}
public @Nullable Map<String, String[]> getUnhandledStationRequest(@Nullable String stationId) {
return this.thinglessStationIds.get(stationId);
}
private void createDiscoveryResult(String stationId) {
ThingUID id = new ThingUID(WundergroundUpdateReceiverBindingConstants.THING_TYPE_UPDATE_RECEIVER, stationId);
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(id)
.withRepresentationProperty(WundergroundUpdateReceiverBindingConstants.REPRESENTATION_PROPERTY)
.withProperty(WundergroundUpdateReceiverBindingConstants.REPRESENTATION_PROPERTY, stationId)
.withThingType(WundergroundUpdateReceiverBindingConstants.THING_TYPE_UPDATE_RECEIVER)
.withLabel("WundergroundUpdateReceiver ID " + stationId).build();
this.thingDiscovered(discoveryResult);
}
@Override
@Activate
protected void activate(@Nullable Map<String, Object> configProperties) {
super.activate(configProperties);
}
@Override
@Modified
protected void modified(@Nullable Map<String, Object> configProperties) {
super.modified(configProperties);
}
@Override
protected void startScan() {
setScanning(true);
if (servletControls != null && !servletControls.isActive()) {
servletWasInactive = true;
servletControls.activate();
}
thinglessStationIds.keySet().forEach(this::createDiscoveryResult);
}
@Override
protected synchronized void stopScan() {
super.stopScan();
thinglessStationIds.keySet().forEach(this::createDiscoveryResult);
if (!isBackgroundDiscoveryEnabled() && servletControls != null && servletWasInactive) {
servletControls.deactivate();
}
setScanning(false);
}
protected synchronized boolean isScanning() {
return this.scanning;
}
protected synchronized void setScanning(boolean value) {
this.scanning = value;
}
}

View File

@@ -0,0 +1,214 @@
/**
* Copyright (c) 2010-2022 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.wundergroundupdatereceiver.internal;
import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.*;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import javax.measure.Quantity;
import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelGroupUID;
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.thing.binding.builder.ChannelBuilder;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.type.ChannelType;
import org.openhab.core.thing.type.ChannelTypeRegistry;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link WundergroundUpdateReceiverHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Daniel Demus - Initial contribution
*/
@NonNullByDefault
public class WundergroundUpdateReceiverHandler extends BaseThingHandler {
public String getStationId() {
return config.stationId;
}
private final Logger logger = LoggerFactory.getLogger(WundergroundUpdateReceiverHandler.class);
private final WundergroundUpdateReceiverServlet wundergroundUpdateReceiverServlet;
private final WundergroundUpdateReceiverDiscoveryService discoveryService;
private final WundergroundUpdateReceiverUnknownChannelTypeProvider channelTypeProvider;
private final ChannelTypeRegistry channelTypeRegistry;
private final ChannelUID dateutcDatetimeChannel;
private final ChannelUID lastReceivedChannel;
private final ChannelUID queryStateChannel;
private final ChannelUID queryTriggerChannel;
private WundergroundUpdateReceiverConfiguration config = new WundergroundUpdateReceiverConfiguration();
public WundergroundUpdateReceiverHandler(Thing thing,
WundergroundUpdateReceiverServlet wunderGroundUpdateReceiverServlet,
WundergroundUpdateReceiverDiscoveryService discoveryService,
WundergroundUpdateReceiverUnknownChannelTypeProvider channelTypeProvider,
ChannelTypeRegistry channelTypeRegistry) {
super(thing);
this.discoveryService = discoveryService;
this.channelTypeProvider = channelTypeProvider;
this.channelTypeRegistry = channelTypeRegistry;
final ChannelGroupUID metadatGroupUID = new ChannelGroupUID(getThing().getUID(), METADATA_GROUP);
this.dateutcDatetimeChannel = new ChannelUID(metadatGroupUID, DATEUTC_DATETIME);
this.lastReceivedChannel = new ChannelUID(metadatGroupUID, LAST_RECEIVED);
this.queryTriggerChannel = new ChannelUID(metadatGroupUID, LAST_QUERY_TRIGGER);
this.queryStateChannel = new ChannelUID(metadatGroupUID, LAST_QUERY_STATE);
this.wundergroundUpdateReceiverServlet = Objects.requireNonNull(wunderGroundUpdateReceiverServlet);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.trace("Ignoring command {}", command);
}
@Override
public void handleConfigurationUpdate(Map<String, Object> configurationParameters) {
super.handleConfigurationUpdate(configurationParameters);
this.wundergroundUpdateReceiverServlet.handlerConfigUpdated(this);
}
@Override
public void initialize() {
this.config = getConfigAs(WundergroundUpdateReceiverConfiguration.class);
wundergroundUpdateReceiverServlet.addHandler(this);
@Nullable
Map<String, String[]> requestParameters = discoveryService.getUnhandledStationRequest(config.stationId);
if (requestParameters != null && thing.getChannels().isEmpty()) {
final String[] noValues = new String[0];
ThingBuilder thingBuilder = editThing();
List.of(LAST_RECEIVED, LAST_QUERY_TRIGGER, DATEUTC_DATETIME, LAST_QUERY_STATE)
.forEach((String channelId) -> buildChannel(thingBuilder, channelId, noValues));
requestParameters
.forEach((String parameter, String[] query) -> buildChannel(thingBuilder, parameter, query));
updateThing(thingBuilder.build());
}
discoveryService.removeUnhandledStationId(config.stationId);
if (wundergroundUpdateReceiverServlet.isActive()) {
updateStatus(ThingStatus.ONLINE);
logger.debug("Wunderground update receiver listening for updates to station id {}", config.stationId);
return;
}
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
wundergroundUpdateReceiverServlet.getErrorDetail());
}
@Override
public void dispose() {
wundergroundUpdateReceiverServlet.removeHandler(this.getStationId());
super.dispose();
logger.debug("Wunderground update receiver stopped listening for updates to station id {}", config.stationId);
}
public void updateChannelStates(Map<String, String> requestParameters) {
requestParameters.forEach(this::updateChannelState);
updateState(lastReceivedChannel, new DateTimeType());
updateState(dateutcDatetimeChannel, safeResolvUtcDateTime(requestParameters.getOrDefault(DATEUTC, NOW)));
String lastQuery = requestParameters.getOrDefault(LAST_QUERY, "");
if (lastQuery.isEmpty()) {
return;
}
updateState(queryStateChannel, StringType.valueOf(lastQuery));
triggerChannel(queryTriggerChannel, lastQuery);
}
private void buildChannel(ThingBuilder thingBuilder, String parameter, String... query) {
@Nullable
WundergroundUpdateReceiverParameterMapping channelTypeMapping = WundergroundUpdateReceiverParameterMapping
.getOrCreateMapping(parameter, String.join("", query), channelTypeProvider);
if (channelTypeMapping == null) {
return;
}
ChannelType channelType = channelTypeRegistry.getChannelType(channelTypeMapping.channelTypeId);
if (channelType == null) {
return;
}
ChannelBuilder channelBuilder = ChannelBuilder
.create(new ChannelUID(thing.getUID(), channelTypeMapping.channelGroup, parameter))
.withType(channelTypeMapping.channelTypeId).withAcceptedItemType(channelType.getItemType());
thingBuilder.withChannel(channelBuilder.build());
}
private DateTimeType safeResolvUtcDateTime(String dateUtc) {
if (!dateUtc.isEmpty() && !NOW.equals(dateUtc)) {
try {
// Supposedly the format is "yyyy-MM-dd hh:mm:ss" from the weather station
return new DateTimeType(ZonedDateTime.parse(dateUtc.replace(" ", "T") + "Z"));
} catch (Exception ex) {
logger.warn("The weather station is submitting unparsable datetime values: {}", dateUtc);
}
}
return new DateTimeType();
}
public void updateChannelState(String channelId, String[] stateParts) {
updateChannelState(channelId, String.join("", stateParts));
}
public void updateChannelState(String parameterName, String state) {
Optional<Channel> channel = getThing().getChannels().stream()
.filter(ch -> parameterName.equals(ch.getUID().getIdWithoutGroup())).findFirst();
if (channel.isPresent()) {
ChannelUID channelUID = channel.get().getUID();
@Nullable
Float numberValue = null;
try {
numberValue = Float.valueOf(state);
} catch (NumberFormatException ignored) {
}
if (numberValue == null) {
updateState(channelUID, StringType.valueOf(state));
return;
}
@Nullable
Unit<? extends Quantity<?>> unit = WundergroundUpdateReceiverParameterMapping.getUnit(parameterName);
if (unit != null) {
updateState(channelUID, new QuantityType<>(numberValue, unit));
} else if (LOW_BATTERY.equals(parameterName)) {
updateState(channelUID, OnOffType.from(state));
} else {
updateState(channelUID, new DecimalType(numberValue));
}
} else if (this.discoveryService.isDiscovering()
&& !WundergroundUpdateReceiverParameterMapping.isExcluded(parameterName)) {
ThingBuilder thingBuilder = editThing();
buildChannel(thingBuilder, parameterName, state);
updateThing(thingBuilder.build());
}
}
}

View File

@@ -0,0 +1,85 @@
/**
* Copyright (c) 2010-2022 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.wundergroundupdatereceiver.internal;
import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.THING_TYPE_UPDATE_RECEIVER;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
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.openhab.core.thing.type.ChannelTypeRegistry;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.http.HttpService;
/**
* The {@link WundergroundUpdateReceiverHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Daniel Demus - Initial contribution
*/
@NonNullByDefault
@Component(configurationPid = "binding.wundergroundupdatereceiver", service = ThingHandlerFactory.class)
public class WundergroundUpdateReceiverHandlerFactory extends BaseThingHandlerFactory {
private final WundergroundUpdateReceiverDiscoveryService discoveryService;
private final ChannelTypeRegistry channelTypeRegistry;
private final WundergroundUpdateReceiverUnknownChannelTypeProvider channelTypeProvider;
private final WundergroundUpdateReceiverServlet wunderGroundUpdateReceiverServlet;
@Activate
public WundergroundUpdateReceiverHandlerFactory(@Reference HttpService httpService,
@Reference WundergroundUpdateReceiverDiscoveryService discoveryService,
@Reference WundergroundUpdateReceiverUnknownChannelTypeProvider channelTypeProvider,
@Reference ChannelTypeRegistry channelTypeRegistry) {
this.discoveryService = discoveryService;
this.channelTypeRegistry = channelTypeRegistry;
this.channelTypeProvider = channelTypeProvider;
this.wunderGroundUpdateReceiverServlet = new WundergroundUpdateReceiverServlet(httpService,
this.discoveryService);
this.discoveryService.servletControls = this.wunderGroundUpdateReceiverServlet;
if (this.discoveryService.isBackgroundDiscoveryEnabled()) {
this.wunderGroundUpdateReceiverServlet.activate();
}
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return WundergroundUpdateReceiverBindingConstants.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (THING_TYPE_UPDATE_RECEIVER.equals(thingTypeUID)) {
return new WundergroundUpdateReceiverHandler(thing, this.wunderGroundUpdateReceiverServlet,
this.discoveryService, this.channelTypeProvider, this.channelTypeRegistry);
}
return null;
}
@Override
protected void deactivate(ComponentContext componentContext) {
this.wunderGroundUpdateReceiverServlet.deactivate();
this.wunderGroundUpdateReceiverServlet.dispose();
super.deactivate(componentContext);
}
}

View File

@@ -0,0 +1,198 @@
/**
* Copyright (c) 2010-2022 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.wundergroundupdatereceiver.internal;
import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.*;
import static org.openhab.core.library.unit.ImperialUnits.*;
import static org.openhab.core.library.unit.Units.*;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.regex.Pattern;
import javax.measure.Quantity;
import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.thing.type.ChannelTypeUID;
/**
* @author Daniel Demus - Initial contribution
*/
@NonNullByDefault
public class WundergroundUpdateReceiverParameterMapping {
public final String parameterName;
public final ChannelTypeUID channelTypeId;
public final String channelGroup;
public final @Nullable Unit<? extends Quantity<?>> unit;
public final boolean isIndexable;
public final @Nullable Pattern pattern;
private WundergroundUpdateReceiverParameterMapping(String parameterName, ChannelTypeUID channelTypeId,
String channelGroup, @Nullable Unit<? extends Quantity<?>> unit, boolean isIndexable,
@Nullable Pattern pattern) {
this.parameterName = parameterName;
this.channelTypeId = channelTypeId;
this.channelGroup = channelGroup;
this.unit = unit;
this.isIndexable = isIndexable;
this.pattern = pattern;
}
private static final List<String> UNMAPPED_PARAMETERS = List.of(STATION_ID_PARAMETER, PASSWORD, ACTION,
REALTIME_MARKER);
private static final WundergroundUpdateReceiverParameterMapping[] KNOWN_MAPPINGS = {
new WundergroundUpdateReceiverParameterMapping(LAST_RECEIVED, LAST_RECEIVED_DATETIME_CHANNELTYPEUID,
METADATA_GROUP, null, false, null),
new WundergroundUpdateReceiverParameterMapping(LAST_QUERY_STATE, LAST_QUERY_STATE_CHANNELTYPEUID,
METADATA_GROUP, null, false, null),
new WundergroundUpdateReceiverParameterMapping(LAST_QUERY_TRIGGER, LAST_QUERY_TRIGGER_CHANNELTYPEUID,
METADATA_GROUP, null, false, null),
new WundergroundUpdateReceiverParameterMapping(DATEUTC_DATETIME, DATEUTC_DATETIME_CHANNELTYPEUID,
METADATA_GROUP, null, false, null),
new WundergroundUpdateReceiverParameterMapping(DATEUTC, DATEUTC_CHANNELTYPEUID, METADATA_GROUP, null, false,
null),
new WundergroundUpdateReceiverParameterMapping(SOFTWARE_TYPE, SOFTWARETYPE_CHANNELTYPEUID, METADATA_GROUP,
null, false, null),
new WundergroundUpdateReceiverParameterMapping(LOW_BATTERY, LOW_BATTERY_CHANNELTYPEUID, METADATA_GROUP,
null, false, null),
new WundergroundUpdateReceiverParameterMapping(REALTIME_FREQUENCY, REALTIME_FREQUENCY_CHANNELTYPEUID,
METADATA_GROUP, null, false, null),
new WundergroundUpdateReceiverParameterMapping(WIND_DIRECTION, WIND_DIRECTION_CHANNELTYPEUID, WIND_GROUP,
DEGREE_ANGLE, false, null),
new WundergroundUpdateReceiverParameterMapping(WIND_SPEED, WIND_SPEED_CHANNELTYPEUID, WIND_GROUP,
MILES_PER_HOUR, false, null),
new WundergroundUpdateReceiverParameterMapping(GUST_SPEED, GUST_SPEED_CHANNELTYPEUID, WIND_GROUP,
MILES_PER_HOUR, false, null),
new WundergroundUpdateReceiverParameterMapping(GUST_DIRECTION, GUST_DIRECTION_CHANNELTYPEUID, WIND_GROUP,
DEGREE_ANGLE, false, null),
new WundergroundUpdateReceiverParameterMapping(WIND_SPEED_AVG_2MIN, WIND_SPEED_AVG_2MIN_CHANNELTYPEUID,
WIND_GROUP, MILES_PER_HOUR, false, null),
new WundergroundUpdateReceiverParameterMapping(WIND_DIRECTION_AVG_2MIN,
WIND_DIRECTION_AVG_2MIN_CHANNELTYPEUID, WIND_GROUP, DEGREE_ANGLE, false, null),
new WundergroundUpdateReceiverParameterMapping(GUST_SPEED_AVG_10MIN, GUST_SPEED_10MIN_CHANNELTYPEUID,
WIND_GROUP, MILES_PER_HOUR, false, null),
new WundergroundUpdateReceiverParameterMapping(GUST_DIRECTION_AVG_10MIN,
GUST_DIRECTION_10MIN_CHANNELTYPEUID, WIND_GROUP, DEGREE_ANGLE, false, null),
new WundergroundUpdateReceiverParameterMapping(TEMPERATURE, TEMPERATURE_CHANNELTYPEUID, TEMPERATURE_GROUP,
FAHRENHEIT, true, Pattern.compile(TEMPERATURE_INDEXED)),
new WundergroundUpdateReceiverParameterMapping(INDOOR_TEMPERATURE, INDOOR_TEMPERATURE_CHANNELTYPEUID,
TEMPERATURE_GROUP, FAHRENHEIT, false, null),
new WundergroundUpdateReceiverParameterMapping(SOIL_TEMPERATURE, SOIL_TEMPERATURE_CHANNELTYPEUID,
TEMPERATURE_GROUP, FAHRENHEIT, true, Pattern.compile(SOIL_TEMPERATURE_INDEXED)),
new WundergroundUpdateReceiverParameterMapping(WIND_CHILL, WIND_CHILL_CHANNELTYPEUID, TEMPERATURE_GROUP,
FAHRENHEIT, false, null),
new WundergroundUpdateReceiverParameterMapping(HUMIDITY, HUMIDITY_CHANNELTYPEUID, HUMIDITY_GROUP, PERCENT,
false, null),
new WundergroundUpdateReceiverParameterMapping(INDOOR_HUMIDITY, INDOOR_HUMIDITY_CHANNELTYPEUID,
HUMIDITY_GROUP, PERCENT, false, null),
new WundergroundUpdateReceiverParameterMapping(DEWPOINT, DEW_POINT_CHANNELTYPEUID, HUMIDITY_GROUP,
FAHRENHEIT, false, null),
new WundergroundUpdateReceiverParameterMapping(SOIL_MOISTURE, SOIL_MOISTURE_CHANNELTYPEUID, HUMIDITY_GROUP,
PERCENT, true, Pattern.compile(SOIL_MOISTURE_INDEXED)),
new WundergroundUpdateReceiverParameterMapping(LEAF_WETNESS, LEAFWETNESS_CHANNELTYPEUID, HUMIDITY_GROUP,
PERCENT, true, Pattern.compile(LEAF_WETNESS_INDEXED)),
new WundergroundUpdateReceiverParameterMapping(RAIN_IN, RAIN_CHANNELTYPEUID, RAIN_GROUP, INCH, false, null),
new WundergroundUpdateReceiverParameterMapping(DAILY_RAIN_IN, RAIN_DAILY_CHANNELTYPEUID, RAIN_GROUP, INCH,
false, null),
new WundergroundUpdateReceiverParameterMapping(WEEKLY_RAIN_IN, RAIN_WEEKLY_CHANNELTYPEUID, RAIN_GROUP, INCH,
false, null),
new WundergroundUpdateReceiverParameterMapping(MONTHLY_RAIN_IN, RAIN_MONTHLY_CHANNELTYPEUID, RAIN_GROUP,
INCH, false, null),
new WundergroundUpdateReceiverParameterMapping(YEARLY_RAIN_IN, RAIN_YEARLY_CHANNELTYPEUID, RAIN_GROUP, INCH,
false, null),
new WundergroundUpdateReceiverParameterMapping(SOLAR_RADIATION, SOLARRADIATION_CHANNELTYPEUID,
SUNLIGHT_GROUP, IRRADIANCE, false, null),
new WundergroundUpdateReceiverParameterMapping(UV, UV_CHANNELTYPEUID, SUNLIGHT_GROUP, null, false, null),
new WundergroundUpdateReceiverParameterMapping(VISIBILITY, VISIBILITY_CHANNELTYPEUID, SUNLIGHT_GROUP,
NAUTICAL_MILE, false, null),
new WundergroundUpdateReceiverParameterMapping(WEATHER, METAR_CHANNELTYPEUID, SUNLIGHT_GROUP, null, false,
null),
new WundergroundUpdateReceiverParameterMapping(CLOUDS, CLOUDS_CHANNELTYPEUID, SUNLIGHT_GROUP, null, false,
null),
new WundergroundUpdateReceiverParameterMapping(BAROM_IN, BAROMETRIC_PRESSURE_CHANNELTYPEUID, PRESSURE_GROUP,
INCH_OF_MERCURY, false, null),
new WundergroundUpdateReceiverParameterMapping(AQ_NO, NITRIC_OXIDE_CHANNELTYPEUID, POLLUTION_GROUP,
PARTS_PER_BILLION, false, null),
new WundergroundUpdateReceiverParameterMapping(AQ_NO2, NITROGEN_DIOXIDE_NOX_NO_CHANNELTYPEUID,
POLLUTION_GROUP, PARTS_PER_BILLION, false, null),
new WundergroundUpdateReceiverParameterMapping(AQ_NO2T, NITROGEN_DIOXIDE_MEASURED_CHANNELTYPEUID,
POLLUTION_GROUP, PARTS_PER_BILLION, false, null),
new WundergroundUpdateReceiverParameterMapping(AQ_NO2Y, NITROGEN_DIOXIDE_NOY_NO_CHANNELTYPEUID,
POLLUTION_GROUP, PARTS_PER_BILLION, false, null),
new WundergroundUpdateReceiverParameterMapping(AQ_NOX, NITROGEN_OXIDES_CHANNELTYPEUID, POLLUTION_GROUP,
PARTS_PER_BILLION, false, null),
new WundergroundUpdateReceiverParameterMapping(AQ_NOY, TOTAL_REACTIVE_NITROGEN_CHANNELTYPEUID,
POLLUTION_GROUP, PARTS_PER_BILLION, false, null),
new WundergroundUpdateReceiverParameterMapping(AQ_NO3, NO3_ION_CHANNELTYPEUID, POLLUTION_GROUP,
MICROGRAM_PER_CUBICMETRE, false, null),
new WundergroundUpdateReceiverParameterMapping(AQ_SO2, SULFUR_DIOXIDE_CHANNELTYPEUID, POLLUTION_GROUP,
PARTS_PER_BILLION, false, null),
new WundergroundUpdateReceiverParameterMapping(AQ_SO2T, SULFUR_DIOXIDE_TRACE_LEVELS_CHANNELTYPEUID,
POLLUTION_GROUP, PARTS_PER_BILLION, false, null),
new WundergroundUpdateReceiverParameterMapping(AQ_SO4, SO4_ION_CHANNELTYPEUID, POLLUTION_GROUP,
MICROGRAM_PER_CUBICMETRE, false, null),
new WundergroundUpdateReceiverParameterMapping(AQ_CO, CARBON_MONOXIDE_CHANNELTYPEUID, POLLUTION_GROUP,
PARTS_PER_MILLION, false, null),
new WundergroundUpdateReceiverParameterMapping(AQ_COT, CARBON_MONOXIDE_TRACE_LEVELS_CHANNELTYPEUID,
POLLUTION_GROUP, MICROGRAM_PER_CUBICMETRE, false, null),
new WundergroundUpdateReceiverParameterMapping(AQ_EC, ELEMENTAL_CARBON_CHANNELTYPEUID, POLLUTION_GROUP,
MICROGRAM_PER_CUBICMETRE, false, null),
new WundergroundUpdateReceiverParameterMapping(AQ_OC, ORGANIC_CARBON_CHANNELTYPEUID, POLLUTION_GROUP,
MICROGRAM_PER_CUBICMETRE, false, null),
new WundergroundUpdateReceiverParameterMapping(AQ_BC, BLACK_CARBON_CHANNELTYPEUID, POLLUTION_GROUP,
MICROGRAM_PER_CUBICMETRE, false, null),
new WundergroundUpdateReceiverParameterMapping(AQ_UV_AETH, AETHALOMETER_CHANNELTYPEUID, POLLUTION_GROUP,
MICROGRAM_PER_CUBICMETRE, false, null),
new WundergroundUpdateReceiverParameterMapping(AQ_PM2_5, PM2_5_MASS_CHANNELTYPEUID, POLLUTION_GROUP,
MICROGRAM_PER_CUBICMETRE, false, null),
new WundergroundUpdateReceiverParameterMapping(AQ_PM10, PM10_MASS_CHANNELTYPEUID, POLLUTION_GROUP,
MICROGRAM_PER_CUBICMETRE, false, null),
new WundergroundUpdateReceiverParameterMapping(AQ_OZONE, OZONE_CHANNELTYPEUID, POLLUTION_GROUP,
PARTS_PER_BILLION, false, null) };
public static @Nullable WundergroundUpdateReceiverParameterMapping getOrCreateMapping(String parameterName,
String value, WundergroundUpdateReceiverUnknownChannelTypeProvider channelTypeProvider) {
if (isExcluded(parameterName)) {
return null;
}
Optional<WundergroundUpdateReceiverParameterMapping> knownMapping = lookupMapping(parameterName);
return knownMapping.orElseGet(() -> new WundergroundUpdateReceiverParameterMapping(parameterName,
channelTypeProvider.getOrCreateChannelType(parameterName, value).getUID(), "Uncategorized", null, false,
null));
}
public static boolean isExcluded(String parameter) {
return UNMAPPED_PARAMETERS.contains(parameter);
}
public static @Nullable Unit<? extends Quantity<?>> getUnit(String parameterName) {
Optional<WundergroundUpdateReceiverParameterMapping> mapping = lookupMapping(parameterName);
return mapping
.<Unit<? extends Quantity<?>>> map(
wundergroundUpdateReceiverParameterMapping -> wundergroundUpdateReceiverParameterMapping.unit)
.orElse(null);
}
private static Optional<WundergroundUpdateReceiverParameterMapping> lookupMapping(String parameterName) {
return Arrays.stream(KNOWN_MAPPINGS)
.filter((WundergroundUpdateReceiverParameterMapping m) -> m.parameterName.equals(parameterName)
|| (m.isIndexable && m.pattern != null && m.pattern.matcher(parameterName).matches()))
.findFirst();
}
}

View File

@@ -0,0 +1,56 @@
/**
* Copyright (c) 2010-2022 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.wundergroundupdatereceiver.internal;
import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.profiles.ProfileAdvisor;
import org.openhab.core.thing.profiles.ProfileTypeUID;
import org.openhab.core.thing.profiles.SystemProfiles;
import org.openhab.core.thing.type.ChannelType;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.osgi.service.component.annotations.Component;
/**
* The {@link WundergroundUpdateReceiverProfileAdvisor} suggests the default profile for
* all channels except last updated.
*
* @author Daniel Demus - Initial contribution
*/
@Component
@NonNullByDefault
public class WundergroundUpdateReceiverProfileAdvisor implements ProfileAdvisor {
@Override
public @Nullable ProfileTypeUID getSuggestedProfileTypeUID(Channel channel, @Nullable String itemType) {
return getSuggestedProfileTypeUID(channel.getChannelTypeUID());
}
@Override
public @Nullable ProfileTypeUID getSuggestedProfileTypeUID(ChannelType channelType, @Nullable String itemType) {
return getSuggestedProfileTypeUID(channelType.getUID());
}
private @Nullable ProfileTypeUID getSuggestedProfileTypeUID(@Nullable ChannelTypeUID channelTypeUID) {
if (channelTypeUID == null || !channelTypeUID.getBindingId().equals(BINDING_ID)) {
return null;
}
if (LAST_RECEIVED_DATETIME_CHANNELTYPEUID.equals(channelTypeUID)) {
return SystemProfiles.TIMESTAMP_UPDATE;
}
return SystemProfiles.DEFAULT;
}
}

View File

@@ -0,0 +1,219 @@
/**
* Copyright (c) 2010-2022 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.wundergroundupdatereceiver.internal;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.Instant;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.io.http.servlet.BaseOpenHABServlet;
import org.osgi.service.http.HttpContext;
import org.osgi.service.http.HttpService;
import org.osgi.service.http.NamespaceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link WundergroundUpdateReceiverServlet} is responsible for receiving updates,and
* updating the matching channels.
*
* @author Daniel Demus - Initial contribution
*/
@NonNullByDefault
public class WundergroundUpdateReceiverServlet extends BaseOpenHABServlet
implements WundergroundUpdateReceiverServletControls {
public static final String SERVLET_URL = "/weatherstation/updateweatherstation.php";
private static final long serialVersionUID = -5296703727081438023L;
private final Logger logger = LoggerFactory.getLogger(WundergroundUpdateReceiverServlet.class);
private final Map<String, WundergroundUpdateReceiverHandler> handlers = new HashMap<>();
private static final Object LOCK = new Object();
private final WundergroundUpdateReceiverDiscoveryService discoveryService;
private boolean active = false;
private String errorDetail = "";
public WundergroundUpdateReceiverServlet(HttpService httpService,
WundergroundUpdateReceiverDiscoveryService discoveryService) {
super(httpService);
this.discoveryService = discoveryService;
}
public boolean isActive() {
synchronized (LOCK) {
return this.active;
}
}
public String getErrorDetail() {
synchronized (LOCK) {
return this.errorDetail;
}
}
public Set<String> getStationIds() {
return this.handlers.keySet();
}
public void activate() {
activate(SERVLET_URL, httpService.createDefaultHttpContext());
}
public void addHandler(WundergroundUpdateReceiverHandler handler) {
synchronized (this.handlers) {
if (this.handlers.containsKey(handler.getStationId())) {
errorDetail = "Handler handling request for stationId " + handler.getStationId() + " is already added";
logger.warn("Error during handler registration - StationId {} already being handled",
handler.getStationId());
return;
}
this.handlers.put(handler.getStationId(), handler);
errorDetail = "";
if (!isActive()) {
activate();
}
}
}
public void removeHandler(String stationId) {
synchronized (this.handlers) {
WundergroundUpdateReceiverHandler handler = this.handlers.get(stationId);
if (handler != null) {
this.handlers.remove(stationId);
}
if (this.handlers.isEmpty() && !this.discoveryService.isBackgroundDiscoveryEnabled()) {
deactivate();
}
}
}
public void deactivate() {
synchronized (LOCK) {
logger.debug("Stopping servlet {} at {}", getClass().getSimpleName(), SERVLET_URL);
try {
super.deactivate(SERVLET_URL);
} catch (IllegalArgumentException ignored) {
// SERVLET_URL is already unregistered
}
errorDetail = "";
active = false;
}
}
public void handlerConfigUpdated(WundergroundUpdateReceiverHandler handler) {
synchronized (this.handlers) {
final Set<Map.Entry<String, WundergroundUpdateReceiverHandler>> changedStationIds = this.handlers.entrySet()
.stream().filter(entry -> handler.getThing().getUID().equals(entry.getValue().getThing().getUID()))
.collect(toSet());
changedStationIds.forEach(entry -> {
logger.debug("Re-assigning listener from station id {} to station id {}", entry.getKey(),
handler.getStationId());
this.removeHandler(entry.getKey());
this.addHandler(handler);
});
}
}
public void dispose() {
synchronized (this.handlers) {
Set<String> stationIds = new HashSet<>(getStationIds());
stationIds.forEach(this::removeHandler);
deactivate();
}
}
@Override
protected void activate(String alias, HttpContext httpContext) {
synchronized (LOCK) {
try {
logger.debug("Starting servlet {} at {}", getClass().getSimpleName(), alias);
Dictionary<String, String> props = new Hashtable<>(1, 10);
httpService.registerServlet(alias, this, props, httpContext);
errorDetail = "";
active = true;
} catch (NamespaceException e) {
active = false;
errorDetail = "Servlet couldn't be registered - alias " + alias + " already in use";
logger.warn("Error during servlet registration - alias {} already in use", alias, e);
} catch (ServletException e) {
active = false;
errorDetail = "Servlet couldn't be registered - " + e.getMessage();
logger.warn("Error during servlet registration", e);
}
}
}
@Override
protected void doGet(@Nullable HttpServletRequest req, @Nullable HttpServletResponse resp) throws IOException {
if (!active) {
return;
}
if (req == null) {
return;
}
if (resp == null) {
return;
}
if (req.getRequestURI() == null) {
return;
}
logger.trace("doGet {}", req.getQueryString());
String stationId = req.getParameter(STATION_ID_PARAMETER);
Optional.ofNullable(this.handlers.get(stationId)).ifPresentOrElse(handler -> {
Map<String, String> states = req.getParameterMap().entrySet().stream()
.collect(toMap(Map.Entry::getKey, e -> String.join("", e.getValue())));
String queryString = req.getQueryString();
if (queryString != null && queryString.length() > 0) {
states.put(LAST_QUERY, queryString);
}
handler.updateChannelStates(states);
}, () -> {
this.discoveryService.addUnhandledStationId(stationId, req.getParameterMap());
});
resp.setStatus(HttpServletResponse.SC_OK);
resp.setContentType("text/html;charset=utf-8");
resp.setContentLength(7);
resp.setDateHeader("Date", Instant.now().toEpochMilli());
resp.setHeader("Connection", "close");
PrintWriter writer = resp.getWriter();
writer.write("success");
writer.flush();
writer.close();
}
protected Map<String, WundergroundUpdateReceiverHandler> getHandlers() {
return Collections.unmodifiableMap(this.handlers);
}
}

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2010-2022 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.wundergroundupdatereceiver.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* @author Daniel Demus - Initial contribution
*/
@NonNullByDefault
public interface WundergroundUpdateReceiverServletControls {
void activate();
void deactivate();
boolean isActive();
}

View File

@@ -0,0 +1,83 @@
/**
* Copyright (c) 2010-2022 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.wundergroundupdatereceiver.internal;
import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.THING_TYPE_UPDATE_RECEIVER;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.thing.type.ChannelType;
import org.openhab.core.thing.type.ChannelTypeBuilder;
import org.openhab.core.thing.type.ChannelTypeProvider;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Daniel Demus - Initial contribution
*/
@Component(service = { ChannelTypeProvider.class, WundergroundUpdateReceiverUnknownChannelTypeProvider.class })
@NonNullByDefault
public class WundergroundUpdateReceiverUnknownChannelTypeProvider implements ChannelTypeProvider {
private static final List<String> BOOLEAN_STRINGS = List.of("1", "0", "true", "false");
private final Map<ChannelTypeUID, ChannelType> channelTypes = new ConcurrentHashMap<>();
private final Logger logger = LoggerFactory.getLogger(WundergroundUpdateReceiverUnknownChannelTypeProvider.class);
@Override
public Collection<ChannelType> getChannelTypes(@Nullable Locale locale) {
return channelTypes.values();
}
@Override
public @Nullable ChannelType getChannelType(ChannelTypeUID channelTypeUID, @Nullable Locale locale) {
return channelTypes.get(channelTypeUID);
}
public ChannelType getOrCreateChannelType(String parameterName, String value) {
ChannelTypeUID typeUid = new ChannelTypeUID(THING_TYPE_UPDATE_RECEIVER.getBindingId(), parameterName);
@Nullable
ChannelType type = getChannelType(typeUid, null);
if (type == null) {
String itemType = guessItemType(value);
type = ChannelTypeBuilder.state(typeUid, parameterName + " channel type", itemType).build();
return addChannelType(typeUid, type);
}
return type;
}
private static String guessItemType(String value) {
if (BOOLEAN_STRINGS.contains(value.toLowerCase())) {
return "Switch";
}
try {
Float.valueOf(value);
return "Number";
} catch (NumberFormatException ignored) {
}
return "String";
}
private ChannelType addChannelType(ChannelTypeUID channelTypeUID, ChannelType channelType) {
logger.warn("Adding channelType {} for unknown parameter", channelTypeUID.getAsString());
this.channelTypes.put(channelTypeUID, channelType);
return channelType;
}
}

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="wundergroundupdatereceiver" 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>Wunderground Update Receiver Binding</name>
<description>
This binding enables acting as a receiver of updates from weather stations that post measurements to
https://rtupdate.wunderground.com/weatherstation/updateweatherstation.php.
</description>
</binding:binding>

View File

@@ -0,0 +1,140 @@
# binding
binding.wundergroundupdatereceiver.name = Wunderground Update Receiver Binding
binding.wundergroundupdatereceiver.description = This binding enables acting as a receiver of updates from weather stations that post measurements to https://rtupdate.wunderground.com/weatherstation/updateweatherstation.php.
# thing types
thing-type.wundergroundupdatereceiver.wundergroundUpdateReceiver.label = Update Receiver
thing-type.wundergroundupdatereceiver.wundergroundUpdateReceiver.description = An endpoint thing that can receive and propagate HTTP GET updates meant for a particular station id at wunderground.com
# thing types config
thing-type.config.wundergroundupdatereceiver.wundergroundUpdateReceiver.stationId.label = Station ID
thing-type.config.wundergroundupdatereceiver.wundergroundUpdateReceiver.stationId.description = <br /> The wunderground.com update api requires a station id, that is defined for the WeatherUnderground account measurements are to be submitted to.<br /> <br /> In this binding it is used to identify a unique thing, so each weather-station or other apparatus submitting measurements can have a separate id, but if you don't intend to forward the observations to wunderground.com, this value can be any non-blank string.
# channel group types
channel-group-type.wundergroundupdatereceiver.humidity-group.label = Moisture
channel-group-type.wundergroundupdatereceiver.humidity-group.description = Moisture measurements
channel-group-type.wundergroundupdatereceiver.metadata-group.label = Metadata
channel-group-type.wundergroundupdatereceiver.metadata-group.description = Metadata regarding the last submission
channel-group-type.wundergroundupdatereceiver.pollution-group.label = Pollution
channel-group-type.wundergroundupdatereceiver.pollution-group.description = Pollutant measurements
channel-group-type.wundergroundupdatereceiver.pressure-group.label = Pressure
channel-group-type.wundergroundupdatereceiver.pressure-group.description = Barometric measurements
channel-group-type.wundergroundupdatereceiver.rain-group.label = Rain
channel-group-type.wundergroundupdatereceiver.rain-group.description = Rain measurements
channel-group-type.wundergroundupdatereceiver.sunlight-group.label = Sunlight
channel-group-type.wundergroundupdatereceiver.sunlight-group.description = Sunlight measurements
channel-group-type.wundergroundupdatereceiver.temperature-group.label = Temperature
channel-group-type.wundergroundupdatereceiver.temperature-group.description = Temperature measurements
channel-group-type.wundergroundupdatereceiver.wind-group.label = Wind
channel-group-type.wundergroundupdatereceiver.wind-group.description = Wind measurements
# channel types
channel-type.wundergroundupdatereceiver.aethalometer.label = Second Channel of Aethalometer
channel-type.wundergroundupdatereceiver.aethalometer.description = second channel of Aethalometer at 370 nm, µG/m3.
channel-type.wundergroundupdatereceiver.black-carbon.label = Black Carbon
channel-type.wundergroundupdatereceiver.black-carbon.description = Black Carbon at 880 nm, µG/m3.
channel-type.wundergroundupdatereceiver.carbon-monoxide-trace-levels.label = Carbon Monoxide Trace Levels
channel-type.wundergroundupdatereceiver.carbon-monoxide-trace-levels.description = Carbon Monoxide, trace levels ppb.
channel-type.wundergroundupdatereceiver.carbon-monoxide.label = Carbon Monoxide
channel-type.wundergroundupdatereceiver.carbon-monoxide.description = Carbon Monoxide, conventional ppm.
channel-type.wundergroundupdatereceiver.clouds.label = Cloud Cover
channel-type.wundergroundupdatereceiver.clouds.description = METAR style cloud cover.
channel-type.wundergroundupdatereceiver.dateutc-datetime.label = Last Updated as DateTime
channel-type.wundergroundupdatereceiver.dateutc-datetime.description = The date and time of the last update in UTC as submitted by the weather station converted to a DateTime value. In case of 'now', the current time is used.
channel-type.wundergroundupdatereceiver.dateutc-datetime.state.pattern = %1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS
channel-type.wundergroundupdatereceiver.dateutc.label = Last Updated
channel-type.wundergroundupdatereceiver.dateutc.description = The date and time of the last update in UTC as submitted by the weather station. This can be 'now'.
channel-type.wundergroundupdatereceiver.dew-point.label = Dew Point
channel-type.wundergroundupdatereceiver.dew-point.description = Outdoor dew point.
channel-type.wundergroundupdatereceiver.elemental-carbon.label = Elemental Carbon
channel-type.wundergroundupdatereceiver.elemental-carbon.description = Elemental Carbon, PM2.5 µG/m3.
channel-type.wundergroundupdatereceiver.humidity.label = Humidity
channel-type.wundergroundupdatereceiver.humidity.description = Humidity in %.
channel-type.wundergroundupdatereceiver.indoor-humidity.label = Indoor Humidity
channel-type.wundergroundupdatereceiver.indoor-humidity.description = Indoor humidity in %.
channel-type.wundergroundupdatereceiver.indoor-temperature.label = Indoor Temperature
channel-type.wundergroundupdatereceiver.indoor-temperature.description = Indoor temperature.
channel-type.wundergroundupdatereceiver.last-query-state.label = The last query
channel-type.wundergroundupdatereceiver.last-query-state.description = The part of the last query after the first unurlencoded '?'
channel-type.wundergroundupdatereceiver.last-query-trigger.label = The last query
channel-type.wundergroundupdatereceiver.last-query-trigger.description = The part of the last query after the first unurlencoded '?'
channel-type.wundergroundupdatereceiver.last-received-datetime.label = Last Received
channel-type.wundergroundupdatereceiver.last-received-datetime.description = The date and time of the last update.
channel-type.wundergroundupdatereceiver.last-received-datetime.state.pattern = %1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS
channel-type.wundergroundupdatereceiver.leafwetness.label = Leaf Wetness
channel-type.wundergroundupdatereceiver.leafwetness.description = Leaf wetness in %.
channel-type.wundergroundupdatereceiver.metar.label = METAR Weather Report
channel-type.wundergroundupdatereceiver.metar.description = METAR formatted weather report
channel-type.wundergroundupdatereceiver.nitric-oxide.label = Nitric Oxide
channel-type.wundergroundupdatereceiver.nitric-oxide.description = Nitric Oxide ppm.
channel-type.wundergroundupdatereceiver.nitrogen-dioxide-measured.label = Nitrogen Dioxide
channel-type.wundergroundupdatereceiver.nitrogen-dioxide-measured.description = Nitrogen Dioxide, true measure ppb.
channel-type.wundergroundupdatereceiver.nitrogen-dioxide-nox-no.label = NO2 X computed
channel-type.wundergroundupdatereceiver.nitrogen-dioxide-nox-no.description = NO2 computed, NOx-NO ppb.
channel-type.wundergroundupdatereceiver.nitrogen-dioxide-noy-no.label = NO2 Y computed, NOy-NO ppb
channel-type.wundergroundupdatereceiver.nitrogen-dioxide-noy-no.description = NO2 computed, NOy-NO ppb.
channel-type.wundergroundupdatereceiver.nitrogen-oxides.label = Nitrogen Oxides
channel-type.wundergroundupdatereceiver.nitrogen-oxides.description = Nitrogen Oxides ppb.
channel-type.wundergroundupdatereceiver.no3-ion.label = NO3 ion
channel-type.wundergroundupdatereceiver.no3-ion.description = NO3 ion (nitrate, not adjusted for ammonium ion) µG/m3.
channel-type.wundergroundupdatereceiver.organic-carbon.label = Organic Carbon
channel-type.wundergroundupdatereceiver.organic-carbon.description = Organic Carbon, not adjusted for oxygen and hydrogen, PM2.5 µG/m3.
channel-type.wundergroundupdatereceiver.ozone.label = Ozone
channel-type.wundergroundupdatereceiver.ozone.description = Ozone, ppb.
channel-type.wundergroundupdatereceiver.pm10-mass.label = PM10 Mass
channel-type.wundergroundupdatereceiver.pm10-mass.description = PM10 mass, µG/m3.
channel-type.wundergroundupdatereceiver.pm2_5-mass.label = PM2.5 Mass
channel-type.wundergroundupdatereceiver.pm2_5-mass.description = PM2.5 mass, µG/m3.
channel-type.wundergroundupdatereceiver.rain-daily.label = Daily Rain
channel-type.wundergroundupdatereceiver.rain-daily.description = Rain since the start of the day.
channel-type.wundergroundupdatereceiver.rain-monthly.label = Monthly Rain
channel-type.wundergroundupdatereceiver.rain-monthly.description = Rain since the start if this month.
channel-type.wundergroundupdatereceiver.rain-weekly.label = Weekly Rain
channel-type.wundergroundupdatereceiver.rain-weekly.description = Rain since the start of this week.
channel-type.wundergroundupdatereceiver.rain-yearly.label = Yearly Rain
channel-type.wundergroundupdatereceiver.rain-yearly.description = Rain since the start of this year.
channel-type.wundergroundupdatereceiver.rain.label = Hourly Rain
channel-type.wundergroundupdatereceiver.rain.description = Rain over the past hour.
channel-type.wundergroundupdatereceiver.realtime-frequency.label = Realtime Frequency
channel-type.wundergroundupdatereceiver.realtime-frequency.description = How often does the weather station submit measurements
channel-type.wundergroundupdatereceiver.so4-ion.label = SO4 ion
channel-type.wundergroundupdatereceiver.so4-ion.description = SO4 ion (sulfate, not adjusted for ammonium ion) µG/m3.
channel-type.wundergroundupdatereceiver.softwaretype.label = Software Type
channel-type.wundergroundupdatereceiver.softwaretype.description = A software type string from the weather station
channel-type.wundergroundupdatereceiver.soil-moisture.label = Soil Moisture
channel-type.wundergroundupdatereceiver.soil-moisture.description = Soil moisture in %.
channel-type.wundergroundupdatereceiver.soil-temperature.label = Soil Temperature
channel-type.wundergroundupdatereceiver.soil-temperature.description = Soil temperature.
channel-type.wundergroundupdatereceiver.solarradiation.label = Solar Radiation
channel-type.wundergroundupdatereceiver.solarradiation.description = Solar radiation
channel-type.wundergroundupdatereceiver.sulfur-dioxide-trace-levels.label = Sulfur Dioxide Trace Levels
channel-type.wundergroundupdatereceiver.sulfur-dioxide-trace-levels.description = Sulfur Dioxide, trace levels ppb.
channel-type.wundergroundupdatereceiver.sulfur-dioxide.label = Sulfur Dioxide
channel-type.wundergroundupdatereceiver.sulfur-dioxide.description = Sulfur Dioxide, conventional ppb.
channel-type.wundergroundupdatereceiver.total-reactive-nitrogen.label = Total Reactive Nitrogen
channel-type.wundergroundupdatereceiver.total-reactive-nitrogen.description = Total reactive nitrogen.
channel-type.wundergroundupdatereceiver.uv.label = UV Index
channel-type.wundergroundupdatereceiver.uv.description = UV index.
channel-type.wundergroundupdatereceiver.visibility.label = Visibility
channel-type.wundergroundupdatereceiver.visibility.description = Visibility.
channel-type.wundergroundupdatereceiver.wind-chill.label = Wind Chill
channel-type.wundergroundupdatereceiver.wind-chill.description = The apparent wind chill temperature.
channel-type.wundergroundupdatereceiver.wind-direction-avg-2min.label = Wind Direction 2min Average
channel-type.wundergroundupdatereceiver.wind-direction-avg-2min.description = 2 minute average wind direction.
channel-type.wundergroundupdatereceiver.wind-gust-direction-10min.label = Gust Direction 10min Average
channel-type.wundergroundupdatereceiver.wind-gust-direction-10min.description = 10 minute average gust direction.
channel-type.wundergroundupdatereceiver.wind-gust-direction.label = Gust Direction
channel-type.wundergroundupdatereceiver.wind-gust-direction.description = Current wind gust direction expressed as an angle using software specific time period.
channel-type.wundergroundupdatereceiver.wind-gust-speed-10min.label = Gust Speed 10min Average
channel-type.wundergroundupdatereceiver.wind-gust-speed-10min.description = 10 minute average gust speed.
channel-type.wundergroundupdatereceiver.wind-gust-speed.label = Current Gust Speed
channel-type.wundergroundupdatereceiver.wind-gust-speed.description = Current wind gust speed, using software specific time period.
channel-type.wundergroundupdatereceiver.wind-speed-avg-2min.label = Wind Speed 2min Average
channel-type.wundergroundupdatereceiver.wind-speed-avg-2min.description = 2 minute average wind speed.
channel-type.wundergroundupdatereceiver.wind-speed.label = Current Wind Speed
channel-type.wundergroundupdatereceiver.wind-speed.description = Current wind speed, using software specific time period.

View File

@@ -0,0 +1,129 @@
binding.wundergroundupdatereceiver.name=Wundergroundopdateringsmodtager Binding
binding.wundergroundupdatereceiver.description=Denne binding gør det muligt at modtage opdateringer fra vejrstationer som sender målinger til https://rtupdate.wunderground.com/weatherstation/updateweatherstation.php.
thing-type.wundergroundupdatereceiver.description=En endpoint til modtagelse og videreformidling af opdateringer fra vejrstationer, som kan opdatere wunderground.com
thing-type.wundergroundupdatereceiver.config.label=Stationsid som er konfigureret i vejstationen.
thing-type.wundergroundupdatereceiver.config.description=<![CDATA[<br /> \
wunderground.com api'en kræver en stationsid, som stammer fra WeatherUnderground \
kontoen man sender målinger til.<br /> \
<br /> \
Her bliver den brugt til at skelne mellem ting instanser, så flere vejrstationers opdateringer kan håndteres \
eller flere slags måleenheders målinger kan aggregeres. \
Hvis man ikke agter at videresende målinger til wunderground.com, kan man sætte dette til en ikke-tom streng.
channel-group-type.wundergroundupdatereceiver.metadata-group.description=Metadata fra den sidste opdatering
channel-group-type.wundergroundupdatereceiver.wind-group.description=Vindmålinger
channel-group-type.wundergroundupdatereceiver.wind-group.label=Vind
channel-group-type.wundergroundupdatereceiver.temperature-group.description=Temperaturmålinger
channel-group-type.wundergroundupdatereceiver.temperature-group.label=Temperatur
channel-group-type.wundergroundupdatereceiver.humidity-group.description=Fugtighedsmålinger
channel-group-type.wundergroundupdatereceiver.humidity-group.label=Fugtighed
channel-group-type.wundergroundupdatereceiver.rain-group.description=Regnmålinger
channel-group-type.wundergroundupdatereceiver.rain-group.label=Regn
channel-group-type.wundergroundupdatereceiver.sunlight-group.description=Lysmålinger
channel-group-type.wundergroundupdatereceiver.sunlight-group.label=Sollys
channel-group-type.wundergroundupdatereceiver.pressure-group.description=Barometertrykmålinger
channel-group-type.wundergroundupdatereceiver.pressure-group.label=Barometertryk
channel-group-type.wundergroundupdatereceiver.pollution-group.description=Målinger af atmosfærisk forurening
channel-group-type.wundergroundupdatereceiver.pollution-group.label=Forurening
channel-type.wundergroundupdatereceiver.last-received-datetime.description=Tidspunkt for sidst modtaget opdatering fra stationen
channel-type.wundergroundupdatereceiver.last-received-datetime.label=Sidst modtaget
channel-type.wundergroundupdatereceiver.dateutc.description=Stationens værdi for sidst opdateringstidspunkt. Kan være 'now'
channel-type.wundergroundupdatereceiver.dateutc.label=Sidst opdateret
channel-type.system.low-battery.description=Stationens batteri er ved være flad
channel-type.system.low-battery.label=Lav batteri
channel-type.wundergroundupdatereceiver.realtime-frequency.description=Stationens angivelse af opdateringsfrekvensen
channel-type.wundergroundupdatereceiver.realtime-frequency.label=Opdateringsfrekvens
channel-type.wundergroundupdatereceiver.wind-speed.description=Øjeblikkelig vindhastighed i mph
channel-type.wundergroundupdatereceiver.wind-speed.label=Vindhastighed
channel-type.wundergroundupdatereceiver.wind-gust-speed.description=Øjeblikkelig vindstødshastighed
channel-type.wundergroundupdatereceiver.wind-gust-speed.label=Vindstødshastighed
channel-type.system.wind-direction.description=Øjeblikkelig vindretning som vinkel
channel-type.system.wind-direction.label=Vindretning
channel-type.wundergroundupdatereceiver.wind-speed-avg-2min.description=Gennemsnitlig vindhastighed i de sidste 2 minutter
channel-type.wundergroundupdatereceiver.wind-speed-avg-2min.label=Gns. vindhastighed (2min)
channel-type.wundergroundupdatereceiver.wind-direction-avg-2min.description=Gennemsnitlig vindretning i de sidste 2 minutter som vinkel
channel-type.wundergroundupdatereceiver.wind-direction-avg-2min.label=Gns. vindretning (2min)
channel-type.wundergroundupdatereceiver.wind-gust-speed-10min.description=Gennemsnitlig vindstæshastighed i de sidste 10 minutter
channel-type.wundergroundupdatereceiver.wind-gust-speed-10min.label=Gns. vindstødshastighed (10min)
channel-type.wundergroundupdatereceiver.wind-gust-direction-10min.description=Gennemsnitlig vindstødsretning i de sidste 10 minutter som vinkel
channel-type.wundergroundupdatereceiver.wind-gust-direction-10min.label=Gns. vindstødsretning (10min)
channel-type.wundergroundupdatereceiver.humidity.description=Udendørs fugtighed i pct
channel-type.wundergroundupdatereceiver.humidity.label=Udendørs fugtighed
channel-type.wundergroundupdatereceiver.indoor-humidity.description=Indendørs fugtighed i pct
channel-type.wundergroundupdatereceiver.indoor-humidity.label=Indendørs fugtighed
channel-type.wundergroundupdatereceiver.dew-point.description=Dugpunkt
channel-type.wundergroundupdatereceiver.dew-point.label=Dugpunkt
channel-type.wundergroundupdatereceiver.wind-chill.description=Kuldeindeks
channel-type.wundergroundupdatereceiver.wind-chill.label=Kuldeindeks
channel-type.system.temperature.description=Udendørs temperatur
channel-type.system.temperature.label=Udendørs temperatur
channel-type.wundergroundupdatereceiver.indoor-temperature.description=Indendørs temperatur
channel-type.wundergroundupdatereceiver.indoor-temperature.label=Indendørs temperatur
channel-type.wundergroundupdatereceiver.soil-temperature.description=Jordtemeperatur
channel-type.wundergroundupdatereceiver.soil-temperature.label=Jordtemeperatur
channel-type.wundergroundupdatereceiver.rain.description=Regnmængde i den sidste time
channel-type.wundergroundupdatereceiver.rain.label=Regn (sidste time)
channel-type.wundergroundupdatereceiver.rain-daily.description=Regnmængde siden starten af dagen
channel-type.wundergroundupdatereceiver.rain-daily.label=Regn (i dag)
channel-type.wundergroundupdatereceiver.rain-weekly.description=Regnmængde siden starten af ugen
channel-type.wundergroundupdatereceiver.rain-weekly.label=Regn (denne uge)
channel-type.wundergroundupdatereceiver.rain-monthly.description=Regnmængde siden starten af måneden
channel-type.wundergroundupdatereceiver.rain-monthly.label=Regn (denne måned)
channel-type.wundergroundupdatereceiver.rain-yearly.description=Regnmængde siden starten af året
channel-type.wundergroundupdatereceiver.rain-yearly.label=Regn (i år)
channel-type.wundergroundupdatereceiver.metar.description=Vejrmelding i METAR format
channel-type.wundergroundupdatereceiver.metar.label=METAR vejrmelding
channel-type.wundergroundupdatereceiver.clouds.description=Skydække
channel-type.wundergroundupdatereceiver.clouds.label=Skydække i METAR format
channel-type.wundergroundupdatereceiver.soil-moisture.description=Jordfugtighed i pct
channel-type.wundergroundupdatereceiver.soil-moisture.label=Jordfugtighed
channel-type.wundergroundupdatereceiver.leafwetness.description=Bladfugtighed i pct
channel-type.wundergroundupdatereceiver.leafwetness.label=Bladfugtighed
channel-type.wundergroundupdatereceiver.solarradiation.description=Solstråling i W/m2
channel-type.wundergroundupdatereceiver.solarradiation.label=Solstråling
channel-type.wundergroundupdatereceiver.uv.description=UV index
channel-type.wundergroundupdatereceiver.uv.label=UV index
channel-type.wundergroundupdatereceiver.visibility.description=Sigtbarhed
channel-type.wundergroundupdatereceiver.visibility.label=Sigtbarhed
channel-type.wundergroundupdatereceiver.nitric-oxide.description=
channel-type.wundergroundupdatereceiver.nitric-oxide.label=Nitrogenoxid
channel-type.wundergroundupdatereceiver.nitrogen-dioxide-measured.description=Målt nitrogendioxid i ppb
channel-type.wundergroundupdatereceiver.nitrogen-dioxide-measured.label=Nitrogendioxid
channel-type.wundergroundupdatereceiver.nitrogen-dioxide-nox-no.description=Beregnet nitrogendioxid i ppb
channel-type.wundergroundupdatereceiver.nitrogen-dioxide-nox-no.label=Beregnet nitrogendioxid
channel-type.wundergroundupdatereceiver.nitrogen-dioxide-noy-no.description=Beregnet nitrogendioxid Y
channel-type.wundergroundupdatereceiver.nitrogen-dioxide-noy-no.label=Beregnet nitrogendioxid Y
channel-type.wundergroundupdatereceiver.nitrogen-oxides.description=Nitrogenoxider
channel-type.wundergroundupdatereceiver.nitrogen-oxides.label=Nitrogenoxid i ppb
channel-type.wundergroundupdatereceiver.total-reactive-nitrogen.description=Reactive nitrogen ialt
channel-type.wundergroundupdatereceiver.total-reactive-nitrogen.label=Reactive nitrogen ialt
channel-type.wundergroundupdatereceiver.no3-ion.description=Nitrat tæthed
channel-type.wundergroundupdatereceiver.no3-ion.label=Nitrat
channel-type.wundergroundupdatereceiver.so4-ion.description=Sulfat tæthed
channel-type.wundergroundupdatereceiver.so4-ion.label=Sulfat
channel-type.wundergroundupdatereceiver.sulfur-dioxide.description=Svovldioxid i ppb
channel-type.wundergroundupdatereceiver.sulfur-dioxide.label=Svovldioxid
channel-type.wundergroundupdatereceiver.sulfur-dioxide-trace-levels.description=Svovldioxid sporelementer
channel-type.wundergroundupdatereceiver.sulfur-dioxide-trace-levels.label=Svovldioxid sporelementer
channel-type.wundergroundupdatereceiver.elemental-carbon.description=Kulstofselementer
channel-type.wundergroundupdatereceiver.elemental-carbon.label=Kulstofselementer
channel-type.wundergroundupdatereceiver.organic-carbon.description=Organisk kulstof
channel-type.wundergroundupdatereceiver.organic-carbon.label=Organisk kulstof
channel-type.wundergroundupdatereceiver.black-carbon.description=Sort kulstof
channel-type.wundergroundupdatereceiver.black-carbon.label=Sort kulstof
channel-type.wundergroundupdatereceiver.aethalometer.description=Aethalometer
channel-type.wundergroundupdatereceiver.aethalometer.label=Aethalometer
channel-type.wundergroundupdatereceiver.pm2_5-mass.description=PM2.5 massetæthed
channel-type.wundergroundupdatereceiver.pm2_5-mass.label=PM2.5 massetæthed
channel-type.wundergroundupdatereceiver.pm10-mass.description=PM10 massetæthed
channel-type.wundergroundupdatereceiver.pm10-mass.label=PM10 massetæthed
channel-type.wundergroundupdatereceiver.ozone.description=Ozon
channel-type.wundergroundupdatereceiver.ozone.label=Ozon
channel-type.wundergroundupdatereceiver.softwaretype.description=En software type streng fra vejrstationen
channel-type.wundergroundupdatereceiver.softwaretype.label=Software type
channel-type.wundergroundupdatereceiver.last-query-state.description=Querystring delen af den sidste query
channel-type.wundergroundupdatereceiver.last-query-state.label=Den sidste query
channel-type.wundergroundupdatereceiver.last-query-trigger.description=Querystring delen af den sidste query
channel-type.wundergroundupdatereceiver.last-query-trigger.label=Den sidste query

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" ?>
<thing:thing-descriptions bindingId="wundergroundupdatereceiver"
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">
<channel-group-type id="metadata-group">
<label>Metadata</label>
<description>Metadata regarding the last submission</description>
<category>Text</category>
</channel-group-type>
<channel-group-type id="wind-group">
<label>Wind</label>
<description>Wind measurements</description>
<category>Wind</category>
</channel-group-type>
<channel-group-type id="temperature-group">
<label>Temperature</label>
<description>Temperature measurements</description>
<category>Temperature</category>
</channel-group-type>
<channel-group-type id="humidity-group">
<label>Moisture</label>
<description>Moisture measurements</description>
<category>Humidity</category>
</channel-group-type>
<channel-group-type id="rain-group">
<label>Rain</label>
<description>Rain measurements</description>
<category>Rain</category>
</channel-group-type>
<channel-group-type id="sunlight-group">
<label>Sunlight</label>
<description>Sunlight measurements</description>
<category>Sun_Clouds</category>
</channel-group-type>
<channel-group-type id="pressure-group">
<label>Pressure</label>
<description>Barometric measurements</description>
<category>Pressure</category>
</channel-group-type>
<channel-group-type id="pollution-group">
<label>Pollution</label>
<description>Pollutant measurements</description>
<category>Pollution</category>
</channel-group-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,580 @@
<?xml version="1.0" ?>
<thing:thing-descriptions bindingId="wundergroundupdatereceiver"
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">
<channel-type id="last-received-datetime">
<item-type>DateTime</item-type>
<label>Last Received</label>
<description>The date and time of the last update.</description>
<category>Time</category>
<tags>
<tag>Point</tag>
<tag>Timestamp</tag>
</tags>
<state readOnly="true" pattern="%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS"/>
</channel-type>
<channel-type id="dateutc" advanced="true">
<item-type>String</item-type>
<label>Last Updated</label>
<description>The date and time of the last update in UTC as submitted by the weather station. This can be 'now'.</description>
<category>Time</category>
<tags>
<tag>Point</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="dateutc-datetime" advanced="true">
<item-type>DateTime</item-type>
<label>Last Updated as DateTime</label>
<description>The date and time of the last update in UTC as submitted by the weather station converted to a DateTime
value. In case of 'now', the current time is used.</description>
<category>Time</category>
<tags>
<tag>Point</tag>
<tag>Timestamp</tag>
</tags>
<state readOnly="true" pattern="%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS"/>
</channel-type>
<channel-type id="wind-speed">
<item-type>Number:Speed</item-type>
<label>Current Wind Speed</label>
<description>Current wind speed, using software specific time period.</description>
<category>Wind</category>
<tags>
<tag>Measurement</tag>
<tag>Wind</tag>
</tags>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
<channel-type id="wind-gust-speed">
<item-type>Number:Speed</item-type>
<label>Current Gust Speed</label>
<description>Current wind gust speed, using software specific time period.</description>
<category>Wind</category>
<tags>
<tag>Measurement</tag>
<tag>Wind</tag>
</tags>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
<channel-type id="wind-gust-direction">
<item-type>Number:Angle</item-type>
<label>Gust Direction</label>
<description>Current wind gust direction expressed as an angle using software specific time period.</description>
<category>Wind</category>
<tags>
<tag>Measurement</tag>
<tag>Wind</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="wind-speed-avg-2min" advanced="true">
<item-type>Number:Speed</item-type>
<label>Wind Speed 2min Average</label>
<description>2 minute average wind speed.</description>
<category>Wind</category>
<tags>
<tag>Measurement</tag>
<tag>Wind</tag>
</tags>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
<channel-type id="wind-direction-avg-2min" advanced="true">
<item-type>Number:Angle</item-type>
<label>Wind Direction 2min Average</label>
<description>2 minute average wind direction.</description>
<category>Wind</category>
<tags>
<tag>Measurement</tag>
<tag>Wind</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="wind-gust-speed-10min" advanced="true">
<item-type>Number:Speed</item-type>
<label>Gust Speed 10min Average</label>
<description>10 minute average gust speed.</description>
<category>Wind</category>
<tags>
<tag>Measurement</tag>
<tag>Wind</tag>
</tags>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
<channel-type id="wind-gust-direction-10min" advanced="true">
<item-type>Number:Angle</item-type>
<label>Gust Direction 10min Average</label>
<description>10 minute average gust direction.</description>
<category>Wind</category>
<tags>
<tag>Measurement</tag>
<tag>Wind</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="humidity">
<item-type>Number:Dimensionless</item-type>
<label>Humidity</label>
<description>Humidity in %.</description>
<category>Humidity</category>
<tags>
<tag>Measurement</tag>
<tag>Humidity</tag>
</tags>
<state readOnly="true" pattern="%.0f %%"/>
</channel-type>
<channel-type id="indoor-humidity">
<item-type>Number:Dimensionless</item-type>
<label>Indoor Humidity</label>
<description>Indoor humidity in %.</description>
<category>Humidity</category>
<tags>
<tag>Measurement</tag>
<tag>Humidity</tag>
</tags>
<state readOnly="true" pattern="%.0f %%"/>
</channel-type>
<channel-type id="dew-point" advanced="true">
<item-type>Number:Temperature</item-type>
<label>Dew Point</label>
<description>Outdoor dew point.</description>
<category>Humidity</category>
<tags>
<tag>Measurement</tag>
<tag>Temperature</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="wind-chill" advanced="true">
<item-type>Number:Temperature</item-type>
<label>Wind Chill</label>
<description>The apparent wind chill temperature.</description>
<category>Temperature</category>
<tags>
<tag>Measurement</tag>
<tag>Wind</tag>
</tags>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<!-- for extra outdoor sensors use temp2f, temp3f, and so on -->
<channel-type id="indoor-temperature">
<item-type>Number:Temperature</item-type>
<label>Indoor Temperature</label>
<description>Indoor temperature.</description>
<category>Temperature</category>
<tags>
<tag>Measurement</tag>
<tag>Temperature</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<!-- for sensors 2,3,4 use soiltemp2f, soiltemp3f, and soiltemp4f -->
<channel-type id="soil-temperature" advanced="true">
<item-type>Number:Temperature</item-type>
<label>Soil Temperature</label>
<description>Soil temperature.</description>
<category>Temperature</category>
<tags>
<tag>Measurement</tag>
<tag>Temperature</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="rain">
<item-type>Number:Length</item-type>
<label>Hourly Rain</label>
<description>Rain over the past hour.</description>
<category>Rain</category>
<tags>
<tag>Measurement</tag>
<tag>Rain</tag>
</tags>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
<channel-type id="rain-daily">
<item-type>Number:Length</item-type>
<label>Daily Rain</label>
<description>Rain since the start of the day.</description>
<category>Rain</category>
<tags>
<tag>Measurement</tag>
<tag>Rain</tag>
</tags>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
<channel-type id="rain-weekly" advanced="true">
<item-type>Number:Length</item-type>
<label>Weekly Rain</label>
<description>Rain since the start of this week.</description>
<category>Rain</category>
<tags>
<tag>Measurement</tag>
<tag>Rain</tag>
</tags>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
<channel-type id="rain-monthly" advanced="true">
<item-type>Number:Length</item-type>
<label>Monthly Rain</label>
<description>Rain since the start if this month.</description>
<category>Rain</category>
<tags>
<tag>Measurement</tag>
<tag>Rain</tag>
</tags>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
<channel-type id="rain-yearly" advanced="true">
<item-type>Number:Length</item-type>
<label>Yearly Rain</label>
<description>Rain since the start of this year.</description>
<category>Rain</category>
<tags>
<tag>Measurement</tag>
<tag>Rain</tag>
</tags>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
<channel-type id="metar" advanced="true">
<item-type>String</item-type>
<label>METAR Weather Report</label>
<description>METAR formatted weather report</description>
<category>Sun_Clouds</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="clouds" advanced="true">
<item-type>String</item-type>
<label>Cloud Cover</label>
<description>METAR style cloud cover.</description>
<category>Sun_Clouds</category>
<state readOnly="true"/>
</channel-type>
<!-- for sensors 2,3,4 use soilmoisture2, soilmoisture3, and soilmoisture4 -->
<channel-type id="soil-moisture" advanced="true">
<item-type>Number:Dimensionless</item-type>
<label>Soil Moisture</label>
<description>Soil moisture in %.</description>
<category>Moisture</category>
<tags>
<tag>Measurement</tag>
<tag>Water</tag>
</tags>
<state readOnly="true" pattern="%.0f %%"/>
</channel-type>
<!-- for sensor 2 use leafwetness2 -->
<channel-type id="leafwetness" advanced="true">
<item-type>Number:Dimensionless</item-type>
<label>Leaf Wetness</label>
<description>Leaf wetness in %.</description>
<category>Moisture</category>
<tags>
<tag>Measurement</tag>
<tag>Water</tag>
</tags>
<state readOnly="true" pattern="%.0f %%"/>
</channel-type>
<channel-type id="solarradiation">
<item-type>Number:Intensity</item-type>
<label>Solar Radiation</label>
<description>Solar radiation</description>
<category>Sun</category>
<tags>
<tag>Measurement</tag>
<tag>Light</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="uv">
<item-type>Number:Dimensionless</item-type>
<label>UV Index</label>
<description>UV index.</description>
<category>Sun</category>
<tags>
<tag>Measurement</tag>
<tag>Ultraviolet</tag>
</tags>
<state readOnly="true" pattern="%.0f"/>
</channel-type>
<channel-type id="visibility" advanced="true">
<item-type>Number:Length</item-type>
<label>Visibility</label>
<description>Visibility.</description>
<category>Sun_Clouds</category>
<tags>
<tag>Measurement</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<!-- Pollution Fields: -->
<channel-type id="nitric-oxide" advanced="true">
<item-type>Number:Dimensionless</item-type>
<label>Nitric Oxide</label>
<description>Nitric Oxide ppm.</description>
<category>Pollution</category>
<tags>
<tag>Measurement</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="nitrogen-dioxide-measured" advanced="true">
<item-type>Number:Dimensionless</item-type>
<label>Nitrogen Dioxide</label>
<description>Nitrogen Dioxide, true measure ppb.</description>
<category>Pollution</category>
<tags>
<tag>Measurement</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="nitrogen-dioxide-nox-no" advanced="true">
<item-type>Number:Dimensionless</item-type>
<label>NO2 X computed</label>
<description>NO2 computed, NOx-NO ppb.</description>
<category>Pollution</category>
<tags>
<tag>Measurement</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="nitrogen-dioxide-noy-no" advanced="true">
<item-type>Number:Dimensionless</item-type>
<label>NO2 Y computed, NOy-NO ppb</label>
<description>NO2 computed, NOy-NO ppb.</description>
<category>Pollution</category>
<tags>
<tag>Measurement</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="nitrogen-oxides" advanced="true">
<item-type>Number:Dimensionless</item-type>
<label>Nitrogen Oxides</label>
<description>Nitrogen Oxides ppb.</description>
<category>Pollution</category>
<tags>
<tag>Measurement</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="total-reactive-nitrogen" advanced="true">
<item-type>Number:Dimensionless</item-type>
<label>Total Reactive Nitrogen</label>
<description>Total reactive nitrogen.</description>
<category>Pollution</category>
<tags>
<tag>Measurement</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="no3-ion" advanced="true">
<item-type>Number:Density</item-type>
<label>NO3 ion</label>
<description>NO3 ion (nitrate, not adjusted for ammonium ion) µG/m3.</description>
<category>Pollution</category>
<tags>
<tag>Measurement</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="so4-ion" advanced="true">
<item-type>Number:Density</item-type>
<label>SO4 ion</label>
<description>SO4 ion (sulfate, not adjusted for ammonium ion) µG/m3.</description>
<category>Pollution</category>
<tags>
<tag>Measurement</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="sulfur-dioxide" advanced="true">
<item-type>Number:Dimensionless</item-type>
<label>Sulfur Dioxide</label>
<description>Sulfur Dioxide, conventional ppb.</description>
<category>Pollution</category>
<tags>
<tag>Measurement</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="sulfur-dioxide-trace-levels" advanced="true">
<item-type>Number:Dimensionless</item-type>
<label>Sulfur Dioxide Trace Levels</label>
<description>Sulfur Dioxide, trace levels ppb.</description>
<category>Pollution</category>
<tags>
<tag>Measurement</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="carbon-monoxide" advanced="true">
<item-type>Number:Dimensionless</item-type>
<label>Carbon Monoxide</label>
<description>Carbon Monoxide, conventional ppm.</description>
<category>Pollution</category>
<tags>
<tag>Measurement</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="carbon-monoxide-trace-levels" advanced="true">
<item-type>Number:Dimensionless</item-type>
<label>Carbon Monoxide Trace Levels</label>
<description>Carbon Monoxide, trace levels ppb.</description>
<category>Pollution</category>
<tags>
<tag>Measurement</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="elemental-carbon" advanced="true">
<item-type>Number:Density</item-type>
<label>Elemental Carbon</label>
<description>Elemental Carbon, PM2.5 µG/m3.</description>
<category>Pollution</category>
<tags>
<tag>Measurement</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="organic-carbon" advanced="true">
<item-type>Number:Density</item-type>
<label>Organic Carbon</label>
<description>Organic Carbon, not adjusted for oxygen and hydrogen, PM2.5 µG/m3.</description>
<category>Pollution</category>
<tags>
<tag>Measurement</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="black-carbon" advanced="true">
<item-type>Number:Density</item-type>
<label>Black Carbon</label>
<description>Black Carbon at 880 nm, µG/m3.</description>
<category>Pollution</category>
<tags>
<tag>Measurement</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="aethalometer" advanced="true">
<item-type>Number:Density</item-type>
<label>Second Channel of Aethalometer</label>
<description>second channel of Aethalometer at 370 nm, µG/m3.</description>
<category>Pollution</category>
<tags>
<tag>Measurement</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="pm2_5-mass" advanced="true">
<item-type>Number:Density</item-type>
<label>PM2.5 Mass</label>
<description>PM2.5 mass, µG/m3.</description>
<category>Pollution</category>
<tags>
<tag>Measurement</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="pm10-mass" advanced="true">
<item-type>Number:Density</item-type>
<label>PM10 Mass</label>
<description>PM10 mass, µG/m3.</description>
<category>Pollution</category>
<tags>
<tag>Measurement</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="ozone" advanced="true">
<item-type>Number:Dimensionless</item-type>
<label>Ozone</label>
<description>Ozone, ppb.</description>
<category>Pollution</category>
<tags>
<tag>Measurement</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="softwaretype" advanced="true">
<item-type>String</item-type>
<label>Software Type</label>
<description>A software type string from the weather station</description>
<category>Text</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="realtime-frequency" advanced="true">
<item-type>Number</item-type>
<label>Realtime Frequency</label>
<description>How often does the weather station submit measurements</description>
<category>Number</category>
<state readOnly="true" pattern="%.0f"/>
</channel-type>
<channel-type id="last-query-state" advanced="true">
<item-type>String</item-type>
<kind>state</kind>
<label>The last query</label>
<description>The part of the last query after the first unurlencoded '?'</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="last-query-trigger">
<item-type>String</item-type>
<kind>trigger</kind>
<label>The last query</label>
<description>The part of the last query after the first unurlencoded '?'</description>
<event/>
</channel-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="wundergroundupdatereceiver"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="wundergroundUpdateReceiver">
<label>Update Receiver</label>
<description>An endpoint thing that can receive and propagate HTTP GET updates meant for a particular station id at
wunderground.com</description>
<channel-groups>
<channel-group id="metadata" typeId="metadata-group"/>
<channel-group id="wind" typeId="wind-group"/>
<channel-group id="temperature" typeId="temperature-group"/>
<channel-group id="humidity" typeId="humidity-group"/>
<channel-group id="rain" typeId="rain-group"/>
<channel-group id="sunlight" typeId="sunlight-group"/>
<channel-group id="pressure" typeId="pressure-group"/>
<channel-group id="pollution" typeId="pollution-group"/>
</channel-groups>
<representation-property>stationId</representation-property>
<config-description>
<parameter name="stationId" type="text" required="true" pattern="\w+">
<label>Station ID</label>
<description><![CDATA[<br />
The wunderground.com update api requires a station id, that is defined for the WeatherUnderground
account measurements are to be submitted to.<br />
<br />
In this binding it is used to identify a unique thing, so each weather-station or
other apparatus submitting measurements can have a separate id, but if you don't intend to forward
the observations to wunderground.com, this value can be any non-blank string.
]]>
</description>
</parameter>
</config-description>
</thing-type>
</thing:thing-descriptions>