[wundergroundupdatereceiver] Bugfixes: Regenerate trigger channel with proper type and more metadata. Normalize channel names. It might be easiest to delete and allow recreation of channels. (#13327)
* [wundergroundupdatereceiver] LAST_QUERY parameter should not be mapped automatically * [wundergroundupdatereceiver] All channeltype props need to be applied Especially the channel kind * [wundergroundupdatereceiver] Remove illegal characters from channel name Additionally expand the channel naming test to assert the generated channelUID and test that _ in names isn't inadvertently replaced * [wundergroundupdatereceiver] Don't default AutoUpdatePolicy on creation * [wundergroundupdatereceiver] Migrate changed channel to trigger type Signed-off-by: Daniel Demus <daniel-github@demus.dk>
This commit is contained in:
@@ -49,6 +49,8 @@ public class WundergroundUpdateReceiverBindingConstants {
|
||||
|
||||
public static final String NOW = "now";
|
||||
|
||||
public static final String UNCATEGORIZED = "Uncategorized";
|
||||
|
||||
// Excluded technical paramter names
|
||||
public static final String REALTIME_MARKER = "realtime";
|
||||
public static final String PASSWORD = "PASSWORD";
|
||||
@@ -56,8 +58,8 @@ public class WundergroundUpdateReceiverBindingConstants {
|
||||
|
||||
// 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_RECEIVED_DATETIME = LAST_RECEIVED + "-datetime";
|
||||
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";
|
||||
@@ -72,7 +74,7 @@ public class WundergroundUpdateReceiverBindingConstants {
|
||||
public static final String PRESSURE_GROUP = "pressure";
|
||||
public static final String POLLUTION_GROUP = "pollution";
|
||||
|
||||
// Known or observed request paramters received from devices submitting to wunderground.com
|
||||
// Known or observed request parameters received from devices submitting to wunderground.com
|
||||
public static final String DATEUTC = "dateutc";
|
||||
public static final String SOFTWARE_TYPE = "softwaretype";
|
||||
public static final String LOW_BATTERY = "lowbatt";
|
||||
@@ -125,7 +127,7 @@ public class WundergroundUpdateReceiverBindingConstants {
|
||||
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_PM2_5 = "AqPM2-5";
|
||||
public static final String AQ_PM10 = "AqPM10";
|
||||
public static final String AQ_OZONE = "AqOZONE";
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ public class WundergroundUpdateReceiverDiscoveryService extends AbstractDiscover
|
||||
WundergroundUpdateReceiverServletControls servletControls;
|
||||
|
||||
private static final int TIMEOUT_SEC = 1;
|
||||
private final HashMap<String, Map<String, String[]>> thinglessStationIds = new HashMap<>();
|
||||
private final HashMap<String, Map<String, String>> thinglessStationIds = new HashMap<>();
|
||||
private boolean servletWasInactive = false;
|
||||
|
||||
private boolean scanning = false;
|
||||
@@ -57,7 +57,7 @@ public class WundergroundUpdateReceiverDiscoveryService extends AbstractDiscover
|
||||
thinglessStationIds.remove(stationId);
|
||||
}
|
||||
|
||||
public void addUnhandledStationId(@Nullable String stationId, Map<String, String[]> request) {
|
||||
public void addUnhandledStationId(@Nullable String stationId, Map<String, String> request) {
|
||||
if (stationId == null || stationId.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -73,7 +73,7 @@ public class WundergroundUpdateReceiverDiscoveryService extends AbstractDiscover
|
||||
return isBackgroundDiscoveryEnabled() || isScanning();
|
||||
}
|
||||
|
||||
public @Nullable Map<String, String[]> getUnhandledStationRequest(@Nullable String stationId) {
|
||||
public @Nullable Map<String, String> getUnhandledStationRequest(@Nullable String stationId) {
|
||||
return this.thinglessStationIds.get(stationId);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ 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.ChannelKind;
|
||||
import org.openhab.core.thing.type.ChannelType;
|
||||
import org.openhab.core.thing.type.ChannelTypeRegistry;
|
||||
import org.openhab.core.types.Command;
|
||||
@@ -110,16 +111,15 @@ public class WundergroundUpdateReceiverHandler extends BaseThingHandler {
|
||||
this.config = getConfigAs(WundergroundUpdateReceiverConfiguration.class);
|
||||
wundergroundUpdateReceiverServlet.addHandler(this);
|
||||
@Nullable
|
||||
Map<String, String[]> requestParameters = discoveryService.getUnhandledStationRequest(config.stationId);
|
||||
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));
|
||||
.forEach((String channelId) -> buildChannel(thingBuilder, channelId, ""));
|
||||
requestParameters.forEach((String parameter, String query) -> buildChannel(thingBuilder, parameter, query));
|
||||
updateThing(thingBuilder.build());
|
||||
}
|
||||
migrateChannels();
|
||||
discoveryService.removeUnhandledStationId(config.stationId);
|
||||
if (wundergroundUpdateReceiverServlet.isActive()) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
@@ -130,6 +130,17 @@ public class WundergroundUpdateReceiverHandler extends BaseThingHandler {
|
||||
wundergroundUpdateReceiverServlet.getErrorDetail());
|
||||
}
|
||||
|
||||
private void migrateChannels() {
|
||||
Optional.ofNullable(getThing().getChannel(queryTriggerChannel)).ifPresent(c -> {
|
||||
if (c.getKind() != ChannelKind.TRIGGER) {
|
||||
ThingBuilder builder = editThing();
|
||||
builder.withoutChannel(c.getUID());
|
||||
buildChannel(builder, LAST_QUERY_TRIGGER, "");
|
||||
updateThing(builder.build());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
wundergroundUpdateReceiverServlet.removeHandler(this.getStationId());
|
||||
@@ -149,10 +160,10 @@ public class WundergroundUpdateReceiverHandler extends BaseThingHandler {
|
||||
triggerChannel(queryTriggerChannel, lastQuery);
|
||||
}
|
||||
|
||||
private void buildChannel(ThingBuilder thingBuilder, String parameter, String... query) {
|
||||
private void buildChannel(ThingBuilder thingBuilder, String parameter, String value) {
|
||||
@Nullable
|
||||
WundergroundUpdateReceiverParameterMapping channelTypeMapping = WundergroundUpdateReceiverParameterMapping
|
||||
.getOrCreateMapping(parameter, String.join("", query), channelTypeProvider);
|
||||
.getOrCreateMapping(parameter, value, channelTypeProvider);
|
||||
if (channelTypeMapping == null) {
|
||||
return;
|
||||
}
|
||||
@@ -162,7 +173,10 @@ public class WundergroundUpdateReceiverHandler extends BaseThingHandler {
|
||||
}
|
||||
ChannelBuilder channelBuilder = ChannelBuilder
|
||||
.create(new ChannelUID(thing.getUID(), channelTypeMapping.channelGroup, parameter))
|
||||
.withType(channelTypeMapping.channelTypeId).withAcceptedItemType(channelType.getItemType());
|
||||
.withKind(channelType.getKind()).withAutoUpdatePolicy(channelType.getAutoUpdatePolicy())
|
||||
.withDefaultTags(channelType.getTags()).withType(channelTypeMapping.channelTypeId)
|
||||
.withAcceptedItemType(channelType.getItemType()).withLabel(channelType.getLabel());
|
||||
Optional.ofNullable(channelType.getDescription()).ifPresent(channelBuilder::withDescription);
|
||||
thingBuilder.withChannel(channelBuilder.build());
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ public class WundergroundUpdateReceiverParameterMapping {
|
||||
}
|
||||
|
||||
private static final List<String> UNMAPPED_PARAMETERS = List.of(STATION_ID_PARAMETER, PASSWORD, ACTION,
|
||||
REALTIME_MARKER);
|
||||
REALTIME_MARKER, LAST_QUERY);
|
||||
|
||||
private static final WundergroundUpdateReceiverParameterMapping[] KNOWN_MAPPINGS = {
|
||||
new WundergroundUpdateReceiverParameterMapping(LAST_RECEIVED, LAST_RECEIVED_DATETIME_CHANNELTYPEUID,
|
||||
@@ -173,7 +173,7 @@ public class WundergroundUpdateReceiverParameterMapping {
|
||||
}
|
||||
Optional<WundergroundUpdateReceiverParameterMapping> knownMapping = lookupMapping(parameterName);
|
||||
return knownMapping.orElseGet(() -> new WundergroundUpdateReceiverParameterMapping(parameterName,
|
||||
channelTypeProvider.getOrCreateChannelType(parameterName, value).getUID(), "Uncategorized", null, false,
|
||||
channelTypeProvider.getOrCreateChannelType(parameterName, value).getUID(), UNCATEGORIZED, null, false,
|
||||
null));
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -53,6 +54,7 @@ public class WundergroundUpdateReceiverServlet extends BaseOpenHABServlet
|
||||
|
||||
public static final String SERVLET_URL = "/weatherstation/updateweatherstation.php";
|
||||
private static final long serialVersionUID = -5296703727081438023L;
|
||||
private static final Pattern CLEANER = Pattern.compile("[^\\w-]");
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(WundergroundUpdateReceiverServlet.class);
|
||||
private final Map<String, WundergroundUpdateReceiverHandler> handlers = new HashMap<>();
|
||||
@@ -173,6 +175,11 @@ public class WundergroundUpdateReceiverServlet extends BaseOpenHABServlet
|
||||
}
|
||||
}
|
||||
|
||||
protected Map<String, String> normalizeParameterMap(Map<String, String[]> parameterMap) {
|
||||
return parameterMap.entrySet().stream()
|
||||
.collect(toMap(e -> makeUidSafeString(e.getKey()), e -> String.join("", e.getValue())));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(@Nullable HttpServletRequest req, @Nullable HttpServletResponse resp) throws IOException {
|
||||
if (!active) {
|
||||
@@ -190,16 +197,15 @@ public class WundergroundUpdateReceiverServlet extends BaseOpenHABServlet
|
||||
logger.trace("doGet {}", req.getQueryString());
|
||||
|
||||
String stationId = req.getParameter(STATION_ID_PARAMETER);
|
||||
Map<String, String> states = normalizeParameterMap(req.getParameterMap());
|
||||
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());
|
||||
this.discoveryService.addUnhandledStationId(stationId, states);
|
||||
});
|
||||
|
||||
resp.setStatus(HttpServletResponse.SC_OK);
|
||||
@@ -216,4 +222,8 @@ public class WundergroundUpdateReceiverServlet extends BaseOpenHABServlet
|
||||
protected Map<String, WundergroundUpdateReceiverHandler> getHandlers() {
|
||||
return Collections.unmodifiableMap(this.handlers);
|
||||
}
|
||||
|
||||
private String makeUidSafeString(String key) {
|
||||
return CLEANER.matcher(key).replaceAll("-");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
package org.openhab.binding.wundergroundupdatereceiver.internal;
|
||||
|
||||
import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.THING_TYPE_UPDATE_RECEIVER;
|
||||
import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.UNCATEGORIZED;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -22,6 +23,8 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.library.CoreItemFactory;
|
||||
import org.openhab.core.thing.type.AutoUpdatePolicy;
|
||||
import org.openhab.core.thing.type.ChannelType;
|
||||
import org.openhab.core.thing.type.ChannelTypeBuilder;
|
||||
import org.openhab.core.thing.type.ChannelTypeProvider;
|
||||
@@ -37,7 +40,7 @@ import org.slf4j.LoggerFactory;
|
||||
@NonNullByDefault
|
||||
public class WundergroundUpdateReceiverUnknownChannelTypeProvider implements ChannelTypeProvider {
|
||||
|
||||
private static final List<String> BOOLEAN_STRINGS = List.of("1", "0", "true", "false");
|
||||
private static final List<String> BOOLEAN_STRINGS = List.of("1", "0", "true", "false", "yes", "no", "on", "off");
|
||||
private final Map<ChannelTypeUID, ChannelType> channelTypes = new ConcurrentHashMap<>();
|
||||
private final Logger logger = LoggerFactory.getLogger(WundergroundUpdateReceiverUnknownChannelTypeProvider.class);
|
||||
|
||||
@@ -57,7 +60,8 @@ public class WundergroundUpdateReceiverUnknownChannelTypeProvider implements Cha
|
||||
ChannelType type = getChannelType(typeUid, null);
|
||||
if (type == null) {
|
||||
String itemType = guessItemType(value);
|
||||
type = ChannelTypeBuilder.state(typeUid, parameterName + " channel type", itemType).build();
|
||||
type = ChannelTypeBuilder.state(typeUid, parameterName + " channel type", itemType).isAdvanced(true)
|
||||
.withCategory(UNCATEGORIZED).withAutoUpdatePolicy(AutoUpdatePolicy.DEFAULT).build();
|
||||
return addChannelType(typeUid, type);
|
||||
}
|
||||
return type;
|
||||
@@ -65,18 +69,18 @@ public class WundergroundUpdateReceiverUnknownChannelTypeProvider implements Cha
|
||||
|
||||
private static String guessItemType(String value) {
|
||||
if (BOOLEAN_STRINGS.contains(value.toLowerCase())) {
|
||||
return "Switch";
|
||||
return CoreItemFactory.SWITCH;
|
||||
}
|
||||
try {
|
||||
Float.valueOf(value);
|
||||
return "Number";
|
||||
return CoreItemFactory.NUMBER;
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
return "String";
|
||||
return CoreItemFactory.STRING;
|
||||
}
|
||||
|
||||
private ChannelType addChannelType(ChannelTypeUID channelTypeUID, ChannelType channelType) {
|
||||
logger.warn("Adding channelType {} for unknown parameter", channelTypeUID.getAsString());
|
||||
logger.warn("Adding new synthetic channelType {} for unrecognised parameter", channelTypeUID.getAsString());
|
||||
this.channelTypes.put(channelTypeUID, channelType);
|
||||
return channelType;
|
||||
}
|
||||
|
||||
@@ -59,9 +59,9 @@ 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.label = Last query
|
||||
channel-type.wundergroundupdatereceiver.last-query-state.description = The query part of the last request from the device
|
||||
channel-type.wundergroundupdatereceiver.last-query-trigger.label = The last query
|
||||
channel-type.wundergroundupdatereceiver.last-query-trigger.label = Last query
|
||||
channel-type.wundergroundupdatereceiver.last-query-trigger.description = The query part of the last request from the device
|
||||
channel-type.wundergroundupdatereceiver.last-received-datetime.label = Last Received
|
||||
channel-type.wundergroundupdatereceiver.last-received-datetime.description = The date and time of the last update.
|
||||
|
||||
Reference in New Issue
Block a user