## Discovery
-The WeMo devices are discovered through UPnP discovery service in the network. Devices will show up in the inbox and can be easily added as Things.
+The WeMo devices are discovered through UPnP discovery service in the network.
+Devices will show up in the inbox and can be easily added as Things.
## Binding Configuration
## Thing Configuration
-For manual Thing configuration, one needs to know the UUID of a certain WeMo device.
-In the thing file, this looks e.g. like
+For manual Thing configuration, one needs to know the UDN of a certain WeMo device.
+It can most easily be obtained by performing an auto-discovery before configuring the thing manually.
-```
-wemo:socket:Switch1 [udn="Socket-1_0-221242K11xxxxx"]
-```
+Most devices share the `udn` configuration parameter:
-For a WeMo Link bridge and paired LED Lights, please use the following Thing definition
+| Configuration Parameter | Description |
+|-------------------------|------------------------------------|
+| udn | The UDN identifies the WeMo device |
-```
-Bridge wemo:bridge:Bridge-1_0-231445B01006A0 [udn="Bridge-1_0-231445B010xxxx"] {
-MZ100 94103EA2B278xxxx [ deviceID="94103EA2B278xxxx" ]
-MZ100 94103EA2B278xxxx [ deviceID="94103EA2B278xxxx" ]
-}
-```
+### WeMo LED Light
+
+For LED Lights paired to a WeMo Link bridge, please use the following configuration parameter:
+
+| Configuration Parameter | Description |
+|-------------------------|-------------------------------------------------|
+| deviceID | The device ID identifies one certain WeMo light |
+
+### WeMo Insight Switch
+The WeMo Insight Switch has some additional parameters for controlling the behavior for channel `currentPower`.
+This channel reports the current power consumption in Watt.
+The internal theoretical accuracy is 5 mW, i.e. three decimals.
+These raw values are reported with high frequency, often multiple updates can occur within a single second.
+For example, the sequence of 40.440 W, 40.500 W and 40.485 W would result in the channel being updated with values rounded to nearest integer, respectively 40 W, 41 W and 40 W.
+When persisting items linked to this channel, this can result in a significant amount of data being stored.
+To mitigate this issue, a sliding window with a moving average calculation has been introduced.
+This window is defined with a one minute default period.
+This is combined with a delta trigger value, which is defaulted to 1 W.
+This means that the channel is only updated when one of the following conditions are met:
+
+1. The rounded value received is equal to the rounded average for the past minute, i.e. this value has stabilized. This introduces a delay for very small changes in consumption, but on the other hand it prevents excessive logging and persistence caused by temporary small changes and rounding.
+2. The rounded value received is more than 1 W from the previous value. So when changes are happening fast, the channel will also be updated fast.
+
+| Configuration Parameter | Description |
+|----------------------------|---------------------------------------------------------------------------------------|
+| udn | The UDN identifies the WeMo Insight Switch |
+| currentPowerSlidingSeconds | Sliding window in seconds for which moving average power is calculated (0 = disabled) |
+| currentPowerDeltaTrigger | Delta triggering immediate channel update (in Watt) |
+
+The moving average calculation can be disabled by setting either `currentPowerSlidingSeconds` or `currentPowerDeltaTrigger` to 0.
+This will cause the channel to be updated the same way as in openHAB versions prior to 3.3.
## Channels
| timespan | Number | Time in seconds over which onTotal applies. Typically 2 weeks except first used. | Insight |
| averagePower | Number:Power | Average power consumption in Watts. | Insight |
| currentPower | Number:Power | Current power consumption of an Insight device. 0 if switched off. | Insight |
+| currentPowerRaw | Number:Power | Current power consumption of an Insight device with full precision (5 mW accuracy, three decimals). 0 if switched off. | Insight |
| energyToday | Number:Energy | Energy in Wh used today. | Insight |
| energyTotal | Number:Energy | Energy in Wh used in total. | Insight |
| standbyLimit | Number:Power | Minimum energy draw in W to register device as switched on (default 8W, configurable via WeMo App). | Insight |
| autoOffTime | DateTime | Time when the heater switches off | Heater |
| heatingRemaining | Number | Shows the remaining heating time | Heater |
-
## Full Example
demo.things:
```
wemo:socket:Switch1 "DemoSwitch" @ "Office" [udn="Socket-1_0-221242K11xxxxx"]
wemo:motion:Sensor1 "MotionSensor" @ "Entrance" [udn="Sensor-1_0-221337L11xxxxx"]
+wemo:insight:Insight1 "Insight" @ "Attic" [udn="Insight-1_0-xxxxxxxxxxxxxx", currentPowerSlidingSeconds=120, currentPowerDeltaTrigger=2]
Bridge wemo:bridge:Bridge-1_0-231445B010xxxx [udn="Bridge-1_0-231445B010xxxx"] {
MZ100 94103EA2B278xxxx "DemoLight1" @ "Living" [ deviceID="94103EA2B278xxxx" ]
Number targetTemp { channel="wemo:heater:HeaterB-1_0-231445B010xxxx:targetTemp" }
DateTime autoOffTime { channel="wemo:heater:HeaterB-1_0-231445B010xxxx:autoOffTime" }
String heaterRemaining { channel="wemo:heater:HeaterB-1_0-231445B010xxxx:heaterRemaining" }
-
```
demo.sitemap:
Setpoint item=targetTemp
Text item=autoOffTime
Number item=heaterRemaining
-
-
}
}
```
result.put(WemoBindingConstants.CHANNEL_STATE, getOnOff(value));
break;
case INSIGHT_POSITION_LASTCHANGEDAT:
- result.put(WemoBindingConstants.CHANNEL_LASTCHANGEDAT, getDateTime(value));
+ result.put(WemoBindingConstants.CHANNEL_LAST_CHANGED_AT, getDateTime(value));
break;
case INSIGHT_POSITION_LASTONFOR:
- result.put(WemoBindingConstants.CHANNEL_LASTONFOR, getNumber(value));
+ result.put(WemoBindingConstants.CHANNEL_LAST_ON_FOR, getNumber(value));
break;
case INSIGHT_POSITION_ONTODAY:
- result.put(WemoBindingConstants.CHANNEL_ONTODAY, getNumber(value));
+ result.put(WemoBindingConstants.CHANNEL_ON_TODAY, getNumber(value));
break;
case INSIGHT_POSITION_ONTOTAL:
- result.put(WemoBindingConstants.CHANNEL_ONTOTAL, getNumber(value));
+ result.put(WemoBindingConstants.CHANNEL_ON_TOTAL, getNumber(value));
break;
case INSIGHT_POSITION_TIMESPAN:
result.put(WemoBindingConstants.CHANNEL_TIMESPAN, getNumber(value));
break;
case INSIGHT_POSITION_AVERAGEPOWER:
- result.put(WemoBindingConstants.CHANNEL_AVERAGEPOWER, getPowerFromWatt(value));
+ result.put(WemoBindingConstants.CHANNEL_AVERAGE_POWER, getPowerFromWatt(value));
break;
case INSIGHT_POSITION_CURRENTPOWER:
- result.put(WemoBindingConstants.CHANNEL_CURRENTPOWER, getPowerFromMilliWatt(value));
+ result.put(WemoBindingConstants.CHANNEL_CURRENT_POWER_RAW, getPowerFromMilliWatt(value));
break;
case INSIGHT_POSITION_ENERGYTODAY:
- result.put(WemoBindingConstants.CHANNEL_ENERGYTODAY, getEnergy(value));
+ result.put(WemoBindingConstants.CHANNEL_ENERGY_TODAY, getEnergy(value));
break;
case INSIGHT_POSITION_ENERGYTOTAL:
- result.put(WemoBindingConstants.CHANNEL_ENERGYTOTAL, getEnergy(value));
+ result.put(WemoBindingConstants.CHANNEL_ENERGY_TOTAL, getEnergy(value));
break;
case INSIGHT_POSITION_STANDBYLIMIT:
- result.put(WemoBindingConstants.CHANNEL_STANDBYLIMIT, getPowerFromMilliWatt(value));
+ result.put(WemoBindingConstants.CHANNEL_STAND_BY_LIMIT, getPowerFromMilliWatt(value));
break;
}
}
}
private State getPowerFromMilliWatt(String value) {
- return new QuantityType<>(new BigDecimal(value).divide(new BigDecimal(1000), 0, RoundingMode.HALF_UP),
+ return new QuantityType<>(new BigDecimal(value).divide(new BigDecimal(1000), 3, RoundingMode.HALF_UP),
Units.WATT);
}
// List of all Channel ids
public static final String CHANNEL_STATE = "state";
- public static final String CHANNEL_MOTIONDETECTION = "motionDetection";
- public static final String CHANNEL_LASTMOTIONDETECTED = "lastMotionDetected";
- public static final String CHANNEL_LASTCHANGEDAT = "lastChangedAt";
- public static final String CHANNEL_LASTONFOR = "lastOnFor";
- public static final String CHANNEL_ONTODAY = "onToday";
- public static final String CHANNEL_ONTOTAL = "onTotal";
+ public static final String CHANNEL_MOTION_DETECTION = "motionDetection";
+ public static final String CHANNEL_LAST_MOTION_DETECTED = "lastMotionDetected";
+ public static final String CHANNEL_LAST_CHANGED_AT = "lastChangedAt";
+ public static final String CHANNEL_LAST_ON_FOR = "lastOnFor";
+ public static final String CHANNEL_ON_TODAY = "onToday";
+ public static final String CHANNEL_ON_TOTAL = "onTotal";
public static final String CHANNEL_TIMESPAN = "timespan";
- public static final String CHANNEL_AVERAGEPOWER = "averagePower";
- public static final String CHANNEL_CURRENTPOWER = "currentPower";
- public static final String CHANNEL_ENERGYTODAY = "energyToday";
- public static final String CHANNEL_ENERGYTOTAL = "energyTotal";
- public static final String CHANNEL_STANDBYLIMIT = "standByLimit";
+ public static final String CHANNEL_AVERAGE_POWER = "averagePower";
+ public static final String CHANNEL_CURRENT_POWER = "currentPower";
+ public static final String CHANNEL_CURRENT_POWER_RAW = "currentPowerRaw";
+ public static final String CHANNEL_ENERGY_TODAY = "energyToday";
+ public static final String CHANNEL_ENERGY_TOTAL = "energyTotal";
+ public static final String CHANNEL_STAND_BY_LIMIT = "standByLimit";
public static final String CHANNEL_BRIGHTNESS = "brightness";
public static final String CHANNEL_RELAY = "relay";
public static final String CHANNEL_SENSOR = "sensor";
- public static final String CHANNEL_ONSTANDBY = "onStandBy";
-
- public static final String CHANNEL_COFFEEMODE = "coffeeMode";
- public static final String CHANNEL_MODETIME = "modeTime";
- public static final String CHANNEL_TIMEREMAINING = "timeRemaining";
- public static final String CHANNEL_WATERLEVELREACHED = "waterLevelReached";
- public static final String CHANNEL_CLEANADVISE = "cleanAdvise";
- public static final String CHANNEL_FILTERADVISE = "filterAdvise";
+ public static final String CHANNEL_ON_STAND_BY = "onStandBy";
+
+ public static final String CHANNEL_COFFEE_MODE = "coffeeMode";
+ public static final String CHANNEL_MODE_TIME = "modeTime";
+ public static final String CHANNEL_TIME_REMAINING = "timeRemaining";
+ public static final String CHANNEL_WATER_LEVEL_REACHED = "waterLevelReached";
+ public static final String CHANNEL_CLEAN_ADVISE = "cleanAdvise";
+ public static final String CHANNEL_FILTER_ADVISE = "filterAdvise";
public static final String CHANNEL_BREWED = "brewed";
- public static final String CHANNEL_LASTCLEANED = "lastCleaned";
-
- public static final String CHANNEL_FADERENABLED = "faderEnabled";
- public static final String CHANNEL_TIMERSTART = "timerStart";
- public static final String CHANNEL_FADERCOUNTDOWNTIME = "faderCountDownTime";
- public static final String CHANNEL_NIGHTMODE = "nightMode";
- public static final String CHANNEL_STARTTIME = "startTime";
- public static final String CHANNEL_ENDTIME = "endTime";
- public static final String CHANNEL_NIGHTMODEBRIGHTNESS = "nightModeBrightness";
-
- public static final String CHANNEL_COOKMODE = "cookMode";
- public static final String CHANNEL_LOWCOOKTIME = "lowCookTime";
- public static final String CHANNEL_WARMCOOKTIME = "warmCooktime";
+ public static final String CHANNEL_LAST_CLEANED = "lastCleaned";
+
+ public static final String CHANNEL_FADER_ENABLED = "faderEnabled";
+ public static final String CHANNEL_TIMER_START = "timerStart";
+ public static final String CHANNEL_FADER_COUNT_DOWN_TIME = "faderCountDownTime";
+ public static final String CHANNEL_NIGHT_MODE = "nightMode";
+ public static final String CHANNEL_START_TIME = "startTime";
+ public static final String CHANNEL_END_TIME = "endTime";
+ public static final String CHANNEL_NIGHT_MODE_BRIGHTNESS = "nightModeBrightness";
+
+ public static final String CHANNEL_COOK_MODE = "cookMode";
+ public static final String CHANNEL_LOW_COOK_TIME = "lowCookTime";
+ public static final String CHANNEL_WARM_COOK_TIME = "warmCooktime";
public static final String CHANNEL_HIGHCOOKTIME = "highCooktime";
- public static final String CHANNEL_COOKEDTIME = "cookedtime";
+ public static final String CHANNEL_COOKED_TIME = "cookedtime";
- public static final String CHANNEL_PURIFIERMODE = "purifierMode";
- public static final String CHANNEL_AIRQUALITY = "airQuality";
+ public static final String CHANNEL_PURIFIER_MODE = "purifierMode";
+ public static final String CHANNEL_AIR_QUALITY = "airQuality";
public static final String CHANNEL_IONIZER = "ionizer";
- public static final String CHANNEL_FILTERLIFE = "filterLife";
- public static final String CHANNEL_EXPIREDFILTERTIME = "expiredFilterTime";
- public static final String CHANNEL_FILTERPRESENT = "filterPresent";
+ public static final String CHANNEL_FILTER_LIFE = "filterLife";
+ public static final String CHANNEL_EXPIRED_FILTER_TIME = "expiredFilterTime";
+ public static final String CHANNEL_FILTER_PRESENT = "filterPresent";
- public static final String CHANNEL_HUMIDIFIERMODE = "humidifierMode";
- public static final String CHANNEL_CURRENTHUMIDITY = "currentHumidity";
- public static final String CHANNEL_DESIREDHUMIDITY = "desiredHumidity";
- public static final String CHANNEL_WATERLEVEL = "waterLEvel";
+ public static final String CHANNEL_HUMIDIFIER_MODE = "humidifierMode";
+ public static final String CHANNEL_CURRENT_HUMIDITY = "currentHumidity";
+ public static final String CHANNEL_DESIRED_HUMIDITY = "desiredHumidity";
+ public static final String CHANNEL_WATER_LEVEL = "waterLEvel";
- public static final String CHANNEL_HEATERMODE = "heaterMode";
- public static final String CHANNEL_CURRENTTEMP = "currentTemperature";
- public static final String CHANNEL_TARGETTEMP = "targetTemperature";
- public static final String CHANNEL_AUTOOFFTIME = "autoOffTime";
- public static final String CHANNEL_HEATINGREMAINING = "heatingRemaining";
+ public static final String CHANNEL_HEATER_MODE = "heaterMode";
+ public static final String CHANNEL_CURRENT_TEMPERATURE = "currentTemperature";
+ public static final String CHANNEL_TARGET_TEMPERATURE = "targetTemperature";
+ public static final String CHANNEL_AUTO_OFF_TIME = "autoOffTime";
+ public static final String CHANNEL_HEATING_REMAINING = "heatingRemaining";
// List of thing configuration properties
public static final String UDN = "udn";
public static final String DEVICE_ID = "deviceID";
- public static final String POLLINGINTERVALL = "pollingInterval";
+ public static final String POLLING_INTERVAL = "pollingInterval";
public static final int DEFAULT_REFRESH_INTERVAL_SECONDS = 60;
public static final int SUBSCRIPTION_DURATION_SECONDS = 600;
public static final int LINK_DISCOVERY_SERVICE_INITIAL_DELAY = 5;
--- /dev/null
+/**
+ * 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;
+
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.concurrent.ConcurrentLinkedDeque;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.library.types.QuantityType;
+
+/**
+ * Class for caching and processing historic values for current power.
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class WemoPowerBank {
+
+ private final Deque<CacheItem> slidingCache = new ConcurrentLinkedDeque<CacheItem>();
+
+ @Nullable
+ private QuantityType<?> previousCurrentPower = null;
+ private int slidingSeconds;
+
+ private class CacheItem {
+ public Instant start;
+ public @Nullable Instant end;
+ public double power;
+
+ public CacheItem(double power, Instant start) {
+ this.start = start;
+ this.power = power;
+ }
+ }
+
+ public WemoPowerBank() {
+ this.slidingSeconds = 60;
+ }
+
+ public WemoPowerBank(int slidingSeconds) {
+ this.slidingSeconds = slidingSeconds;
+ }
+
+ public void clear() {
+ slidingCache.clear();
+ previousCurrentPower = null;
+ }
+
+ public void apply(double value) {
+ this.apply(value, Instant.now());
+ }
+
+ public void apply(double value, Instant now) {
+ if (slidingCache.isEmpty()) {
+ slidingCache.add(new CacheItem(value, now));
+ return;
+ }
+ @Nullable
+ CacheItem last = slidingCache.getLast();
+ last.end = now;
+ Instant windowStart = now.minusSeconds(slidingSeconds);
+ final Iterator<CacheItem> it = slidingCache.iterator();
+ while (it.hasNext()) {
+ CacheItem current = it.next();
+ Instant end = current.end;
+ end = end != null ? end.minusNanos(1) : now;
+ if (end.isBefore(windowStart)) {
+ it.remove();
+ continue;
+ }
+ if (current.start.isBefore(windowStart) && end.isAfter(windowStart)) {
+ // Truncate last item before sliding window.
+ current.start = windowStart;
+ break;
+ }
+ }
+ slidingCache.add(new CacheItem(value, now));
+ }
+
+ public void setPreviousCurrentPower(QuantityType<?> previousCurrentPower) {
+ this.previousCurrentPower = previousCurrentPower;
+ }
+
+ public @Nullable QuantityType<?> getPreviousCurrentPower() {
+ return previousCurrentPower;
+ }
+
+ public double getCalculatedAverage(double currentValue) {
+ double historyWattMillis = 0;
+ long historyMillis = 0;
+ for (CacheItem item : slidingCache) {
+ Instant end = item.end;
+ if (end != null) {
+ long millis = item.start.until(end, ChronoUnit.MILLIS);
+ historyWattMillis += item.power * millis;
+ historyMillis += millis;
+ }
+ }
+ double average;
+ if (historyMillis > 0) {
+ average = historyWattMillis / historyMillis;
+ } else {
+ average = currentValue;
+ }
+
+ return average;
+ }
+}
--- /dev/null
+/**
+ * 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.config;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Configuration for a WeMo Insight Switch
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class WemoInsightConfiguration {
+
+ public static final String CURRENT_POWER_SLIDING_SECONDS = "currentPowerSlidingSeconds";
+ public static final String CURRENT_POWER_DELTA_TRIGGER = "currentPowerDeltaTrigger";
+
+ @Nullable
+ public String udn;
+ public int currentPowerSlidingSeconds = 60;
+ public int currentPowerDeltaTrigger = 1;
+}
wemoHttpCaller.executeCall(wemoURL, soapHeader, content);
updateState(CHANNEL_STATE, OnOffType.ON);
State newMode = new StringType("Brewing");
- updateState(CHANNEL_COFFEEMODE, newMode);
+ updateState(CHANNEL_COFFEE_MODE, newMode);
updateStatus(ThingStatus.ONLINE);
} catch (Exception e) {
logger.warn("Failed to send command '{}' for device '{}': {}", command, getThing().getUID(),
case "0":
updateState(CHANNEL_STATE, OnOffType.ON);
newMode = new StringType("Refill");
- updateState(CHANNEL_COFFEEMODE, newMode);
+ updateState(CHANNEL_COFFEE_MODE, newMode);
break;
case "1":
updateState(CHANNEL_STATE, OnOffType.OFF);
newMode = new StringType("PlaceCarafe");
- updateState(CHANNEL_COFFEEMODE, newMode);
+ updateState(CHANNEL_COFFEE_MODE, newMode);
break;
case "2":
updateState(CHANNEL_STATE, OnOffType.OFF);
newMode = new StringType("RefillWater");
- updateState(CHANNEL_COFFEEMODE, newMode);
+ updateState(CHANNEL_COFFEE_MODE, newMode);
break;
case "3":
updateState(CHANNEL_STATE, OnOffType.OFF);
newMode = new StringType("Ready");
- updateState(CHANNEL_COFFEEMODE, newMode);
+ updateState(CHANNEL_COFFEE_MODE, newMode);
break;
case "4":
updateState(CHANNEL_STATE, OnOffType.ON);
newMode = new StringType("Brewing");
- updateState(CHANNEL_COFFEEMODE, newMode);
+ updateState(CHANNEL_COFFEE_MODE, newMode);
break;
case "5":
updateState(CHANNEL_STATE, OnOffType.OFF);
newMode = new StringType("Brewed");
- updateState(CHANNEL_COFFEEMODE, newMode);
+ updateState(CHANNEL_COFFEE_MODE, newMode);
break;
case "6":
updateState(CHANNEL_STATE, OnOffType.OFF);
newMode = new StringType("CleaningBrewing");
- updateState(CHANNEL_COFFEEMODE, newMode);
+ updateState(CHANNEL_COFFEE_MODE, newMode);
break;
case "7":
updateState(CHANNEL_STATE, OnOffType.OFF);
newMode = new StringType("CleaningSoaking");
- updateState(CHANNEL_COFFEEMODE, newMode);
+ updateState(CHANNEL_COFFEE_MODE, newMode);
break;
case "8":
updateState(CHANNEL_STATE, OnOffType.OFF);
newMode = new StringType("BrewFailCarafeRemoved");
- updateState(CHANNEL_COFFEEMODE, newMode);
+ updateState(CHANNEL_COFFEE_MODE, newMode);
break;
}
break;
case "ModeTime":
newAttributeValue = new DecimalType(attributeValue);
- updateState(CHANNEL_MODETIME, newAttributeValue);
+ updateState(CHANNEL_MODE_TIME, newAttributeValue);
break;
case "TimeRemaining":
newAttributeValue = new DecimalType(attributeValue);
- updateState(CHANNEL_TIMEREMAINING, newAttributeValue);
+ updateState(CHANNEL_TIME_REMAINING, newAttributeValue);
break;
case "WaterLevelReached":
newAttributeValue = new DecimalType(attributeValue);
- updateState(CHANNEL_WATERLEVELREACHED, newAttributeValue);
+ updateState(CHANNEL_WATER_LEVEL_REACHED, newAttributeValue);
break;
case "CleanAdvise":
newAttributeValue = "0".equals(attributeValue) ? OnOffType.OFF : OnOffType.ON;
- updateState(CHANNEL_CLEANADVISE, newAttributeValue);
+ updateState(CHANNEL_CLEAN_ADVISE, newAttributeValue);
break;
case "FilterAdvise":
newAttributeValue = "0".equals(attributeValue) ? OnOffType.OFF : OnOffType.ON;
- updateState(CHANNEL_FILTERADVISE, newAttributeValue);
+ updateState(CHANNEL_FILTER_ADVISE, newAttributeValue);
break;
case "Brewed":
newAttributeValue = getDateTimeState(attributeValue);
case "LastCleaned":
newAttributeValue = getDateTimeState(attributeValue);
if (newAttributeValue != null) {
- updateState(CHANNEL_LASTCLEANED, newAttributeValue);
+ updateState(CHANNEL_LAST_CLEANED, newAttributeValue);
}
break;
}
if (command instanceof RefreshType) {
updateWemoState();
- } else if (CHANNEL_COOKMODE.equals(channelUID.getId())) {
+ } else if (CHANNEL_COOK_MODE.equals(channelUID.getId())) {
String commandString = command.toString();
switch (commandString) {
case "OFF":
case "50":
newMode = new StringType("WARM");
State warmTime = DecimalType.valueOf(time);
- updateState(CHANNEL_WARMCOOKTIME, warmTime);
+ updateState(CHANNEL_WARM_COOK_TIME, warmTime);
break;
case "51":
newMode = new StringType("LOW");
State lowTime = DecimalType.valueOf(time);
- updateState(CHANNEL_LOWCOOKTIME, lowTime);
+ updateState(CHANNEL_LOW_COOK_TIME, lowTime);
break;
case "52":
newMode = new StringType("HIGH");
updateState(CHANNEL_HIGHCOOKTIME, highTime);
break;
}
- updateState(CHANNEL_COOKMODE, newMode);
- updateState(CHANNEL_COOKEDTIME, newCoockedTime);
+ updateState(CHANNEL_COOK_MODE, newMode);
+ updateState(CHANNEL_COOKED_TIME, newCoockedTime);
updateStatus(ThingStatus.ONLINE);
} catch (IOException e) {
logger.debug("Failed to get actual state for device '{}': {}", getThing().getUID(), e.getMessage(), e);
if (command.equals(OnOffType.OFF)) {
State brightnessState = new PercentType("0");
updateState(CHANNEL_BRIGHTNESS, brightnessState);
- updateState(CHANNEL_TIMERSTART, OnOffType.OFF);
+ updateState(CHANNEL_TIMER_START, OnOffType.OFF);
} else {
State brightnessState = new PercentType(currentBrightness);
updateState(CHANNEL_BRIGHTNESS, brightnessState);
setBinaryState(action, argument, value);
}
break;
- case CHANNEL_FADERCOUNTDOWNTIME:
+ case CHANNEL_FADER_COUNT_DOWN_TIME:
argument = "Fader";
if (command instanceof DecimalType) {
int commandValue = Integer.valueOf(String.valueOf(command));
setBinaryState(action, argument, value);
}
break;
- case CHANNEL_FADERENABLED:
+ case CHANNEL_FADER_ENABLED:
argument = "Fader";
if (command.equals(OnOffType.ON)) {
value = "<BinaryState></BinaryState>" + "<Duration></Duration>" + "<EndAction></EndAction>"
}
setBinaryState(action, argument, value);
break;
- case CHANNEL_TIMERSTART:
+ case CHANNEL_TIMER_START:
argument = "Fader";
long ts = System.currentTimeMillis() / 1000;
timeStamp = String.valueOf(ts);
}
setBinaryState(action, argument, value);
break;
- case CHANNEL_NIGHTMODE:
+ case CHANNEL_NIGHT_MODE:
action = "ConfigureNightMode";
argument = "NightModeConfiguration";
String nightModeBrightness = String.valueOf(currentNightModeBrightness);
}
setBinaryState(action, argument, value);
break;
- case CHANNEL_NIGHTMODEBRIGHTNESS:
+ case CHANNEL_NIGHT_MODE_BRIGHTNESS:
action = "ConfigureNightMode";
argument = "NightModeConfiguration";
if (command instanceof PercentType) {
logger.debug("State '{}' for device '{}' received", state, getThing().getUID());
updateState(CHANNEL_BRIGHTNESS, state);
if (state.equals(OnOffType.OFF)) {
- updateState(CHANNEL_TIMERSTART, OnOffType.OFF);
+ updateState(CHANNEL_TIMER_START, OnOffType.OFF);
}
}
break;
State faderMinutes = new DecimalType(faderSeconds / 60);
logger.debug("faderTime '{} minutes' for device '{}' received", faderMinutes,
getThing().getUID());
- updateState(CHANNEL_FADERCOUNTDOWNTIME, faderMinutes);
+ updateState(CHANNEL_FADER_COUNT_DOWN_TIME, faderMinutes);
}
if (splitFader[1] != null) {
State isTimerRunning = splitFader[1].equals("-1") ? OnOffType.OFF : OnOffType.ON;
logger.debug("isTimerRunning '{}' for device '{}' received", isTimerRunning,
getThing().getUID());
- updateState(CHANNEL_TIMERSTART, isTimerRunning);
+ updateState(CHANNEL_TIMER_START, isTimerRunning);
if (isTimerRunning.equals(OnOffType.ON)) {
updateState(CHANNEL_STATE, OnOffType.ON);
}
State isFaderEnabled = splitFader[1].equals("0") ? OnOffType.OFF : OnOffType.ON;
logger.debug("isFaderEnabled '{}' for device '{}' received", isFaderEnabled,
getThing().getUID());
- updateState(CHANNEL_FADERENABLED, isFaderEnabled);
+ updateState(CHANNEL_FADER_ENABLED, isFaderEnabled);
}
break;
case "nightMode":
State nightModeState = "0".equals(value) ? OnOffType.OFF : OnOffType.ON;
currentNightModeState = value;
logger.debug("nightModeState '{}' for device '{}' received", nightModeState, getThing().getUID());
- updateState(CHANNEL_NIGHTMODE, nightModeState);
+ updateState(CHANNEL_NIGHT_MODE, nightModeState);
break;
case "startTime":
State startTimeState = getDateTimeState(value);
logger.debug("startTimeState '{}' for device '{}' received", startTimeState, getThing().getUID());
if (startTimeState != null) {
- updateState(CHANNEL_STARTTIME, startTimeState);
+ updateState(CHANNEL_START_TIME, startTimeState);
}
break;
case "endTime":
State endTimeState = getDateTimeState(value);
logger.debug("endTimeState '{}' for device '{}' received", endTimeState, getThing().getUID());
if (endTimeState != null) {
- updateState(CHANNEL_ENDTIME, endTimeState);
+ updateState(CHANNEL_END_TIME, endTimeState);
}
break;
case "nightModeBrightness":
State nightModeBrightnessState = new PercentType(nightModeBrightnessValue);
logger.debug("nightModeBrightnessState '{}' for device '{}' received", nightModeBrightnessState,
getThing().getUID());
- updateState(CHANNEL_NIGHTMODEBRIGHTNESS, nightModeBrightnessState);
+ updateState(CHANNEL_NIGHT_MODE_BRIGHTNESS, nightModeBrightnessState);
break;
}
}
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.OnOffType;
import org.openhab.core.thing.ChannelUID;
@Override
public void initialize() {
super.initialize();
- Configuration configuration = getConfig();
- if (configuration.get(UDN) != null) {
- logger.debug("Initializing WemoHandler for UDN '{}'", configuration.get(UDN));
- addSubscription(BASICEVENT);
- if (THING_TYPE_INSIGHT.equals(thing.getThingTypeUID())) {
- addSubscription(INSIGHTEVENT);
- }
- pollingJob = scheduler.scheduleWithFixedDelay(this::poll, 0, DEFAULT_REFRESH_INTERVAL_SECONDS,
- TimeUnit.SECONDS);
- updateStatus(ThingStatus.UNKNOWN);
- } else {
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
- "@text/config-status.error.missing-udn");
+ addSubscription(BASICEVENT);
+ if (THING_TYPE_INSIGHT.equals(thing.getThingTypeUID())) {
+ addSubscription(INSIGHTEVENT);
}
+ pollingJob = scheduler.scheduleWithFixedDelay(this::poll, 0, DEFAULT_REFRESH_INTERVAL_SECONDS,
+ TimeUnit.SECONDS);
}
@Override
if (command instanceof RefreshType) {
updateWemoState();
- } else if (CHANNEL_PURIFIERMODE.equals(channelUID.getId())) {
+ } else if (CHANNEL_PURIFIER_MODE.equals(channelUID.getId())) {
attribute = "Mode";
String commandString = command.toString();
switch (commandString) {
} else if (OnOffType.OFF.equals(command)) {
value = "0";
}
- } else if (CHANNEL_HUMIDIFIERMODE.equals(channelUID.getId())) {
+ } else if (CHANNEL_HUMIDIFIER_MODE.equals(channelUID.getId())) {
attribute = "FanMode";
String commandString = command.toString();
switch (commandString) {
value = "5";
break;
}
- } else if (CHANNEL_DESIREDHUMIDITY.equals(channelUID.getId())) {
+ } else if (CHANNEL_DESIRED_HUMIDITY.equals(channelUID.getId())) {
attribute = "DesiredHumidity";
String commandString = command.toString();
switch (commandString) {
value = "4";
break;
}
- } else if (CHANNEL_HEATERMODE.equals(channelUID.getId())) {
+ } else if (CHANNEL_HEATER_MODE.equals(channelUID.getId())) {
attribute = "Mode";
String commandString = command.toString();
switch (commandString) {
value = "4";
break;
}
- } else if (CHANNEL_TARGETTEMP.equals(channelUID.getId())) {
+ } else if (CHANNEL_TARGET_TEMPERATURE.equals(channelUID.getId())) {
attribute = "SetTemperature";
value = command.toString();
}
newMode = new StringType("AUTO");
break;
}
- updateState(CHANNEL_PURIFIERMODE, newMode);
+ updateState(CHANNEL_PURIFIER_MODE, newMode);
} else {
switch (attributeValue) {
case "0":
newMode = new StringType("ECO");
break;
}
- updateState(CHANNEL_HEATERMODE, newMode);
+ updateState(CHANNEL_HEATER_MODE, newMode);
}
break;
case "Ionizer":
newMode = new StringType("GOOD");
break;
}
- updateState(CHANNEL_AIRQUALITY, newMode);
+ updateState(CHANNEL_AIR_QUALITY, newMode);
break;
case "FilterLife":
int filterLife = Integer.valueOf(attributeValue);
} else {
filterLife = Math.round((filterLife / 60480) * 100);
}
- updateState(CHANNEL_FILTERLIFE, new PercentType(String.valueOf(filterLife)));
+ updateState(CHANNEL_FILTER_LIFE, new PercentType(String.valueOf(filterLife)));
break;
case "ExpiredFilterTime":
switch (attributeValue) {
newMode = OnOffType.ON;
break;
}
- updateState(CHANNEL_EXPIREDFILTERTIME, newMode);
+ updateState(CHANNEL_EXPIRED_FILTER_TIME, newMode);
break;
case "FilterPresent":
switch (attributeValue) {
newMode = OnOffType.ON;
break;
}
- updateState(CHANNEL_FILTERPRESENT, newMode);
+ updateState(CHANNEL_FILTER_PRESENT, newMode);
break;
case "FANMode":
switch (attributeValue) {
newMode = new StringType("AUTO");
break;
}
- updateState(CHANNEL_PURIFIERMODE, newMode);
+ updateState(CHANNEL_PURIFIER_MODE, newMode);
break;
case "DesiredHumidity":
switch (attributeValue) {
newMode = new PercentType("100");
break;
}
- updateState(CHANNEL_DESIREDHUMIDITY, newMode);
+ updateState(CHANNEL_DESIRED_HUMIDITY, newMode);
break;
case "CurrentHumidity":
newMode = new StringType(attributeValue);
- updateState(CHANNEL_CURRENTHUMIDITY, newMode);
+ updateState(CHANNEL_CURRENT_HUMIDITY, newMode);
break;
case "Temperature":
newMode = new StringType(attributeValue);
- updateState(CHANNEL_CURRENTTEMP, newMode);
+ updateState(CHANNEL_CURRENT_TEMPERATURE, newMode);
break;
case "SetTemperature":
newMode = new StringType(attributeValue);
- updateState(CHANNEL_TARGETTEMP, newMode);
+ updateState(CHANNEL_TARGET_TEMPERATURE, newMode);
break;
case "AutoOffTime":
newMode = new StringType(attributeValue);
- updateState(CHANNEL_AUTOOFFTIME, newMode);
+ updateState(CHANNEL_AUTO_OFF_TIME, newMode);
break;
case "TimeRemaining":
newMode = new StringType(attributeValue);
- updateState(CHANNEL_HEATINGREMAINING, newMode);
+ updateState(CHANNEL_HEATING_REMAINING, newMode);
break;
}
}
*/
package org.openhab.binding.wemo.internal.handler;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.wemo.internal.InsightParser;
import org.openhab.binding.wemo.internal.WemoBindingConstants;
+import org.openhab.binding.wemo.internal.WemoPowerBank;
+import org.openhab.binding.wemo.internal.config.WemoInsightConfiguration;
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.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;
private final Logger logger = LoggerFactory.getLogger(WemoInsightHandler.class);
private final Map<String, String> stateMap = new ConcurrentHashMap<String, String>();
+ private WemoPowerBank wemoPowerBank = new WemoPowerBank();
+ private int currentPowerSlidingSeconds;
+ private int currentPowerDeltaTrigger;
+
public WemoInsightHandler(Thing thing, UpnpIOService upnpIOService, WemoHttpCall wemoHttpCaller) {
super(thing, upnpIOService, wemoHttpCaller);
}
+ @Override
+ public void initialize() {
+ logger.debug("Initializing WemoInsightHandler for thing '{}'", thing.getUID());
+
+ WemoInsightConfiguration configuration = getConfigAs(WemoInsightConfiguration.class);
+ currentPowerSlidingSeconds = configuration.currentPowerSlidingSeconds;
+ currentPowerDeltaTrigger = configuration.currentPowerDeltaTrigger;
+ wemoPowerBank = new WemoPowerBank(currentPowerSlidingSeconds);
+
+ updateStatus(ThingStatus.UNKNOWN);
+ super.initialize();
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ wemoPowerBank.clear();
+ }
+
@Override
public void onValueReceived(@Nullable String variable, @Nullable String value, @Nullable String service) {
logger.debug("Received pair '{}':'{}' (service '{}') for thing '{}'",
if (insightParams != null) {
InsightParser parser = new InsightParser(insightParams);
Map<String, State> results = parser.parse();
- results.forEach((channel, state) -> {
+ for (Entry<String, State> entry : results.entrySet()) {
+ String channel = entry.getKey();
+ State state = entry.getValue();
+
logger.trace("New InsightParam {} '{}' for device '{}' received", channel, state,
getThing().getUID());
updateState(channel, state);
- });
+ if (channel.equals(WemoBindingConstants.CHANNEL_CURRENT_POWER_RAW)
+ && state instanceof QuantityType) {
+ QuantityType<?> power = state.as(QuantityType.class);
+ if (power != null) {
+ updateCurrentPower(power);
+ }
+ }
+ }
// Update helper channel onStandBy by checking if currentPower > standByLimit.
- var standByLimit = (QuantityType<?>) results.get(WemoBindingConstants.CHANNEL_STANDBYLIMIT);
+ var standByLimit = (QuantityType<?>) results.get(WemoBindingConstants.CHANNEL_STAND_BY_LIMIT);
if (standByLimit != null) {
- var currentPower = (QuantityType<?>) results.get(WemoBindingConstants.CHANNEL_CURRENTPOWER);
+ QuantityType<?> currentPower = wemoPowerBank.getPreviousCurrentPower();
if (currentPower != null) {
- updateState(WemoBindingConstants.CHANNEL_ONSTANDBY,
+ updateState(WemoBindingConstants.CHANNEL_ON_STAND_BY,
OnOffType.from(currentPower.intValue() <= standByLimit.intValue()));
}
}
}
}
}
+
+ private boolean updateCurrentPower(QuantityType<?> power) {
+ double value = power.doubleValue();
+ var roundedValueState = new QuantityType<>(new BigDecimal(value).setScale(0, RoundingMode.HALF_UP),
+ power.getUnit());
+ if (currentPowerSlidingSeconds == 0 || currentPowerDeltaTrigger == 0) {
+ updateState(WemoBindingConstants.CHANNEL_CURRENT_POWER, roundedValueState);
+ return true;
+ }
+
+ wemoPowerBank.apply(value);
+ double averageValue = wemoPowerBank.getCalculatedAverage(value);
+
+ var roundedAverageValueState = new QuantityType<>(
+ new BigDecimal(averageValue).setScale(0, RoundingMode.HALF_UP), power.getUnit());
+
+ if (roundedValueState.equals(wemoPowerBank.getPreviousCurrentPower())) {
+ // No change, skip.
+ return false;
+ }
+
+ double roundedValue = roundedValueState.doubleValue();
+ QuantityType<?> previousCurrentPower = wemoPowerBank.getPreviousCurrentPower();
+
+ if (previousCurrentPower == null) {
+ // Always update initially.
+ return updateCurrentPowerBalanced(roundedValue);
+ }
+ double previousRoundedValue = previousCurrentPower.doubleValue();
+ if (roundedValue < previousRoundedValue - currentPowerDeltaTrigger
+ || roundedValue > previousRoundedValue + currentPowerDeltaTrigger) {
+ // Update immediately when delta is > 1 W.
+ return updateCurrentPowerBalanced(roundedValue);
+ }
+ if (roundedValueState.equals(roundedAverageValueState)) {
+ // Update when rounded value has stabilized.
+ return updateCurrentPowerBalanced(roundedValue);
+ }
+ return false;
+ }
+
+ private boolean updateCurrentPowerBalanced(double power) {
+ var state = new QuantityType<>(power, Units.WATT);
+ updateState(WemoBindingConstants.CHANNEL_CURRENT_POWER, state);
+ wemoPowerBank.setPreviousCurrentPower(state);
+ return true;
+ }
}
super(thing, upnpIOService, wemoHttpCaller);
}
+ @Override
+ public void initialize() {
+ logger.debug("Initializing WemoMotionHandler for thing '{}'", thing.getUID());
+ updateStatus(ThingStatus.UNKNOWN);
+ super.initialize();
+ }
+
@Override
public void onValueReceived(@Nullable String variable, @Nullable String value, @Nullable String service) {
logger.debug("Received pair '{}':'{}' (service '{}') for thing '{}'",
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);
+ updateState(WemoBindingConstants.CHANNEL_MOTION_DETECTION, state);
if (OnOffType.ON.equals(state)) {
State lastMotionDetected = new DateTimeType();
- updateState(WemoBindingConstants.CHANNEL_LASTMOTIONDETECTED, lastMotionDetected);
+ updateState(WemoBindingConstants.CHANNEL_LAST_MOTION_DETECTED, lastMotionDetected);
}
}
}
super(thing, upnpIOService, wemoHttpCaller);
}
+ @Override
+ public void initialize() {
+ logger.debug("Initializing WemoSwitchHandler for thing '{}'", thing.getUID());
+ updateStatus(ThingStatus.UNKNOWN);
+ super.initialize();
+ }
+
@Override
public void onValueReceived(@Nullable String variable, @Nullable String value, @Nullable String service) {
logger.debug("Received pair '{}':'{}' (service '{}') for thing '{}'",
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<config-description:config-descriptions
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
+ xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
+ https://openhab.org/schemas/config-description-1.0.0.xsd">
+
+ <config-description uri="thing-type:wemo:insight">
+ <parameter name="udn" type="text" required="true">
+ <label>Unique Device Name</label>
+ <description>The UDN identifies the WeMo Insight Switch</description>
+ </parameter>
+ <parameter name="currentPowerSlidingSeconds" type="integer" min="0" unit="s">
+ <label>Current Power sliding window</label>
+ <description>Sliding window in seconds for which moving average power is calculated (0 = disabled)</description>
+ <unitLabel>seconds</unitLabel>
+ <default>60</default>
+ <advanced>true</advanced>
+ </parameter>
+ <parameter name="currentPowerDeltaTrigger" type="integer" min="0" unit="W">
+ <label>Current Power delta trigger</label>
+ <description>Delta triggering immediate channel update (in Watt)</description>
+ <unitLabel>Watt</unitLabel>
+ <default>1</default>
+ <advanced>true</advanced>
+ </parameter>
+ </config-description>
+
+</config-description:config-descriptions>
thing-type.config.wemo.bridge.udn.description = The UDN identifies the WeMo Link Device
thing-type.config.wemo.device.udn.label = Unique Device Name
thing-type.config.wemo.device.udn.description = The UDN identifies the WeMo Device
+thing-type.config.wemo.insight.currentPowerDeltaTrigger.label = Current Power delta trigger
+thing-type.config.wemo.insight.currentPowerDeltaTrigger.description = Delta triggering immediate channel update (in Watt)
+thing-type.config.wemo.insight.currentPowerSlidingSeconds.label = Current Power sliding window
+thing-type.config.wemo.insight.currentPowerSlidingSeconds.description = Sliding window in seconds for which moving average power is calculated (0 = disabled)
+thing-type.config.wemo.insight.udn.label = Unique Device Name
+thing-type.config.wemo.insight.udn.description = The UDN identifies the WeMo Insight Switch
# channel types
channel-type.wemo.cookedTime.description = Shows the elapsed cooking time
channel-type.wemo.currentHumidity.label = Current Humidity
channel-type.wemo.currentHumidity.description = Shows the current humidity of a WeMo enabled Holmes Humidifier
-channel-type.wemo.currentPower.label = Power
+channel-type.wemo.currentPower.label = Current Power
channel-type.wemo.currentPower.description = The current power consumption
+channel-type.wemo.currentPowerRaw.label = Current Power Raw
+channel-type.wemo.currentPowerRaw.description = The current power consumption with full precision
channel-type.wemo.currentTemperature.label = Current Temperature
channel-type.wemo.currentTemperature.description = Shows the current temperature measured by a WeMo enabled Heater
channel-type.wemo.desiredHumidity.label = Target Humidity
<channel-type id="currentPower">
<item-type>Number:Power</item-type>
- <label>Power</label>
+ <label>Current Power</label>
<description>The current power consumption</description>
<category>Energy</category>
<state pattern="%.0f %unit%"/>
</channel-type>
+ <channel-type id="currentPowerRaw" advanced="true">
+ <item-type>Number:Power</item-type>
+ <label>Current Power Raw</label>
+ <description>The current power consumption with full precision</description>
+ <category>Energy</category>
+ <state pattern="%.3f %unit%"/>
+ </channel-type>
+
<channel-type id="energyToday" advanced="true">
<item-type>Number:Energy</item-type>
<label>Energy Today</label>
<channel id="timespan" typeId="timespan"/>
<channel id="averagePower" typeId="averagePower"/>
<channel id="currentPower" typeId="currentPower"/>
+ <channel id="currentPowerRaw" typeId="currentPowerRaw"/>
<channel id="energyToday" typeId="energyToday"/>
<channel id="energyTotal" typeId="energyTotal"/>
<channel id="standByLimit" typeId="standByLimit"/>
<representation-property>udn</representation-property>
- <config-description-ref uri="thing-type:wemo:device"/>
+ <config-description-ref uri="thing-type:wemo:insight"/>
</thing-type>
</thing:thing-descriptions>
Map<String, State> result = parser.parse();
assertEquals(OnOffType.ON, result.get(WemoBindingConstants.CHANNEL_STATE));
assertEquals(DateTimeType.valueOf("2022-02-25T15:50:47.000+0100").toZone(ZoneId.systemDefault()),
- result.get(WemoBindingConstants.CHANNEL_LASTCHANGEDAT));
- assertEquals(new DecimalType(109_676), result.get(WemoBindingConstants.CHANNEL_LASTONFOR));
- assertEquals(new DecimalType(80_323), result.get(WemoBindingConstants.CHANNEL_ONTODAY));
- assertEquals(new DecimalType(1_196_960), result.get(WemoBindingConstants.CHANNEL_ONTOTAL));
+ result.get(WemoBindingConstants.CHANNEL_LAST_CHANGED_AT));
+ assertEquals(new DecimalType(109_676), result.get(WemoBindingConstants.CHANNEL_LAST_ON_FOR));
+ assertEquals(new DecimalType(80_323), result.get(WemoBindingConstants.CHANNEL_ON_TODAY));
+ assertEquals(new DecimalType(1_196_960), result.get(WemoBindingConstants.CHANNEL_ON_TOTAL));
assertEquals(new DecimalType(1_209_600), result.get(WemoBindingConstants.CHANNEL_TIMESPAN));
- assertEquals(new QuantityType<>(44, Units.WATT), result.get(WemoBindingConstants.CHANNEL_AVERAGEPOWER));
- assertEquals(new QuantityType<>(41, Units.WATT), result.get(WemoBindingConstants.CHANNEL_CURRENTPOWER));
- assertEquals(new QuantityType<>(505, Units.WATT_HOUR), result.get(WemoBindingConstants.CHANNEL_ENERGYTODAY));
- assertEquals(new QuantityType<>(8056, Units.WATT_HOUR), result.get(WemoBindingConstants.CHANNEL_ENERGYTOTAL));
- assertEquals(new QuantityType<>(8, Units.WATT), result.get(WemoBindingConstants.CHANNEL_STANDBYLIMIT));
+ assertEquals(new QuantityType<>(44, Units.WATT), result.get(WemoBindingConstants.CHANNEL_AVERAGE_POWER));
+ assertEquals(new QuantityType<>(41.4, Units.WATT), result.get(WemoBindingConstants.CHANNEL_CURRENT_POWER_RAW));
+ assertEquals(new QuantityType<>(505, Units.WATT_HOUR), result.get(WemoBindingConstants.CHANNEL_ENERGY_TODAY));
+ assertEquals(new QuantityType<>(8056, Units.WATT_HOUR), result.get(WemoBindingConstants.CHANNEL_ENERGY_TOTAL));
+ assertEquals(new QuantityType<>(8, Units.WATT), result.get(WemoBindingConstants.CHANNEL_STAND_BY_LIMIT));
}
/**
Map<String, State> result = parser.parse();
assertEquals(OnOffType.ON, result.get(WemoBindingConstants.CHANNEL_STATE));
assertEquals(DateTimeType.valueOf("2022-02-27T14:13:47.000+0100").toZone(ZoneId.systemDefault()),
- result.get(WemoBindingConstants.CHANNEL_LASTCHANGEDAT));
- assertEquals(new DecimalType(0), result.get(WemoBindingConstants.CHANNEL_LASTONFOR));
- assertEquals(new DecimalType(0), result.get(WemoBindingConstants.CHANNEL_ONTODAY));
- assertEquals(new DecimalType(0), result.get(WemoBindingConstants.CHANNEL_ONTOTAL));
+ result.get(WemoBindingConstants.CHANNEL_LAST_CHANGED_AT));
+ assertEquals(new DecimalType(0), result.get(WemoBindingConstants.CHANNEL_LAST_ON_FOR));
+ assertEquals(new DecimalType(0), result.get(WemoBindingConstants.CHANNEL_ON_TODAY));
+ assertEquals(new DecimalType(0), result.get(WemoBindingConstants.CHANNEL_ON_TOTAL));
assertEquals(new DecimalType(1_209_600), result.get(WemoBindingConstants.CHANNEL_TIMESPAN));
- assertEquals(new QuantityType<>(13, Units.WATT), result.get(WemoBindingConstants.CHANNEL_AVERAGEPOWER));
- assertEquals(new QuantityType<>(0, Units.WATT), result.get(WemoBindingConstants.CHANNEL_CURRENTPOWER));
- assertEquals(new QuantityType<>(0, Units.WATT_HOUR), result.get(WemoBindingConstants.CHANNEL_ENERGYTODAY));
- assertEquals(new QuantityType<>(0, Units.WATT_HOUR), result.get(WemoBindingConstants.CHANNEL_ENERGYTOTAL));
- assertEquals(new QuantityType<>(8, Units.WATT), result.get(WemoBindingConstants.CHANNEL_STANDBYLIMIT));
+ assertEquals(new QuantityType<>(13, Units.WATT), result.get(WemoBindingConstants.CHANNEL_AVERAGE_POWER));
+ assertEquals(new QuantityType<>(0, Units.WATT), result.get(WemoBindingConstants.CHANNEL_CURRENT_POWER_RAW));
+ assertEquals(new QuantityType<>(0, Units.WATT_HOUR), result.get(WemoBindingConstants.CHANNEL_ENERGY_TODAY));
+ assertEquals(new QuantityType<>(0, Units.WATT_HOUR), result.get(WemoBindingConstants.CHANNEL_ENERGY_TOTAL));
+ assertEquals(new QuantityType<>(8, Units.WATT), result.get(WemoBindingConstants.CHANNEL_STAND_BY_LIMIT));
}
/**
Map<String, State> result = parser.parse();
assertEquals(OnOffType.ON, result.get(WemoBindingConstants.CHANNEL_STATE));
assertEquals(DateTimeType.valueOf("2022-02-25T15:50:47.000+0100").toZone(ZoneId.systemDefault()),
- result.get(WemoBindingConstants.CHANNEL_LASTCHANGEDAT));
- assertEquals(new DecimalType(109_676), result.get(WemoBindingConstants.CHANNEL_LASTONFOR));
- assertEquals(new DecimalType(80_323), result.get(WemoBindingConstants.CHANNEL_ONTODAY));
- assertEquals(new DecimalType(1_196_960), result.get(WemoBindingConstants.CHANNEL_ONTOTAL));
+ result.get(WemoBindingConstants.CHANNEL_LAST_CHANGED_AT));
+ assertEquals(new DecimalType(109_676), result.get(WemoBindingConstants.CHANNEL_LAST_ON_FOR));
+ assertEquals(new DecimalType(80_323), result.get(WemoBindingConstants.CHANNEL_ON_TODAY));
+ assertEquals(new DecimalType(1_196_960), result.get(WemoBindingConstants.CHANNEL_ON_TOTAL));
assertEquals(new DecimalType(1_209_600), result.get(WemoBindingConstants.CHANNEL_TIMESPAN));
- assertEquals(new QuantityType<>(44, Units.WATT), result.get(WemoBindingConstants.CHANNEL_AVERAGEPOWER));
- assertEquals(new QuantityType<>(41, Units.WATT), result.get(WemoBindingConstants.CHANNEL_CURRENTPOWER));
- assertEquals(new QuantityType<>(505, Units.WATT_HOUR), result.get(WemoBindingConstants.CHANNEL_ENERGYTODAY));
- assertEquals(new QuantityType<>(8056, Units.WATT_HOUR), result.get(WemoBindingConstants.CHANNEL_ENERGYTOTAL));
- assertNull(result.get(WemoBindingConstants.CHANNEL_STANDBYLIMIT));
+ assertEquals(new QuantityType<>(44, Units.WATT), result.get(WemoBindingConstants.CHANNEL_AVERAGE_POWER));
+ assertEquals(new QuantityType<>(41.4, Units.WATT), result.get(WemoBindingConstants.CHANNEL_CURRENT_POWER_RAW));
+ assertEquals(new QuantityType<>(505, Units.WATT_HOUR), result.get(WemoBindingConstants.CHANNEL_ENERGY_TODAY));
+ assertEquals(new QuantityType<>(8056, Units.WATT_HOUR), result.get(WemoBindingConstants.CHANNEL_ENERGY_TOTAL));
+ assertNull(result.get(WemoBindingConstants.CHANNEL_STAND_BY_LIMIT));
}
@Test
public void parseInvalidLastChangedAt() {
InsightParser parser = new InsightParser("1|A");
Map<String, State> result = parser.parse();
- assertEquals(UnDefType.UNDEF, result.get(WemoBindingConstants.CHANNEL_LASTCHANGEDAT));
+ assertEquals(UnDefType.UNDEF, result.get(WemoBindingConstants.CHANNEL_LAST_CHANGED_AT));
}
@Test
public void parseInvalidLastOnFor() {
InsightParser parser = new InsightParser("1|1645800647|A");
Map<String, State> result = parser.parse();
- assertEquals(UnDefType.UNDEF, result.get(WemoBindingConstants.CHANNEL_LASTONFOR));
+ assertEquals(UnDefType.UNDEF, result.get(WemoBindingConstants.CHANNEL_LAST_ON_FOR));
}
}
--- /dev/null
+/**
+ * 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;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneId;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.Test;
+import org.openhab.binding.wemo.internal.WemoPowerBank;
+
+/**
+ * Unit tests for {@link WemoPowerBank}.
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class WemoPowerBankTest {
+
+ @Test
+ public void getCalculatedAverageOneMinuteEvenLoad() {
+ var bank = new WemoPowerBank();
+
+ bank.apply(22, getInstantOf("2022-03-08T22:00:00Z"));
+ bank.apply(23, getInstantOf("2022-03-08T22:00:30Z"));
+ bank.apply(99, getInstantOf("2022-03-08T22:01:00Z"));
+
+ assertEquals(22.5, bank.getCalculatedAverage(0));
+ }
+
+ @Test
+ public void getCalculatedAverageOlderValuesAreIgnored() {
+ var bank = new WemoPowerBank();
+
+ bank.apply(99, getInstantOf("2022-03-08T21:59:59Z"));
+ bank.apply(22, getInstantOf("2022-03-08T22:00:00Z"));
+ bank.apply(23, getInstantOf("2022-03-08T22:00:30Z"));
+ bank.apply(99, getInstantOf("2022-03-08T22:01:00Z"));
+
+ assertEquals(22.5, bank.getCalculatedAverage(0));
+ }
+
+ @Test
+ public void getCalculatedAveragePreviousValueBeforeWindowIsConsidered() {
+ var bank = new WemoPowerBank();
+
+ bank.apply(22, getInstantOf("2022-03-08T21:59:59Z"));
+ bank.apply(23, getInstantOf("2022-03-08T22:00:30Z"));
+ bank.apply(99, getInstantOf("2022-03-08T22:01:00Z"));
+
+ assertEquals(22.5, bank.getCalculatedAverage(0));
+ }
+
+ @Test
+ public void getCalculatedAverageOneMinuteUnevenLoad() {
+ var bank = new WemoPowerBank();
+
+ bank.apply(20, getInstantOf("2022-03-08T22:00:00Z"));
+ bank.apply(26, getInstantOf("2022-03-08T22:00:20Z"));
+ bank.apply(99, getInstantOf("2022-03-08T22:01:00Z"));
+
+ assertEquals(24, bank.getCalculatedAverage(0));
+ }
+
+ @Test
+ public void getCalculatedAverageSingleValue() {
+ var bank = new WemoPowerBank();
+
+ bank.apply(20, getInstantOf("2022-03-08T22:00:00Z"));
+
+ assertEquals(50, bank.getCalculatedAverage(50));
+ }
+
+ @Test
+ public void getCalculatedAverageDuplicateInstants() {
+ var bank = new WemoPowerBank();
+
+ bank.apply(22, getInstantOf("2022-03-08T22:00:00Z"));
+ bank.apply(99, getInstantOf("2022-03-08T22:00:30Z"));
+ bank.apply(23, getInstantOf("2022-03-08T22:00:30Z"));
+ bank.apply(99, getInstantOf("2022-03-08T22:01:00Z"));
+
+ assertEquals(22.5, bank.getCalculatedAverage(0));
+ }
+
+ private Instant getInstantOf(String time) {
+ Clock clock = Clock.fixed(Instant.parse(time), ZoneId.of("UTC"));
+ return Instant.now(clock);
+ }
+}
public void assertThatChannelLASTONFORIsUpdatedOnReceivedValue() {
insightParams.lastOnFor = TIME_PARAM;
State expectedStateType = new DecimalType(TIME_PARAM);
- String expectedChannel = CHANNEL_LASTONFOR;
+ String expectedChannel = CHANNEL_LAST_ON_FOR;
testOnValueReceived(expectedChannel, expectedStateType, insightParams.toString());
}
public void assertThatChannelONTODAYIsUpdatedOnReceivedValue() {
insightParams.onToday = TIME_PARAM;
State expectedStateType = new DecimalType(TIME_PARAM);
- String expectedChannel = CHANNEL_ONTODAY;
+ String expectedChannel = CHANNEL_ON_TODAY;
testOnValueReceived(expectedChannel, expectedStateType, insightParams.toString());
}
public void assertThatChannelONTOTALIsUpdatedOnReceivedValue() {
insightParams.onTotal = TIME_PARAM;
State expectedStateType = new DecimalType(TIME_PARAM);
- String expectedChannel = CHANNEL_ONTOTAL;
+ String expectedChannel = CHANNEL_ON_TOTAL;
testOnValueReceived(expectedChannel, expectedStateType, insightParams.toString());
}
public void assertThatChannelAVERAGEPOWERIsUpdatedOnReceivedValue() {
insightParams.avgPower = POWER_PARAM;
State expectedStateType = new QuantityType<>(POWER_PARAM, Units.WATT);
- String expectedChannel = CHANNEL_AVERAGEPOWER;
+ String expectedChannel = CHANNEL_AVERAGE_POWER;
testOnValueReceived(expectedChannel, expectedStateType, insightParams.toString());
}