]> git.basschouten.com Git - openhab-addons.git/commitdiff
Create concrete handlers for Insight, Motion and Socket/Light Switch. (#12120)
authorJacob Laursen <jacob-github@vindvejr.dk>
Fri, 28 Jan 2022 07:23:24 +0000 (08:23 +0100)
committerGitHub <noreply@github.com>
Fri, 28 Jan 2022 07:23:24 +0000 (08:23 +0100)
No logic/code changes yet, just extraction/separation.

Fixes #12105

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/WemoBindingConstants.java
bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/WemoHandlerFactory.java
bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/handler/WemoBaseThingHandler.java
bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/handler/WemoHandler.java
bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/handler/WemoInsightHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/handler/WemoMotionHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/handler/WemoSwitchHandler.java [new file with mode: 0644]
itests/org.openhab.binding.wemo.tests/src/main/java/org/openhab/binding/wemo/internal/handler/test/WemoHandlerTest.java [deleted file]
itests/org.openhab.binding.wemo.tests/src/main/java/org/openhab/binding/wemo/internal/handler/test/WemoInsightHandlerTest.java [new file with mode: 0644]

index 5f36ad5707abfaf3fc04437cb211f57045dee64c..48f3000680a52404ec76585cc40e6341345b9c9a 100644 (file)
@@ -129,10 +129,6 @@ public class WemoBindingConstants {
 
     public static final Set<ThingTypeUID> SUPPORTED_LIGHT_THING_TYPES = Collections.singleton(THING_TYPE_MZ100);
 
-    public static final Set<ThingTypeUID> SUPPORTED_DEVICE_THING_TYPES = Collections
-            .unmodifiableSet(Stream.of(THING_TYPE_SOCKET, THING_TYPE_INSIGHT, THING_TYPE_LIGHTSWITCH, THING_TYPE_MOTION)
-                    .collect(Collectors.toSet()));
-
     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections
             .unmodifiableSet(Stream
                     .of(THING_TYPE_SOCKET, THING_TYPE_INSIGHT, THING_TYPE_LIGHTSWITCH, THING_TYPE_MOTION,
index 56299260c52aab6d0d03f409a83abe80ad6ec1c1..75e5dd7f40e59de664f9d70c1041f04f042db500 100644 (file)
@@ -26,10 +26,12 @@ import org.openhab.binding.wemo.internal.handler.WemoBridgeHandler;
 import org.openhab.binding.wemo.internal.handler.WemoCoffeeHandler;
 import org.openhab.binding.wemo.internal.handler.WemoCrockpotHandler;
 import org.openhab.binding.wemo.internal.handler.WemoDimmerHandler;
-import org.openhab.binding.wemo.internal.handler.WemoHandler;
 import org.openhab.binding.wemo.internal.handler.WemoHolmesHandler;
+import org.openhab.binding.wemo.internal.handler.WemoInsightHandler;
 import org.openhab.binding.wemo.internal.handler.WemoLightHandler;
 import org.openhab.binding.wemo.internal.handler.WemoMakerHandler;
+import org.openhab.binding.wemo.internal.handler.WemoMotionHandler;
+import org.openhab.binding.wemo.internal.handler.WemoSwitchHandler;
 import org.openhab.binding.wemo.internal.http.WemoHttpCall;
 import org.openhab.core.config.discovery.DiscoveryService;
 import org.openhab.core.io.transport.upnp.UpnpIOService;
@@ -103,10 +105,19 @@ public class WemoHandlerFactory extends BaseThingHandlerFactory {
             WemoBridgeHandler handler = new WemoBridgeHandler((Bridge) thing);
             registerDeviceDiscoveryService(handler, wemoHttpcaller);
             return handler;
-        } else if (WemoBindingConstants.SUPPORTED_DEVICE_THING_TYPES.contains(thing.getThingTypeUID())) {
-            logger.debug("Creating a WemoHandler for thing '{}' with UDN '{}'", thing.getUID(),
+        } else if (WemoBindingConstants.THING_TYPE_INSIGHT.equals(thing.getThingTypeUID())) {
+            logger.debug("Creating a WemoInsightHandler for thing '{}' with UDN '{}'", thing.getUID(),
                     thing.getConfiguration().get(UDN));
-            return new WemoHandler(thing, upnpIOService, wemoHttpcaller);
+            return new WemoInsightHandler(thing, upnpIOService, wemoHttpcaller);
+        } else if (WemoBindingConstants.THING_TYPE_SOCKET.equals(thing.getThingTypeUID())
+                || WemoBindingConstants.THING_TYPE_LIGHTSWITCH.equals(thing.getThingTypeUID())) {
+            logger.debug("Creating a WemoSwitchHandler for thing '{}' with UDN '{}'", thing.getUID(),
+                    thing.getConfiguration().get(UDN));
+            return new WemoSwitchHandler(thing, upnpIOService, wemoHttpcaller);
+        } else if (WemoBindingConstants.THING_TYPE_MOTION.equals(thing.getThingTypeUID())) {
+            logger.debug("Creating a WemoMotionHandler for thing '{}' with UDN '{}'", thing.getUID(),
+                    thing.getConfiguration().get(UDN));
+            return new WemoMotionHandler(thing, upnpIOService, wemoHttpcaller);
         } else if (thingTypeUID.equals(WemoBindingConstants.THING_TYPE_MAKER)) {
             logger.debug("Creating a WemoMakerHandler for thing '{}' with UDN '{}'", thing.getUID(),
                     thing.getConfiguration().get(UDN));
index 2be37f227f599a62f5f8401acce450d3075914f5..81b734a8f4d616bd6463e8c392342648f9a76fc5 100644 (file)
@@ -32,7 +32,7 @@ import org.openhab.core.types.Command;
  * @author Jacob Laursen - Initial contribution
  */
 @NonNullByDefault
-public class WemoBaseThingHandler extends BaseThingHandler implements UpnpIOParticipant {
+public abstract class WemoBaseThingHandler extends BaseThingHandler implements UpnpIOParticipant {
 
     protected @Nullable UpnpIOService service;
     protected WemoHttpCall wemoHttpCaller;
index fb31f7539fad6827f23ad5e3847efac1dfc5a55f..4b16e08a744330600886ba63c9b7c6bcb648decf 100644 (file)
@@ -15,30 +15,17 @@ package org.openhab.binding.wemo.internal.handler;
 import static org.openhab.binding.wemo.internal.WemoBindingConstants.*;
 import static org.openhab.binding.wemo.internal.WemoUtil.*;
 
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.time.Instant;
-import java.time.ZonedDateTime;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Set;
-import java.util.TimeZone;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.wemo.internal.http.WemoHttpCall;
 import org.openhab.core.config.core.Configuration;
 import org.openhab.core.io.transport.upnp.UpnpIOService;
-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.unit.Units;
 import org.openhab.core.thing.ChannelUID;
 import org.openhab.core.thing.Thing;
 import org.openhab.core.thing.ThingStatus;
@@ -46,7 +33,6 @@ import org.openhab.core.thing.ThingStatusDetail;
 import org.openhab.core.thing.ThingTypeUID;
 import org.openhab.core.types.Command;
 import org.openhab.core.types.RefreshType;
-import org.openhab.core.types.State;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -61,19 +47,13 @@ import org.slf4j.LoggerFactory;
  * @author Mihir Patil - Added standby switch
  */
 @NonNullByDefault
-public class WemoHandler extends WemoBaseThingHandler {
+public abstract class WemoHandler extends WemoBaseThingHandler {
 
     private final Logger logger = LoggerFactory.getLogger(WemoHandler.class);
 
-    public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Stream
-            .of(THING_TYPE_SOCKET, THING_TYPE_INSIGHT, THING_TYPE_LIGHTSWITCH, THING_TYPE_MOTION)
-            .collect(Collectors.toSet());
-
     private final Object upnpLock = new Object();
     private final Object jobLock = new Object();
 
-    private final Map<String, String> stateMap = Collections.synchronizedMap(new HashMap<>());
-
     private Map<String, Boolean> subscriptionState = new HashMap<>();
 
     private @Nullable ScheduledFuture<?> pollingJob;
@@ -202,137 +182,6 @@ public class WemoHandler extends WemoBaseThingHandler {
         }
     }
 
-    @Override
-    public void onValueReceived(@Nullable String variable, @Nullable String value, @Nullable String service) {
-        logger.debug("Received pair '{}':'{}' (service '{}') for thing '{}'",
-                new Object[] { variable, value, service, this.getThing().getUID() });
-
-        updateStatus(ThingStatus.ONLINE);
-
-        if (!"BinaryState".equals(variable) && !"InsightParams".equals(variable)) {
-            return;
-        }
-
-        String oldValue = this.stateMap.get(variable);
-        if (variable != null && value != null) {
-            this.stateMap.put(variable, value);
-        }
-
-        if (value != null && value.length() > 1) {
-            String insightParams = stateMap.get(variable);
-
-            if (insightParams != null) {
-                String[] splitInsightParams = insightParams.split("\\|");
-
-                if (splitInsightParams[0] != null) {
-                    OnOffType binaryState = "0".equals(splitInsightParams[0]) ? OnOffType.OFF : OnOffType.ON;
-                    logger.trace("New InsightParam binaryState '{}' for device '{}' received", binaryState,
-                            getThing().getUID());
-                    updateState(CHANNEL_STATE, binaryState);
-                }
-
-                long lastChangedAt = 0;
-                try {
-                    lastChangedAt = Long.parseLong(splitInsightParams[1]) * 1000; // convert s to ms
-                } catch (NumberFormatException e) {
-                    logger.error("Unable to parse lastChangedAt value '{}' for device '{}'; expected long",
-                            splitInsightParams[1], getThing().getUID());
-                }
-                ZonedDateTime zoned = ZonedDateTime.ofInstant(Instant.ofEpochMilli(lastChangedAt),
-                        TimeZone.getDefault().toZoneId());
-
-                State lastChangedAtState = new DateTimeType(zoned);
-                if (lastChangedAt != 0) {
-                    logger.trace("New InsightParam lastChangedAt '{}' for device '{}' received", lastChangedAtState,
-                            getThing().getUID());
-                    updateState(CHANNEL_LASTCHANGEDAT, lastChangedAtState);
-                }
-
-                State lastOnFor = DecimalType.valueOf(splitInsightParams[2]);
-                logger.trace("New InsightParam lastOnFor '{}' for device '{}' received", lastOnFor,
-                        getThing().getUID());
-                updateState(CHANNEL_LASTONFOR, lastOnFor);
-
-                State onToday = DecimalType.valueOf(splitInsightParams[3]);
-                logger.trace("New InsightParam onToday '{}' for device '{}' received", onToday, getThing().getUID());
-                updateState(CHANNEL_ONTODAY, onToday);
-
-                State onTotal = DecimalType.valueOf(splitInsightParams[4]);
-                logger.trace("New InsightParam onTotal '{}' for device '{}' received", onTotal, getThing().getUID());
-                updateState(CHANNEL_ONTOTAL, onTotal);
-
-                State timespan = DecimalType.valueOf(splitInsightParams[5]);
-                logger.trace("New InsightParam timespan '{}' for device '{}' received", timespan, getThing().getUID());
-                updateState(CHANNEL_TIMESPAN, timespan);
-
-                State averagePower = new QuantityType<>(DecimalType.valueOf(splitInsightParams[6]), Units.WATT); // natively
-                                                                                                                 // given
-                                                                                                                 // in W
-                logger.trace("New InsightParam averagePower '{}' for device '{}' received", averagePower,
-                        getThing().getUID());
-                updateState(CHANNEL_AVERAGEPOWER, averagePower);
-
-                BigDecimal currentMW = new BigDecimal(splitInsightParams[7]);
-                State currentPower = new QuantityType<>(currentMW.divide(new BigDecimal(1000), 0, RoundingMode.HALF_UP),
-                        Units.WATT); // recalculate
-                // mW to W
-                logger.trace("New InsightParam currentPower '{}' for device '{}' received", currentPower,
-                        getThing().getUID());
-                updateState(CHANNEL_CURRENTPOWER, currentPower);
-
-                BigDecimal energyTodayMWMin = new BigDecimal(splitInsightParams[8]);
-                // recalculate mW-mins to Wh
-                State energyToday = new QuantityType<>(
-                        energyTodayMWMin.divide(new BigDecimal(60000), 0, RoundingMode.HALF_UP), Units.WATT_HOUR);
-                logger.trace("New InsightParam energyToday '{}' for device '{}' received", energyToday,
-                        getThing().getUID());
-                updateState(CHANNEL_ENERGYTODAY, energyToday);
-
-                BigDecimal energyTotalMWMin = new BigDecimal(splitInsightParams[9]);
-                // recalculate mW-mins to Wh
-                State energyTotal = new QuantityType<>(
-                        energyTotalMWMin.divide(new BigDecimal(60000), 0, RoundingMode.HALF_UP), Units.WATT_HOUR);
-                logger.trace("New InsightParam energyTotal '{}' for device '{}' received", energyTotal,
-                        getThing().getUID());
-                updateState(CHANNEL_ENERGYTOTAL, energyTotal);
-
-                if (splitInsightParams.length > 10 && splitInsightParams[10] != null) {
-                    BigDecimal standByLimitMW = new BigDecimal(splitInsightParams[10]);
-                    State standByLimit = new QuantityType<>(
-                            standByLimitMW.divide(new BigDecimal(1000), 0, RoundingMode.HALF_UP), Units.WATT); // recalculate
-                    // mW to W
-                    logger.trace("New InsightParam standByLimit '{}' for device '{}' received", standByLimit,
-                            getThing().getUID());
-                    updateState(CHANNEL_STANDBYLIMIT, standByLimit);
-
-                    if (currentMW.divide(new BigDecimal(1000), 0, RoundingMode.HALF_UP).intValue() > standByLimitMW
-                            .divide(new BigDecimal(1000), 0, RoundingMode.HALF_UP).intValue()) {
-                        updateState(CHANNEL_ONSTANDBY, OnOffType.OFF);
-                    } else {
-                        updateState(CHANNEL_ONSTANDBY, OnOffType.ON);
-                    }
-                }
-            }
-        } else if (value != null && value.length() == 1) {
-            String binaryState = stateMap.get("BinaryState");
-            if (binaryState != null) {
-                if (oldValue == null || !oldValue.equals(binaryState)) {
-                    State state = "0".equals(binaryState) ? OnOffType.OFF : OnOffType.ON;
-                    logger.debug("State '{}' for device '{}' received", state, getThing().getUID());
-                    if ("motion".equals(getThing().getThingTypeUID().getId())) {
-                        updateState(CHANNEL_MOTIONDETECTION, state);
-                        if (OnOffType.ON.equals(state)) {
-                            State lastMotionDetected = new DateTimeType();
-                            updateState(CHANNEL_LASTMOTIONDETECTED, lastMotionDetected);
-                        }
-                    } else {
-                        updateState(CHANNEL_STATE, state);
-                    }
-                }
-            }
-        }
-    }
-
     private synchronized void addSubscription() {
         synchronized (upnpLock) {
             UpnpIOService localService = service;
diff --git a/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/handler/WemoInsightHandler.java b/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/handler/WemoInsightHandler.java
new file mode 100644 (file)
index 0000000..4383233
--- /dev/null
@@ -0,0 +1,167 @@
+/**
+ * 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.wemo.internal.handler;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.Instant;
+import java.time.ZonedDateTime;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.wemo.internal.WemoBindingConstants;
+import org.openhab.binding.wemo.internal.http.WemoHttpCall;
+import org.openhab.core.io.transport.upnp.UpnpIOService;
+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.unit.Units;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.types.State;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link WemoInsightHandler} is responsible for handling commands for
+ * a WeMo Insight Switch.
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class WemoInsightHandler extends WemoHandler {
+
+    private final Logger logger = LoggerFactory.getLogger(WemoInsightHandler.class);
+    private final Map<String, String> stateMap = new ConcurrentHashMap<String, String>();
+
+    public WemoInsightHandler(Thing thing, UpnpIOService upnpIOService, WemoHttpCall wemoHttpCaller) {
+        super(thing, upnpIOService, wemoHttpCaller);
+    }
+
+    @Override
+    public void onValueReceived(@Nullable String variable, @Nullable String value, @Nullable String service) {
+        logger.debug("Received pair '{}':'{}' (service '{}') for thing '{}'",
+                new Object[] { variable, value, service, this.getThing().getUID() });
+
+        updateStatus(ThingStatus.ONLINE);
+
+        if (!"BinaryState".equals(variable) && !"InsightParams".equals(variable)) {
+            return;
+        }
+
+        if (variable != null && value != null) {
+            this.stateMap.put(variable, value);
+        }
+
+        if (value != null && value.length() > 1) {
+            String insightParams = stateMap.get(variable);
+
+            if (insightParams != null) {
+                String[] splitInsightParams = insightParams.split("\\|");
+
+                if (splitInsightParams[0] != null) {
+                    OnOffType binaryState = "0".equals(splitInsightParams[0]) ? OnOffType.OFF : OnOffType.ON;
+                    logger.trace("New InsightParam binaryState '{}' for device '{}' received", binaryState,
+                            getThing().getUID());
+                    updateState(WemoBindingConstants.CHANNEL_STATE, binaryState);
+                }
+
+                long lastChangedAt = 0;
+                try {
+                    lastChangedAt = Long.parseLong(splitInsightParams[1]) * 1000; // convert s to ms
+                } catch (NumberFormatException e) {
+                    logger.error("Unable to parse lastChangedAt value '{}' for device '{}'; expected long",
+                            splitInsightParams[1], getThing().getUID());
+                }
+                ZonedDateTime zoned = ZonedDateTime.ofInstant(Instant.ofEpochMilli(lastChangedAt),
+                        TimeZone.getDefault().toZoneId());
+
+                State lastChangedAtState = new DateTimeType(zoned);
+                if (lastChangedAt != 0) {
+                    logger.trace("New InsightParam lastChangedAt '{}' for device '{}' received", lastChangedAtState,
+                            getThing().getUID());
+                    updateState(WemoBindingConstants.CHANNEL_LASTCHANGEDAT, lastChangedAtState);
+                }
+
+                State lastOnFor = DecimalType.valueOf(splitInsightParams[2]);
+                logger.trace("New InsightParam lastOnFor '{}' for device '{}' received", lastOnFor,
+                        getThing().getUID());
+                updateState(WemoBindingConstants.CHANNEL_LASTONFOR, lastOnFor);
+
+                State onToday = DecimalType.valueOf(splitInsightParams[3]);
+                logger.trace("New InsightParam onToday '{}' for device '{}' received", onToday, getThing().getUID());
+                updateState(WemoBindingConstants.CHANNEL_ONTODAY, onToday);
+
+                State onTotal = DecimalType.valueOf(splitInsightParams[4]);
+                logger.trace("New InsightParam onTotal '{}' for device '{}' received", onTotal, getThing().getUID());
+                updateState(WemoBindingConstants.CHANNEL_ONTOTAL, onTotal);
+
+                State timespan = DecimalType.valueOf(splitInsightParams[5]);
+                logger.trace("New InsightParam timespan '{}' for device '{}' received", timespan, getThing().getUID());
+                updateState(WemoBindingConstants.CHANNEL_TIMESPAN, timespan);
+
+                State averagePower = new QuantityType<>(DecimalType.valueOf(splitInsightParams[6]), Units.WATT); // natively
+                                                                                                                 // given
+                                                                                                                 // in W
+                logger.trace("New InsightParam averagePower '{}' for device '{}' received", averagePower,
+                        getThing().getUID());
+                updateState(WemoBindingConstants.CHANNEL_AVERAGEPOWER, averagePower);
+
+                BigDecimal currentMW = new BigDecimal(splitInsightParams[7]);
+                State currentPower = new QuantityType<>(currentMW.divide(new BigDecimal(1000), 0, RoundingMode.HALF_UP),
+                        Units.WATT); // recalculate
+                // mW to W
+                logger.trace("New InsightParam currentPower '{}' for device '{}' received", currentPower,
+                        getThing().getUID());
+                updateState(WemoBindingConstants.CHANNEL_CURRENTPOWER, currentPower);
+
+                BigDecimal energyTodayMWMin = new BigDecimal(splitInsightParams[8]);
+                // recalculate mW-mins to Wh
+                State energyToday = new QuantityType<>(
+                        energyTodayMWMin.divide(new BigDecimal(60000), 0, RoundingMode.HALF_UP), Units.WATT_HOUR);
+                logger.trace("New InsightParam energyToday '{}' for device '{}' received", energyToday,
+                        getThing().getUID());
+                updateState(WemoBindingConstants.CHANNEL_ENERGYTODAY, energyToday);
+
+                BigDecimal energyTotalMWMin = new BigDecimal(splitInsightParams[9]);
+                // recalculate mW-mins to Wh
+                State energyTotal = new QuantityType<>(
+                        energyTotalMWMin.divide(new BigDecimal(60000), 0, RoundingMode.HALF_UP), Units.WATT_HOUR);
+                logger.trace("New InsightParam energyTotal '{}' for device '{}' received", energyTotal,
+                        getThing().getUID());
+                updateState(WemoBindingConstants.CHANNEL_ENERGYTOTAL, energyTotal);
+
+                if (splitInsightParams.length > 10 && splitInsightParams[10] != null) {
+                    BigDecimal standByLimitMW = new BigDecimal(splitInsightParams[10]);
+                    State standByLimit = new QuantityType<>(
+                            standByLimitMW.divide(new BigDecimal(1000), 0, RoundingMode.HALF_UP), Units.WATT); // recalculate
+                    // mW to W
+                    logger.trace("New InsightParam standByLimit '{}' for device '{}' received", standByLimit,
+                            getThing().getUID());
+                    updateState(WemoBindingConstants.CHANNEL_STANDBYLIMIT, standByLimit);
+
+                    if (currentMW.divide(new BigDecimal(1000), 0, RoundingMode.HALF_UP).intValue() > standByLimitMW
+                            .divide(new BigDecimal(1000), 0, RoundingMode.HALF_UP).intValue()) {
+                        updateState(WemoBindingConstants.CHANNEL_ONSTANDBY, OnOffType.OFF);
+                    } else {
+                        updateState(WemoBindingConstants.CHANNEL_ONSTANDBY, OnOffType.ON);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/handler/WemoMotionHandler.java b/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/handler/WemoMotionHandler.java
new file mode 100644 (file)
index 0000000..032bab2
--- /dev/null
@@ -0,0 +1,78 @@
+/**
+ * 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.wemo.internal.handler;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.wemo.internal.WemoBindingConstants;
+import org.openhab.binding.wemo.internal.http.WemoHttpCall;
+import org.openhab.core.io.transport.upnp.UpnpIOService;
+import org.openhab.core.library.types.DateTimeType;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.types.State;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link WemoMotionHandler} is responsible for handling commands for
+ * a WeMo Motion.
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class WemoMotionHandler extends WemoHandler {
+
+    private final Logger logger = LoggerFactory.getLogger(WemoMotionHandler.class);
+    private final Map<String, String> stateMap = new ConcurrentHashMap<String, String>();
+
+    public WemoMotionHandler(Thing thing, UpnpIOService upnpIOService, WemoHttpCall wemoHttpCaller) {
+        super(thing, upnpIOService, wemoHttpCaller);
+    }
+
+    @Override
+    public void onValueReceived(@Nullable String variable, @Nullable String value, @Nullable String service) {
+        logger.debug("Received pair '{}':'{}' (service '{}') for thing '{}'",
+                new Object[] { variable, value, service, this.getThing().getUID() });
+
+        updateStatus(ThingStatus.ONLINE);
+
+        if (!"BinaryState".equals(variable)) {
+            return;
+        }
+
+        String oldValue = this.stateMap.get(variable);
+        if (variable != null && value != null) {
+            this.stateMap.put(variable, value);
+        }
+
+        if (value != null && value.length() == 1) {
+            String binaryState = stateMap.get("BinaryState");
+            if (binaryState != null) {
+                if (oldValue == null || !oldValue.equals(binaryState)) {
+                    State state = "0".equals(binaryState) ? OnOffType.OFF : OnOffType.ON;
+                    logger.debug("State '{}' for device '{}' received", state, getThing().getUID());
+                    updateState(WemoBindingConstants.CHANNEL_MOTIONDETECTION, state);
+                    if (OnOffType.ON.equals(state)) {
+                        State lastMotionDetected = new DateTimeType();
+                        updateState(WemoBindingConstants.CHANNEL_LASTMOTIONDETECTED, lastMotionDetected);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/handler/WemoSwitchHandler.java b/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/handler/WemoSwitchHandler.java
new file mode 100644 (file)
index 0000000..79df99d
--- /dev/null
@@ -0,0 +1,73 @@
+/**
+ * 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.wemo.internal.handler;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.wemo.internal.WemoBindingConstants;
+import org.openhab.binding.wemo.internal.http.WemoHttpCall;
+import org.openhab.core.io.transport.upnp.UpnpIOService;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.types.State;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link WemoSwitchHandler} is responsible for handling commands for
+ * a WeMo device supporting a binary switch: Socket or Light Switch.
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class WemoSwitchHandler extends WemoHandler {
+
+    private final Logger logger = LoggerFactory.getLogger(WemoSwitchHandler.class);
+    private final Map<String, String> stateMap = new ConcurrentHashMap<String, String>();
+
+    public WemoSwitchHandler(Thing thing, UpnpIOService upnpIOService, WemoHttpCall wemoHttpCaller) {
+        super(thing, upnpIOService, wemoHttpCaller);
+    }
+
+    @Override
+    public void onValueReceived(@Nullable String variable, @Nullable String value, @Nullable String service) {
+        logger.debug("Received pair '{}':'{}' (service '{}') for thing '{}'",
+                new Object[] { variable, value, service, this.getThing().getUID() });
+
+        updateStatus(ThingStatus.ONLINE);
+
+        if (!"BinaryState".equals(variable)) {
+            return;
+        }
+
+        String oldValue = this.stateMap.get(variable);
+        if (variable != null && value != null) {
+            this.stateMap.put(variable, value);
+        }
+
+        if (value != null && value.length() == 1) {
+            String binaryState = stateMap.get("BinaryState");
+            if (binaryState != null) {
+                if (oldValue == null || !oldValue.equals(binaryState)) {
+                    State state = "0".equals(binaryState) ? OnOffType.OFF : OnOffType.ON;
+                    logger.debug("State '{}' for device '{}' received", state, getThing().getUID());
+                    updateState(WemoBindingConstants.CHANNEL_STATE, state);
+                }
+            }
+        }
+    }
+}
diff --git a/itests/org.openhab.binding.wemo.tests/src/main/java/org/openhab/binding/wemo/internal/handler/test/WemoHandlerTest.java b/itests/org.openhab.binding.wemo.tests/src/main/java/org/openhab/binding/wemo/internal/handler/test/WemoHandlerTest.java
deleted file mode 100644 (file)
index bd09fdb..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-/**
- * 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.wemo.internal.handler.test;
-
-import static org.hamcrest.CoreMatchers.*;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.mockito.Mockito.*;
-import static org.openhab.binding.wemo.internal.WemoBindingConstants.*;
-
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.openhab.binding.wemo.internal.WemoBindingConstants;
-import org.openhab.binding.wemo.internal.handler.WemoHandler;
-import org.openhab.binding.wemo.internal.http.WemoHttpCall;
-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.unit.Units;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingStatus;
-import org.openhab.core.thing.ThingStatusDetail;
-import org.openhab.core.thing.ThingTypeUID;
-import org.openhab.core.thing.ThingUID;
-import org.openhab.core.types.State;
-
-/**
- * Tests for {@link WemoHandler}.
- *
- * @author Svilen Valkanov - Initial contribution
- * @author Stefan Triller - Ported Tests from Groovy to Java
- */
-public class WemoHandlerTest {
-
-    private static final ThingTypeUID THING_TYPE = WemoBindingConstants.THING_TYPE_INSIGHT;
-    private static final String THING_ID = "test";
-
-    private MockWemoHandler handler;
-
-    private static final String SERVICE_ID = "insight";
-    private static final String PARAMS_NAME = "InsightParams";
-    private WemoInsightParams insightParams;
-
-    /** Used for all tests, where expected value is time in seconds **/
-    private static final int TIME_PARAM = 4702;
-
-    /** Represents a state parameter, where 1 stays for ON and 0 stays for OFF **/
-    private static final int STATE_PARAM = 1;
-
-    /** Represents power in Wats **/
-    private static final int POWER_PARAM = 54;
-
-    private final Thing thing = mock(Thing.class);
-
-    @BeforeEach
-    public void setUp() {
-        insightParams = new WemoInsightParams();
-        when(thing.getUID()).thenReturn(new ThingUID(THING_TYPE, THING_ID));
-        when(thing.getThingTypeUID()).thenReturn(THING_TYPE);
-        when(thing.getStatus()).thenReturn(ThingStatus.ONLINE);
-    }
-
-    @AfterEach
-    public void clear() {
-        handler.channelState = null;
-        handler.channelToWatch = null;
-    }
-
-    @Test
-    public void assertThatChannelSTATEisUpdatedOnReceivedValue() {
-        insightParams.state = STATE_PARAM;
-        State expectedStateType = OnOffType.ON;
-        String expectedChannel = CHANNEL_STATE;
-
-        testOnValueReceived(expectedChannel, expectedStateType, insightParams.toString());
-    }
-
-    @Test
-    public void assertThatChannelLASTONFORIsUpdatedOnReceivedValue() {
-        insightParams.lastOnFor = TIME_PARAM;
-        State expectedStateType = new DecimalType(TIME_PARAM);
-        String expectedChannel = CHANNEL_LASTONFOR;
-
-        testOnValueReceived(expectedChannel, expectedStateType, insightParams.toString());
-    }
-
-    @Test
-    public void assertThatChannelONTODAYIsUpdatedOnReceivedValue() {
-        insightParams.onToday = TIME_PARAM;
-        State expectedStateType = new DecimalType(TIME_PARAM);
-        String expectedChannel = CHANNEL_ONTODAY;
-
-        testOnValueReceived(expectedChannel, expectedStateType, insightParams.toString());
-    }
-
-    @Test
-    public void assertThatChannelONTOTALIsUpdatedOnReceivedValue() {
-        insightParams.onTotal = TIME_PARAM;
-        State expectedStateType = new DecimalType(TIME_PARAM);
-        String expectedChannel = CHANNEL_ONTOTAL;
-
-        testOnValueReceived(expectedChannel, expectedStateType, insightParams.toString());
-    }
-
-    @Test
-    public void assertThatChannelTIMESPANIsUpdatedOnReceivedValue() {
-        insightParams.timespan = TIME_PARAM;
-        State expectedStateType = new DecimalType(TIME_PARAM);
-        String expectedChannel = CHANNEL_TIMESPAN;
-
-        testOnValueReceived(expectedChannel, expectedStateType, insightParams.toString());
-    }
-
-    @Test
-    public void assertThatChannelAVERAGEPOWERIsUpdatedOnReceivedValue() {
-        insightParams.avgPower = POWER_PARAM;
-        State expectedStateType = new QuantityType<>(POWER_PARAM, Units.WATT);
-        String expectedChannel = CHANNEL_AVERAGEPOWER;
-
-        testOnValueReceived(expectedChannel, expectedStateType, insightParams.toString());
-    }
-
-    private void testOnValueReceived(String expectedChannel, State expectedState, String insightParams) {
-        handler = new MockWemoHandler(thing, expectedChannel);
-
-        handler.onValueReceived(PARAMS_NAME, insightParams, SERVICE_ID);
-        assertThat(handler.channelState, is(notNullValue()));
-        assertThat(handler.channelState, is(expectedState));
-    }
-
-    class MockWemoHandler extends WemoHandler {
-        State channelState;
-        String channelToWatch;
-
-        public MockWemoHandler(Thing thing, String channelToWatch) {
-            super(thing, null, new WemoHttpCall());
-            this.channelToWatch = channelToWatch;
-        }
-
-        @Override
-        protected void updateState(String channelID, State channelState) {
-            if (channelID.equals(channelToWatch)) {
-                this.channelState = channelState;
-            }
-        }
-
-        @Override
-        protected void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, String description) {
-        }
-
-        @Override
-        protected void updateStatus(ThingStatus status, ThingStatusDetail statusDetail) {
-        }
-
-        @Override
-        protected void updateStatus(ThingStatus status) {
-        }
-    }
-
-    class WemoInsightParams {
-        int state, lastChangedAt, lastOnFor, onToday, onTotal, timespan, avgPower, currPower, todayEnergy, totalEnergy,
-                standbyLimit;
-
-        @Override
-        public String toString() {
-            // Example string looks like "1|1427230660|4702|25528|82406|1209600|39|40880|15620649|54450534.000000|8000"
-            return state + "|" + lastChangedAt + "|" + lastOnFor + "|" + onToday + "|" + onTotal + "|" + timespan + "|"
-                    + avgPower + "|" + currPower + "|" + todayEnergy + "|" + totalEnergy + "|" + standbyLimit;
-        }
-    }
-}
diff --git a/itests/org.openhab.binding.wemo.tests/src/main/java/org/openhab/binding/wemo/internal/handler/test/WemoInsightHandlerTest.java b/itests/org.openhab.binding.wemo.tests/src/main/java/org/openhab/binding/wemo/internal/handler/test/WemoInsightHandlerTest.java
new file mode 100644 (file)
index 0000000..cf04469
--- /dev/null
@@ -0,0 +1,181 @@
+/**
+ * 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.wemo.internal.handler.test;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.*;
+import static org.openhab.binding.wemo.internal.WemoBindingConstants.*;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.openhab.binding.wemo.internal.WemoBindingConstants;
+import org.openhab.binding.wemo.internal.handler.WemoInsightHandler;
+import org.openhab.binding.wemo.internal.http.WemoHttpCall;
+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.unit.Units;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.ThingUID;
+import org.openhab.core.types.State;
+
+/**
+ * Tests for {@link WemoInsightHandler}.
+ *
+ * @author Svilen Valkanov - Initial contribution
+ * @author Stefan Triller - Ported Tests from Groovy to Java
+ */
+public class WemoInsightHandlerTest {
+
+    private static final ThingTypeUID THING_TYPE = WemoBindingConstants.THING_TYPE_INSIGHT;
+    private static final String THING_ID = "test";
+
+    private MockWemoInsightHandler handler;
+
+    private static final String SERVICE_ID = "insight";
+    private static final String PARAMS_NAME = "InsightParams";
+    private WemoInsightParams insightParams;
+
+    /** Used for all tests, where expected value is time in seconds **/
+    private static final int TIME_PARAM = 4702;
+
+    /** Represents a state parameter, where 1 stays for ON and 0 stays for OFF **/
+    private static final int STATE_PARAM = 1;
+
+    /** Represents power in Wats **/
+    private static final int POWER_PARAM = 54;
+
+    private final Thing thing = mock(Thing.class);
+
+    @BeforeEach
+    public void setUp() {
+        insightParams = new WemoInsightParams();
+        when(thing.getUID()).thenReturn(new ThingUID(THING_TYPE, THING_ID));
+        when(thing.getThingTypeUID()).thenReturn(THING_TYPE);
+        when(thing.getStatus()).thenReturn(ThingStatus.ONLINE);
+    }
+
+    @AfterEach
+    public void clear() {
+        handler.channelState = null;
+        handler.channelToWatch = null;
+    }
+
+    @Test
+    public void assertThatChannelSTATEisUpdatedOnReceivedValue() {
+        insightParams.state = STATE_PARAM;
+        State expectedStateType = OnOffType.ON;
+        String expectedChannel = CHANNEL_STATE;
+
+        testOnValueReceived(expectedChannel, expectedStateType, insightParams.toString());
+    }
+
+    @Test
+    public void assertThatChannelLASTONFORIsUpdatedOnReceivedValue() {
+        insightParams.lastOnFor = TIME_PARAM;
+        State expectedStateType = new DecimalType(TIME_PARAM);
+        String expectedChannel = CHANNEL_LASTONFOR;
+
+        testOnValueReceived(expectedChannel, expectedStateType, insightParams.toString());
+    }
+
+    @Test
+    public void assertThatChannelONTODAYIsUpdatedOnReceivedValue() {
+        insightParams.onToday = TIME_PARAM;
+        State expectedStateType = new DecimalType(TIME_PARAM);
+        String expectedChannel = CHANNEL_ONTODAY;
+
+        testOnValueReceived(expectedChannel, expectedStateType, insightParams.toString());
+    }
+
+    @Test
+    public void assertThatChannelONTOTALIsUpdatedOnReceivedValue() {
+        insightParams.onTotal = TIME_PARAM;
+        State expectedStateType = new DecimalType(TIME_PARAM);
+        String expectedChannel = CHANNEL_ONTOTAL;
+
+        testOnValueReceived(expectedChannel, expectedStateType, insightParams.toString());
+    }
+
+    @Test
+    public void assertThatChannelTIMESPANIsUpdatedOnReceivedValue() {
+        insightParams.timespan = TIME_PARAM;
+        State expectedStateType = new DecimalType(TIME_PARAM);
+        String expectedChannel = CHANNEL_TIMESPAN;
+
+        testOnValueReceived(expectedChannel, expectedStateType, insightParams.toString());
+    }
+
+    @Test
+    public void assertThatChannelAVERAGEPOWERIsUpdatedOnReceivedValue() {
+        insightParams.avgPower = POWER_PARAM;
+        State expectedStateType = new QuantityType<>(POWER_PARAM, Units.WATT);
+        String expectedChannel = CHANNEL_AVERAGEPOWER;
+
+        testOnValueReceived(expectedChannel, expectedStateType, insightParams.toString());
+    }
+
+    private void testOnValueReceived(String expectedChannel, State expectedState, String insightParams) {
+        handler = new MockWemoInsightHandler(thing, expectedChannel);
+
+        handler.onValueReceived(PARAMS_NAME, insightParams, SERVICE_ID);
+        assertThat(handler.channelState, is(notNullValue()));
+        assertThat(handler.channelState, is(expectedState));
+    }
+
+    class MockWemoInsightHandler extends WemoInsightHandler {
+        State channelState;
+        String channelToWatch;
+
+        public MockWemoInsightHandler(Thing thing, String channelToWatch) {
+            super(thing, null, new WemoHttpCall());
+            this.channelToWatch = channelToWatch;
+        }
+
+        @Override
+        protected void updateState(String channelID, State channelState) {
+            if (channelID.equals(channelToWatch)) {
+                this.channelState = channelState;
+            }
+        }
+
+        @Override
+        protected void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, String description) {
+        }
+
+        @Override
+        protected void updateStatus(ThingStatus status, ThingStatusDetail statusDetail) {
+        }
+
+        @Override
+        protected void updateStatus(ThingStatus status) {
+        }
+    }
+
+    class WemoInsightParams {
+        int state, lastChangedAt, lastOnFor, onToday, onTotal, timespan, avgPower, currPower, todayEnergy, totalEnergy,
+                standbyLimit;
+
+        @Override
+        public String toString() {
+            // Example string looks like "1|1427230660|4702|25528|82406|1209600|39|40880|15620649|54450534.000000|8000"
+            return state + "|" + lastChangedAt + "|" + lastOnFor + "|" + onToday + "|" + onTotal + "|" + timespan + "|"
+                    + avgPower + "|" + currPower + "|" + todayEnergy + "|" + totalEnergy + "|" + standbyLimit;
+        }
+    }
+}