[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.
|
||||
|
||||
@@ -35,11 +35,13 @@ import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.server.HttpChannel;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Answers;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.DefaultSystemChannelTypeProvider;
|
||||
import org.openhab.core.thing.ManagedThingProvider;
|
||||
import org.openhab.core.thing.Thing;
|
||||
@@ -48,6 +50,7 @@ import org.openhab.core.thing.binding.ThingHandlerCallback;
|
||||
import org.openhab.core.thing.binding.builder.ThingBuilder;
|
||||
import org.openhab.core.thing.internal.type.StateChannelTypeBuilderImpl;
|
||||
import org.openhab.core.thing.internal.type.TriggerChannelTypeBuilderImpl;
|
||||
import org.openhab.core.thing.type.ChannelKind;
|
||||
import org.openhab.core.thing.type.ChannelTypeProvider;
|
||||
import org.openhab.core.thing.type.ChannelTypeRegistry;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
@@ -69,6 +72,60 @@ class WundergroundUpdateReceiverDiscoveryServiceTest {
|
||||
openMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
void programmaticChannelsAreAddedCorrectlyOnce() {
|
||||
// Given
|
||||
final String queryString = "ID=dfggger&" + "PASSWORD=XXXXXX&" + "humidity=74&" + "AqPM2.5=30&"
|
||||
+ "windspdmph_avg2m=10&" + "dateutc=2021-02-07%2014:04:03&" + "softwaretype=WH2600%20V2.2.8&"
|
||||
+ "action=updateraw&" + "realtime=1&" + "rtfreq=5";
|
||||
MetaData.Request request = new MetaData.Request("GET",
|
||||
new HttpURI("http://localhost" + WundergroundUpdateReceiverServlet.SERVLET_URL + "?" + queryString),
|
||||
HttpVersion.HTTP_1_1, new HttpFields());
|
||||
HttpChannel httpChannel = mock(HttpChannel.class);
|
||||
Request req = new Request(httpChannel, null);
|
||||
req.setMetaData(request);
|
||||
|
||||
TestChannelTypeRegistry channelTypeRegistry = new TestChannelTypeRegistry();
|
||||
WundergroundUpdateReceiverDiscoveryService discoveryService = new WundergroundUpdateReceiverDiscoveryService(
|
||||
true);
|
||||
HttpService httpService = mock(HttpService.class);
|
||||
WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
|
||||
discoveryService.addUnhandledStationId(REQ_STATION_ID, sut.normalizeParameterMap(req.getParameterMap()));
|
||||
Thing thing = ThingBuilder.create(SUPPORTED_THING_TYPES_UIDS.stream().findFirst().get(), TEST_THING_UID)
|
||||
.withConfiguration(new Configuration(Map.of(REPRESENTATION_PROPERTY, REQ_STATION_ID)))
|
||||
.withLabel("test thing").withLocation("location").build();
|
||||
ManagedThingProvider managedThingProvider = mock(ManagedThingProvider.class);
|
||||
when(managedThingProvider.get(any())).thenReturn(thing);
|
||||
WundergroundUpdateReceiverHandler handler = new WundergroundUpdateReceiverHandler(thing, sut, discoveryService,
|
||||
new WundergroundUpdateReceiverUnknownChannelTypeProvider(), channelTypeRegistry, managedThingProvider);
|
||||
handler.setCallback(mock(ThingHandlerCallback.class));
|
||||
|
||||
// When
|
||||
handler.initialize();
|
||||
var actual = handler.getThing().getChannels();
|
||||
|
||||
// Then
|
||||
assertThat(actual.size(), is(9));
|
||||
|
||||
assertChannel(actual.get(0), METADATA_GROUP, LAST_RECEIVED, LAST_RECEIVED_DATETIME_CHANNELTYPEUID,
|
||||
ChannelKind.STATE, is("DateTime"));
|
||||
assertChannel(actual.get(1), METADATA_GROUP, LAST_QUERY_TRIGGER, LAST_QUERY_TRIGGER_CHANNELTYPEUID,
|
||||
ChannelKind.TRIGGER, nullValue());
|
||||
assertChannel(actual.get(2), METADATA_GROUP, LAST_QUERY_STATE, LAST_QUERY_STATE_CHANNELTYPEUID,
|
||||
ChannelKind.STATE, is("String"));
|
||||
assertChannel(actual.get(3), METADATA_GROUP, DATEUTC, DATEUTC_CHANNELTYPEUID, ChannelKind.STATE, is("String"));
|
||||
assertChannel(actual.get(4), METADATA_GROUP, REALTIME_FREQUENCY, REALTIME_FREQUENCY_CHANNELTYPEUID,
|
||||
ChannelKind.STATE, is("Number"));
|
||||
assertChannel(actual.get(5), METADATA_GROUP, SOFTWARE_TYPE, SOFTWARETYPE_CHANNELTYPEUID, ChannelKind.STATE,
|
||||
is("String"));
|
||||
assertChannel(actual.get(6), HUMIDITY_GROUP, HUMIDITY, HUMIDITY_CHANNELTYPEUID, ChannelKind.STATE,
|
||||
is("Number:Dimensionless"));
|
||||
assertChannel(actual.get(7), WIND_GROUP, WIND_SPEED_AVG_2MIN, WIND_SPEED_AVG_2MIN_CHANNELTYPEUID,
|
||||
ChannelKind.STATE, is("Number:Speed"));
|
||||
assertChannel(actual.get(8), POLLUTION_GROUP, AQ_PM2_5, PM2_5_MASS_CHANNELTYPEUID, ChannelKind.STATE,
|
||||
is("Number:Density"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void aRequestWithAnUnregisteredStationidIsAddedToTheQueueOnce()
|
||||
throws ServletException, NamespaceException, IOException {
|
||||
@@ -124,9 +181,9 @@ class WundergroundUpdateReceiverDiscoveryServiceTest {
|
||||
TestChannelTypeRegistry channelTypeRegistry = new TestChannelTypeRegistry();
|
||||
WundergroundUpdateReceiverDiscoveryService discoveryService = new WundergroundUpdateReceiverDiscoveryService(
|
||||
false);
|
||||
discoveryService.addUnhandledStationId(REQ_STATION_ID, req.getParameterMap());
|
||||
HttpService httpService = mock(HttpService.class);
|
||||
WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
|
||||
discoveryService.addUnhandledStationId(REQ_STATION_ID, sut.normalizeParameterMap(req.getParameterMap()));
|
||||
Thing thing = ThingBuilder.create(SUPPORTED_THING_TYPES_UIDS.stream().findFirst().get(), TEST_THING_UID)
|
||||
.withConfiguration(new Configuration(Map.of(REPRESENTATION_PROPERTY, REQ_STATION_ID)))
|
||||
.withLabel("test thing").withLocation("location").build();
|
||||
@@ -170,9 +227,9 @@ class WundergroundUpdateReceiverDiscoveryServiceTest {
|
||||
TestChannelTypeRegistry channelTypeRegistry = new TestChannelTypeRegistry();
|
||||
WundergroundUpdateReceiverDiscoveryService discoveryService = new WundergroundUpdateReceiverDiscoveryService(
|
||||
true);
|
||||
discoveryService.addUnhandledStationId(REQ_STATION_ID, req1.getParameterMap());
|
||||
HttpService httpService = mock(HttpService.class);
|
||||
WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
|
||||
discoveryService.addUnhandledStationId(REQ_STATION_ID, sut.normalizeParameterMap(req1.getParameterMap()));
|
||||
Thing thing = ThingBuilder.create(SUPPORTED_THING_TYPES_UIDS.stream().findFirst().get(), TEST_THING_UID)
|
||||
.withConfiguration(new Configuration(Map.of(REPRESENTATION_PROPERTY, REQ_STATION_ID)))
|
||||
.withLabel("test thing").withLocation("location").build();
|
||||
@@ -237,9 +294,9 @@ class WundergroundUpdateReceiverDiscoveryServiceTest {
|
||||
TestChannelTypeRegistry channelTypeRegistry = new TestChannelTypeRegistry();
|
||||
WundergroundUpdateReceiverDiscoveryService discoveryService = new WundergroundUpdateReceiverDiscoveryService(
|
||||
true);
|
||||
discoveryService.addUnhandledStationId(REQ_STATION_ID, req1.getParameterMap());
|
||||
HttpService httpService = mock(HttpService.class);
|
||||
WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
|
||||
discoveryService.addUnhandledStationId(REQ_STATION_ID, sut.normalizeParameterMap(req1.getParameterMap()));
|
||||
Thing thing = ThingBuilder.create(SUPPORTED_THING_TYPES_UIDS.stream().findFirst().get(), TEST_THING_UID)
|
||||
.withConfiguration(new Configuration(Map.of(REPRESENTATION_PROPERTY, REQ_STATION_ID)))
|
||||
.withLabel("test thing").withLocation("location").build();
|
||||
@@ -285,11 +342,99 @@ class WundergroundUpdateReceiverDiscoveryServiceTest {
|
||||
assertThat(actual, equalTo(before));
|
||||
}
|
||||
|
||||
class TestChannelTypeRegistry extends ChannelTypeRegistry {
|
||||
@Test
|
||||
void lastQueryTriggerIsMigratedSuccessfully() throws IOException {
|
||||
// Given
|
||||
final String firstDeviceQueryString = "ID=dfggger&" + "PASSWORD=XXXXXX&" + "tempf=26.1&" + "humidity=74&"
|
||||
+ "dateutc=2021-02-07%2014:04:03&" + "softwaretype=WH2600%20V2.2.8&" + "action=updateraw&"
|
||||
+ "realtime=1&" + "rtfreq=5";
|
||||
MetaData.Request request1 = new MetaData.Request("GET", new HttpURI(
|
||||
"http://localhost" + WundergroundUpdateReceiverServlet.SERVLET_URL + "?" + firstDeviceQueryString),
|
||||
HttpVersion.HTTP_1_1, new HttpFields());
|
||||
HttpChannel httpChannel = mock(HttpChannel.class);
|
||||
Request req1 = new Request(httpChannel, null);
|
||||
req1.setMetaData(request1);
|
||||
|
||||
TestChannelTypeRegistry() {
|
||||
UpdatingChannelTypeRegistry channelTypeRegistry = new UpdatingChannelTypeRegistry();
|
||||
WundergroundUpdateReceiverDiscoveryService discoveryService = new WundergroundUpdateReceiverDiscoveryService(
|
||||
true);
|
||||
HttpService httpService = mock(HttpService.class);
|
||||
WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
|
||||
discoveryService.addUnhandledStationId(REQ_STATION_ID, sut.normalizeParameterMap(req1.getParameterMap()));
|
||||
Thing thing = ThingBuilder.create(SUPPORTED_THING_TYPES_UIDS.stream().findFirst().get(), TEST_THING_UID)
|
||||
.withConfiguration(new Configuration(Map.of(REPRESENTATION_PROPERTY, REQ_STATION_ID)))
|
||||
.withLabel("test thing").withLocation("location").build();
|
||||
ManagedThingProvider managedThingProvider = mock(ManagedThingProvider.class);
|
||||
when(managedThingProvider.get(any())).thenReturn(null);
|
||||
WundergroundUpdateReceiverHandler handler = new WundergroundUpdateReceiverHandler(thing, sut, discoveryService,
|
||||
new WundergroundUpdateReceiverUnknownChannelTypeProvider(), channelTypeRegistry, managedThingProvider);
|
||||
handler.setCallback(mock(ThingHandlerCallback.class));
|
||||
|
||||
// When
|
||||
handler.initialize();
|
||||
sut.addHandler(handler);
|
||||
|
||||
// Then
|
||||
ChannelTypeUID[] expectedBefore = new ChannelTypeUID[] { TEMPERATURE_CHANNELTYPEUID, HUMIDITY_CHANNELTYPEUID,
|
||||
DATEUTC_CHANNELTYPEUID, SOFTWARETYPE_CHANNELTYPEUID, REALTIME_FREQUENCY_CHANNELTYPEUID,
|
||||
LAST_QUERY_STATE_CHANNELTYPEUID, LAST_RECEIVED_DATETIME_CHANNELTYPEUID,
|
||||
LAST_QUERY_TRIGGER_CHANNELTYPEUID };
|
||||
List<ChannelTypeUID> before = handler.getThing().getChannels().stream().map(Channel::getChannelTypeUID)
|
||||
.collect(Collectors.toList());
|
||||
assertThat(before, hasItems(expectedBefore));
|
||||
|
||||
// When
|
||||
var actual = handler.getThing().getChannels();
|
||||
|
||||
// Then
|
||||
assertThat(actual.size(), is(8));
|
||||
assertChannel(actual.get(7), METADATA_GROUP, LAST_QUERY_TRIGGER, LAST_QUERY_TRIGGER_CHANNELTYPEUID,
|
||||
ChannelKind.STATE, is("DateTime"));
|
||||
|
||||
// When
|
||||
handler.dispose();
|
||||
handler.initialize();
|
||||
|
||||
final String secondDeviceQueryString = "ID=dfggger&" + "PASSWORD=XXXXXX&" + "lowbatt=1&" + "soilmoisture1=78&"
|
||||
+ "soilmoisture2=73&" + "solarradiation=42.24&" + "dateutc=2021-02-07%2014:04:03&"
|
||||
+ "softwaretype=WH2600%20V2.2.8&" + "action=updateraw&" + "realtime=1&" + "rtfreq=5";
|
||||
MetaData.Request request = new MetaData.Request("GET", new HttpURI(
|
||||
"http://localhost" + WundergroundUpdateReceiverServlet.SERVLET_URL + "?" + secondDeviceQueryString),
|
||||
HttpVersion.HTTP_1_1, new HttpFields());
|
||||
Request req2 = new Request(httpChannel, null);
|
||||
req2.setMetaData(request);
|
||||
sut.activate();
|
||||
|
||||
// Then
|
||||
assertThat(sut.isActive(), is(true));
|
||||
|
||||
// When
|
||||
sut.doGet(req2, mock(HttpServletResponse.class, Answers.RETURNS_MOCKS));
|
||||
actual = handler.getThing().getChannels();
|
||||
|
||||
// Then
|
||||
assertThat(actual.size(), is(8));
|
||||
assertChannel(actual.get(7), METADATA_GROUP, LAST_QUERY_TRIGGER, LAST_QUERY_TRIGGER_CHANNELTYPEUID,
|
||||
ChannelKind.TRIGGER, nullValue());
|
||||
}
|
||||
|
||||
private void assertChannel(Channel actual, String expectedGroup, String expectedName, ChannelTypeUID expectedUid,
|
||||
ChannelKind expectedKind, Matcher<Object> expectedItemType) {
|
||||
assertThat(actual, is(notNullValue()));
|
||||
assertThat(actual.getLabel() + " UID", actual.getUID(),
|
||||
is(new ChannelUID(TEST_THING_UID, expectedGroup, expectedName)));
|
||||
assertThat(actual.getLabel() + " ChannelTypeUID", actual.getChannelTypeUID(), is(expectedUid));
|
||||
assertThat(actual.getLabel() + " Kind", actual.getKind(), is(expectedKind));
|
||||
assertThat(actual.getLabel() + " AcceptedItemType", actual.getAcceptedItemType(), expectedItemType);
|
||||
}
|
||||
|
||||
abstract class AbstractTestChannelTypeRegistry extends ChannelTypeRegistry {
|
||||
|
||||
protected final ChannelTypeProvider provider;
|
||||
|
||||
AbstractTestChannelTypeRegistry(ChannelTypeProvider mock) {
|
||||
super();
|
||||
ChannelTypeProvider provider = mock(ChannelTypeProvider.class);
|
||||
this.provider = mock;
|
||||
when(provider.getChannelType(eq(SOFTWARETYPE_CHANNELTYPEUID), any())).thenReturn(
|
||||
new StateChannelTypeBuilderImpl(SOFTWARETYPE_CHANNELTYPEUID, "Software type", "String").build());
|
||||
when(provider.getChannelType(eq(TEMPERATURE_CHANNELTYPEUID), any()))
|
||||
@@ -303,6 +448,11 @@ class WundergroundUpdateReceiverDiscoveryServiceTest {
|
||||
when(provider.getChannelType(eq(HUMIDITY_CHANNELTYPEUID), any())).thenReturn(
|
||||
new StateChannelTypeBuilderImpl(HUMIDITY_CHANNELTYPEUID, "Humidity", "Number:Dimensionless")
|
||||
.build());
|
||||
when(provider.getChannelType(eq(WIND_SPEED_AVG_2MIN_CHANNELTYPEUID), any()))
|
||||
.thenReturn(new StateChannelTypeBuilderImpl(WIND_SPEED_AVG_2MIN_CHANNELTYPEUID,
|
||||
"Wind Speed 2min Average", "Number:Speed").build());
|
||||
when(provider.getChannelType(eq(PM2_5_MASS_CHANNELTYPEUID), any())).thenReturn(
|
||||
new StateChannelTypeBuilderImpl(PM2_5_MASS_CHANNELTYPEUID, "PM2.5 Mass", "Number:Density").build());
|
||||
when(provider.getChannelType(eq(DATEUTC_CHANNELTYPEUID), any())).thenReturn(
|
||||
new StateChannelTypeBuilderImpl(DATEUTC_CHANNELTYPEUID, "Last Updated", "String").build());
|
||||
when(provider.getChannelType(eq(LOW_BATTERY_CHANNELTYPEUID), any()))
|
||||
@@ -316,10 +466,31 @@ class WundergroundUpdateReceiverDiscoveryServiceTest {
|
||||
when(provider.getChannelType(eq(LAST_RECEIVED_DATETIME_CHANNELTYPEUID), any())).thenReturn(
|
||||
new StateChannelTypeBuilderImpl(LAST_RECEIVED_DATETIME_CHANNELTYPEUID, "Last Received", "DateTime")
|
||||
.build());
|
||||
when(provider.getChannelType(eq(LAST_QUERY_TRIGGER_CHANNELTYPEUID), any())).thenReturn(
|
||||
new TriggerChannelTypeBuilderImpl(LAST_QUERY_TRIGGER_CHANNELTYPEUID, "The last query").build());
|
||||
this.addChannelTypeProvider(provider);
|
||||
this.addChannelTypeProvider(new WundergroundUpdateReceiverUnknownChannelTypeProvider());
|
||||
}
|
||||
}
|
||||
|
||||
class TestChannelTypeRegistry extends AbstractTestChannelTypeRegistry {
|
||||
|
||||
TestChannelTypeRegistry() {
|
||||
super(mock(ChannelTypeProvider.class));
|
||||
when(provider.getChannelType(eq(LAST_QUERY_TRIGGER_CHANNELTYPEUID), any())).thenReturn(
|
||||
new TriggerChannelTypeBuilderImpl(LAST_QUERY_TRIGGER_CHANNELTYPEUID, "The last query").build());
|
||||
}
|
||||
}
|
||||
|
||||
class UpdatingChannelTypeRegistry extends AbstractTestChannelTypeRegistry {
|
||||
|
||||
UpdatingChannelTypeRegistry() {
|
||||
super(mock(ChannelTypeProvider.class));
|
||||
when(provider.getChannelType(eq(LAST_QUERY_TRIGGER_CHANNELTYPEUID), any()))
|
||||
.thenReturn(new StateChannelTypeBuilderImpl(LAST_QUERY_TRIGGER_CHANNELTYPEUID, "The last query",
|
||||
"DateTime").build())
|
||||
.thenReturn(new StateChannelTypeBuilderImpl(LAST_QUERY_TRIGGER_CHANNELTYPEUID, "The last query",
|
||||
"DateTime").build())
|
||||
.thenReturn(new TriggerChannelTypeBuilderImpl(LAST_QUERY_TRIGGER_CHANNELTYPEUID, "The last query")
|
||||
.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user