[wemo] Prevent excessive currentPower channel updates (#12461)

* Introduce algorithm for preventing excessive currentPower updates
* Increase calculation accuracy
* Rename currentPowerAccurate to currentPowerRaw
* Remove duplicated line
* Use interface when declaring double ended queue
* Reformat README to one sentence per line
* Rename constants for consistency and readability

Fixes #12460

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
This commit is contained in:
Jacob Laursen
2022-04-02 12:46:26 +02:00
committed by GitHub
parent 115f5ab534
commit 1239dda691
20 changed files with 602 additions and 182 deletions

View File

@@ -70,34 +70,34 @@ public class InsightParser {
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;
}
}
@@ -137,7 +137,7 @@ public class InsightParser {
}
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);
}

View File

@@ -49,68 +49,69 @@ public class WemoBindingConstants {
// 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_ON_STAND_BY = "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_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_LAST_CLEANED = "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_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_COOKMODE = "cookMode";
public static final String CHANNEL_LOWCOOKTIME = "lowCookTime";
public static final String CHANNEL_WARMCOOKTIME = "warmCooktime";
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;

View File

@@ -0,0 +1,122 @@
/**
* 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;
}
}

View File

@@ -0,0 +1,33 @@
/**
* 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;
}

View File

@@ -161,7 +161,7 @@ public class WemoCoffeeHandler extends WemoBaseThingHandler {
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(),
@@ -244,69 +244,69 @@ public class WemoCoffeeHandler extends WemoBaseThingHandler {
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);
@@ -317,7 +317,7 @@ public class WemoCoffeeHandler extends WemoBaseThingHandler {
case "LastCleaned":
newAttributeValue = getDateTimeState(attributeValue);
if (newAttributeValue != null) {
updateState(CHANNEL_LASTCLEANED, newAttributeValue);
updateState(CHANNEL_LAST_CLEANED, newAttributeValue);
}
break;
}

View File

@@ -128,7 +128,7 @@ public class WemoCrockpotHandler extends WemoBaseThingHandler {
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":
@@ -202,12 +202,12 @@ public class WemoCrockpotHandler extends WemoBaseThingHandler {
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");
@@ -215,8 +215,8 @@ public class WemoCrockpotHandler extends WemoBaseThingHandler {
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);

View File

@@ -152,7 +152,7 @@ public class WemoDimmerHandler extends WemoBaseThingHandler {
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);
@@ -211,7 +211,7 @@ public class WemoDimmerHandler extends WemoBaseThingHandler {
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));
@@ -223,7 +223,7 @@ public class WemoDimmerHandler extends WemoBaseThingHandler {
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>"
@@ -234,7 +234,7 @@ public class WemoDimmerHandler extends WemoBaseThingHandler {
}
setBinaryState(action, argument, value);
break;
case CHANNEL_TIMERSTART:
case CHANNEL_TIMER_START:
argument = "Fader";
long ts = System.currentTimeMillis() / 1000;
timeStamp = String.valueOf(ts);
@@ -265,7 +265,7 @@ public class WemoDimmerHandler extends WemoBaseThingHandler {
}
setBinaryState(action, argument, value);
break;
case CHANNEL_NIGHTMODE:
case CHANNEL_NIGHT_MODE:
action = "ConfigureNightMode";
argument = "NightModeConfiguration";
String nightModeBrightness = String.valueOf(currentNightModeBrightness);
@@ -278,7 +278,7 @@ public class WemoDimmerHandler extends WemoBaseThingHandler {
}
setBinaryState(action, argument, value);
break;
case CHANNEL_NIGHTMODEBRIGHTNESS:
case CHANNEL_NIGHT_MODE_BRIGHTNESS:
action = "ConfigureNightMode";
argument = "NightModeConfiguration";
if (command instanceof PercentType) {
@@ -334,7 +334,7 @@ public class WemoDimmerHandler extends WemoBaseThingHandler {
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;
@@ -358,13 +358,13 @@ public class WemoDimmerHandler extends WemoBaseThingHandler {
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);
}
@@ -373,27 +373,27 @@ public class WemoDimmerHandler extends WemoBaseThingHandler {
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":
@@ -402,7 +402,7 @@ public class WemoDimmerHandler extends WemoBaseThingHandler {
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;
}
}

View File

@@ -21,7 +21,6 @@ import java.util.concurrent.TimeUnit;
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;
@@ -62,21 +61,13 @@ public abstract class WemoHandler extends WemoBaseThingHandler {
@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

View File

@@ -143,7 +143,7 @@ public class WemoHolmesHandler extends WemoBaseThingHandler {
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) {
@@ -170,7 +170,7 @@ public class WemoHolmesHandler extends WemoBaseThingHandler {
} 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) {
@@ -193,7 +193,7 @@ public class WemoHolmesHandler extends WemoBaseThingHandler {
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) {
@@ -213,7 +213,7 @@ public class WemoHolmesHandler extends WemoBaseThingHandler {
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) {
@@ -233,7 +233,7 @@ public class WemoHolmesHandler extends WemoBaseThingHandler {
value = "4";
break;
}
} else if (CHANNEL_TARGETTEMP.equals(channelUID.getId())) {
} else if (CHANNEL_TARGET_TEMPERATURE.equals(channelUID.getId())) {
attribute = "SetTemperature";
value = command.toString();
}
@@ -341,7 +341,7 @@ public class WemoHolmesHandler extends WemoBaseThingHandler {
newMode = new StringType("AUTO");
break;
}
updateState(CHANNEL_PURIFIERMODE, newMode);
updateState(CHANNEL_PURIFIER_MODE, newMode);
} else {
switch (attributeValue) {
case "0":
@@ -360,7 +360,7 @@ public class WemoHolmesHandler extends WemoBaseThingHandler {
newMode = new StringType("ECO");
break;
}
updateState(CHANNEL_HEATERMODE, newMode);
updateState(CHANNEL_HEATER_MODE, newMode);
}
break;
case "Ionizer":
@@ -386,7 +386,7 @@ public class WemoHolmesHandler extends WemoBaseThingHandler {
newMode = new StringType("GOOD");
break;
}
updateState(CHANNEL_AIRQUALITY, newMode);
updateState(CHANNEL_AIR_QUALITY, newMode);
break;
case "FilterLife":
int filterLife = Integer.valueOf(attributeValue);
@@ -395,7 +395,7 @@ public class WemoHolmesHandler extends WemoBaseThingHandler {
} 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) {
@@ -406,7 +406,7 @@ public class WemoHolmesHandler extends WemoBaseThingHandler {
newMode = OnOffType.ON;
break;
}
updateState(CHANNEL_EXPIREDFILTERTIME, newMode);
updateState(CHANNEL_EXPIRED_FILTER_TIME, newMode);
break;
case "FilterPresent":
switch (attributeValue) {
@@ -417,7 +417,7 @@ public class WemoHolmesHandler extends WemoBaseThingHandler {
newMode = OnOffType.ON;
break;
}
updateState(CHANNEL_FILTERPRESENT, newMode);
updateState(CHANNEL_FILTER_PRESENT, newMode);
break;
case "FANMode":
switch (attributeValue) {
@@ -437,7 +437,7 @@ public class WemoHolmesHandler extends WemoBaseThingHandler {
newMode = new StringType("AUTO");
break;
}
updateState(CHANNEL_PURIFIERMODE, newMode);
updateState(CHANNEL_PURIFIER_MODE, newMode);
break;
case "DesiredHumidity":
switch (attributeValue) {
@@ -457,27 +457,27 @@ public class WemoHolmesHandler extends WemoBaseThingHandler {
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;
}
}

View File

@@ -12,17 +12,23 @@
*/
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;
@@ -41,10 +47,33 @@ public class WemoInsightHandler extends WemoHandler {
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 '{}'",
@@ -66,22 +95,79 @@ public class WemoInsightHandler extends WemoHandler {
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;
}
}

View File

@@ -44,6 +44,13 @@ public class WemoMotionHandler extends WemoHandler {
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 '{}'",
@@ -66,10 +73,10 @@ public class WemoMotionHandler extends WemoHandler {
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);
}
}
}

View File

@@ -43,6 +43,13 @@ public class WemoSwitchHandler extends WemoHandler {
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 '{}'",

View File

@@ -0,0 +1,29 @@
<?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>

View File

@@ -44,6 +44,12 @@ thing-type.config.wemo.bridge.udn.label = Unique Device Name
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
@@ -81,8 +87,10 @@ channel-type.wemo.cookedTime.label = CookedTime
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

View File

@@ -74,12 +74,20 @@
<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>

View File

@@ -17,6 +17,7 @@
<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"/>
@@ -25,7 +26,7 @@
<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>

View File

@@ -48,16 +48,16 @@ public class InsightParserTest {
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));
}
/**
@@ -70,16 +70,16 @@ public class InsightParserTest {
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));
}
/**
@@ -93,29 +93,29 @@ public class InsightParserTest {
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));
}
}

View File

@@ -0,0 +1,103 @@
/**
* 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);
}
}