From: Jacob Laursen Date: Fri, 28 Jan 2022 07:23:24 +0000 (+0100) Subject: Create concrete handlers for Insight, Motion and Socket/Light Switch. (#12120) X-Git-Url: https://git.basschouten.com/?a=commitdiff_plain;h=7b3a1c9fc29141e27f62f7b53b0f0cf364fe1a7b;p=openhab-addons.git Create concrete handlers for Insight, Motion and Socket/Light Switch. (#12120) No logic/code changes yet, just extraction/separation. Fixes #12105 Signed-off-by: Jacob Laursen --- diff --git a/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/WemoBindingConstants.java b/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/WemoBindingConstants.java index 5f36ad5707..48f3000680 100644 --- a/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/WemoBindingConstants.java +++ b/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/WemoBindingConstants.java @@ -129,10 +129,6 @@ public class WemoBindingConstants { public static final Set SUPPORTED_LIGHT_THING_TYPES = Collections.singleton(THING_TYPE_MZ100); - public static final Set 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 SUPPORTED_THING_TYPES = Collections .unmodifiableSet(Stream .of(THING_TYPE_SOCKET, THING_TYPE_INSIGHT, THING_TYPE_LIGHTSWITCH, THING_TYPE_MOTION, diff --git a/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/WemoHandlerFactory.java b/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/WemoHandlerFactory.java index 56299260c5..75e5dd7f40 100644 --- a/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/WemoHandlerFactory.java +++ b/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/WemoHandlerFactory.java @@ -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)); diff --git a/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/handler/WemoBaseThingHandler.java b/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/handler/WemoBaseThingHandler.java index 2be37f227f..81b734a8f4 100644 --- a/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/handler/WemoBaseThingHandler.java +++ b/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/handler/WemoBaseThingHandler.java @@ -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; diff --git a/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/handler/WemoHandler.java b/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/handler/WemoHandler.java index fb31f7539f..4b16e08a74 100644 --- a/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/handler/WemoHandler.java +++ b/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/handler/WemoHandler.java @@ -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 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 stateMap = Collections.synchronizedMap(new HashMap<>()); - private Map 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 index 0000000000..4383233f09 --- /dev/null +++ b/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/handler/WemoInsightHandler.java @@ -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 stateMap = new ConcurrentHashMap(); + + 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 index 0000000000..032bab278c --- /dev/null +++ b/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/handler/WemoMotionHandler.java @@ -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 stateMap = new ConcurrentHashMap(); + + 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 index 0000000000..79df99db7e --- /dev/null +++ b/bundles/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/handler/WemoSwitchHandler.java @@ -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 stateMap = new ConcurrentHashMap(); + + 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 index bd09fdb8b6..0000000000 --- a/itests/org.openhab.binding.wemo.tests/src/main/java/org/openhab/binding/wemo/internal/handler/test/WemoHandlerTest.java +++ /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 index 0000000000..cf04469b96 --- /dev/null +++ b/itests/org.openhab.binding.wemo.tests/src/main/java/org/openhab/binding/wemo/internal/handler/test/WemoInsightHandlerTest.java @@ -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; + } + } +}