Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wemo] Refactor Insight Switch parser #12380

Merged
merged 2 commits into from
Feb 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/**
* 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.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.Units;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;

/**
* Parser for WeMo Insight Switch values.
*
* @author Jacob Laursen - Initial contribution
*/
@NonNullByDefault
public class InsightParser {

private static final int INSIGHT_POSITION_STATE = 0;
private static final int INSIGHT_POSITION_LASTCHANGEDAT = 1;
private static final int INSIGHT_POSITION_LASTONFOR = 2;
private static final int INSIGHT_POSITION_ONTODAY = 3;
private static final int INSIGHT_POSITION_ONTOTAL = 4;
private static final int INSIGHT_POSITION_TIMESPAN = 5;
private static final int INSIGHT_POSITION_AVERAGEPOWER = 6;
private static final int INSIGHT_POSITION_CURRENTPOWER = 7;
private static final int INSIGHT_POSITION_ENERGYTODAY = 8;
private static final int INSIGHT_POSITION_ENERGYTOTAL = 9;
private static final int INSIGHT_POSITION_STANDBYLIMIT = 10;

private final String value;

public InsightParser(String value) {
this.value = value;
}

/**
* Parse provided string of values.
*
* @return Map of channel id's with states
*/
public Map<String, State> parse() {
HashMap<String, State> result = new HashMap<>();
String[] params = value.split("\\|");
for (int i = 0; i < params.length; i++) {
String value = params[i];
switch (i) {
case INSIGHT_POSITION_STATE:
result.put(WemoBindingConstants.CHANNEL_STATE, getOnOff(value));
break;
case INSIGHT_POSITION_LASTCHANGEDAT:
result.put(WemoBindingConstants.CHANNEL_LASTCHANGEDAT, getDateTime(value));
break;
case INSIGHT_POSITION_LASTONFOR:
result.put(WemoBindingConstants.CHANNEL_LASTONFOR, getNumber(value));
break;
case INSIGHT_POSITION_ONTODAY:
result.put(WemoBindingConstants.CHANNEL_ONTODAY, getNumber(value));
break;
case INSIGHT_POSITION_ONTOTAL:
result.put(WemoBindingConstants.CHANNEL_ONTOTAL, 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));
break;
case INSIGHT_POSITION_CURRENTPOWER:
result.put(WemoBindingConstants.CHANNEL_CURRENTPOWER, getPowerFromMilliWatt(value));
break;
case INSIGHT_POSITION_ENERGYTODAY:
result.put(WemoBindingConstants.CHANNEL_ENERGYTODAY, getEnergy(value));
break;
case INSIGHT_POSITION_ENERGYTOTAL:
result.put(WemoBindingConstants.CHANNEL_ENERGYTOTAL, getEnergy(value));
break;
case INSIGHT_POSITION_STANDBYLIMIT:
result.put(WemoBindingConstants.CHANNEL_STANDBYLIMIT, getPowerFromMilliWatt(value));
break;
}
}
return result;
}

private State getOnOff(String value) {
return "0".equals(value) ? OnOffType.OFF : OnOffType.ON;
jlaur marked this conversation as resolved.
Show resolved Hide resolved
}

private State getDateTime(String value) {
long lastChangedAt = 0;
try {
lastChangedAt = Long.parseLong(value);
} catch (NumberFormatException e) {
return UnDefType.UNDEF;
}

State lastChangedAtState = new DateTimeType(
ZonedDateTime.ofInstant(Instant.ofEpochSecond(lastChangedAt), ZoneId.systemDefault()));
if (lastChangedAt == 0) {
return UnDefType.UNDEF;
}
return lastChangedAtState;
}

private State getNumber(String value) {
try {
return DecimalType.valueOf(value);
} catch (NumberFormatException e) {
return UnDefType.UNDEF;
}
}

private State getPowerFromWatt(String value) {
return new QuantityType<>(DecimalType.valueOf(value), Units.WATT);
}

private State getPowerFromMilliWatt(String value) {
return new QuantityType<>(new BigDecimal(value).divide(new BigDecimal(1000), 0, RoundingMode.HALF_UP),
Units.WATT);
}

private State getEnergy(String value) {
// recalculate mW-mins to Wh
return new QuantityType<>(new BigDecimal(value).divide(new BigDecimal(60_000), 0, RoundingMode.HALF_UP),
Units.WATT_HOUR);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,17 @@
*/
package org.openhab.binding.wemo.internal.handler;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.wemo.internal.InsightParser;
import org.openhab.binding.wemo.internal.WemoBindingConstants;
import org.openhab.binding.wemo.internal.http.WemoHttpCall;
import org.openhab.core.io.transport.upnp.UpnpIOService;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.types.State;
Expand Down Expand Up @@ -71,94 +64,21 @@ public void onValueReceived(@Nullable String variable, @Nullable String value, @
String insightParams = stateMap.get(variable);

if (insightParams != null) {
String[] splitInsightParams = insightParams.split("\\|");

if (splitInsightParams[0] != null) {
OnOffType binaryState = "0".equals(splitInsightParams[0]) ? OnOffType.OFF : OnOffType.ON;
logger.trace("New InsightParam binaryState '{}' for device '{}' received", binaryState,
getThing().getUID());
updateState(WemoBindingConstants.CHANNEL_STATE, binaryState);
}

long lastChangedAt = 0;
try {
lastChangedAt = Long.parseLong(splitInsightParams[1]) * 1000; // convert s to ms
} catch (NumberFormatException e) {
logger.warn("Unable to parse lastChangedAt value '{}' for device '{}'; expected long",
splitInsightParams[1], getThing().getUID());
}
ZonedDateTime zoned = ZonedDateTime.ofInstant(Instant.ofEpochMilli(lastChangedAt),
TimeZone.getDefault().toZoneId());

State lastChangedAtState = new DateTimeType(zoned);
if (lastChangedAt != 0) {
logger.trace("New InsightParam lastChangedAt '{}' for device '{}' received", lastChangedAtState,
getThing().getUID());
updateState(WemoBindingConstants.CHANNEL_LASTCHANGEDAT, lastChangedAtState);
}

State lastOnFor = DecimalType.valueOf(splitInsightParams[2]);
logger.trace("New InsightParam lastOnFor '{}' for device '{}' received", lastOnFor,
getThing().getUID());
updateState(WemoBindingConstants.CHANNEL_LASTONFOR, lastOnFor);

State onToday = DecimalType.valueOf(splitInsightParams[3]);
logger.trace("New InsightParam onToday '{}' for device '{}' received", onToday, getThing().getUID());
updateState(WemoBindingConstants.CHANNEL_ONTODAY, onToday);

State onTotal = DecimalType.valueOf(splitInsightParams[4]);
logger.trace("New InsightParam onTotal '{}' for device '{}' received", onTotal, getThing().getUID());
updateState(WemoBindingConstants.CHANNEL_ONTOTAL, onTotal);

State timespan = DecimalType.valueOf(splitInsightParams[5]);
logger.trace("New InsightParam timespan '{}' for device '{}' received", timespan, getThing().getUID());
updateState(WemoBindingConstants.CHANNEL_TIMESPAN, timespan);

State averagePower = new QuantityType<>(DecimalType.valueOf(splitInsightParams[6]), Units.WATT); // natively
// given
// in W
logger.trace("New InsightParam averagePower '{}' for device '{}' received", averagePower,
getThing().getUID());
updateState(WemoBindingConstants.CHANNEL_AVERAGEPOWER, averagePower);

BigDecimal currentMW = new BigDecimal(splitInsightParams[7]);
State currentPower = new QuantityType<>(currentMW.divide(new BigDecimal(1000), 0, RoundingMode.HALF_UP),
Units.WATT); // recalculate
// mW to W
logger.trace("New InsightParam currentPower '{}' for device '{}' received", currentPower,
getThing().getUID());
updateState(WemoBindingConstants.CHANNEL_CURRENTPOWER, currentPower);

BigDecimal energyTodayMWMin = new BigDecimal(splitInsightParams[8]);
// recalculate mW-mins to Wh
State energyToday = new QuantityType<>(
energyTodayMWMin.divide(new BigDecimal(60000), 0, RoundingMode.HALF_UP), Units.WATT_HOUR);
logger.trace("New InsightParam energyToday '{}' for device '{}' received", energyToday,
getThing().getUID());
updateState(WemoBindingConstants.CHANNEL_ENERGYTODAY, energyToday);

BigDecimal energyTotalMWMin = new BigDecimal(splitInsightParams[9]);
// recalculate mW-mins to Wh
State energyTotal = new QuantityType<>(
energyTotalMWMin.divide(new BigDecimal(60000), 0, RoundingMode.HALF_UP), Units.WATT_HOUR);
logger.trace("New InsightParam energyTotal '{}' for device '{}' received", energyTotal,
getThing().getUID());
updateState(WemoBindingConstants.CHANNEL_ENERGYTOTAL, energyTotal);

if (splitInsightParams.length > 10 && splitInsightParams[10] != null) {
BigDecimal standByLimitMW = new BigDecimal(splitInsightParams[10]);
State standByLimit = new QuantityType<>(
standByLimitMW.divide(new BigDecimal(1000), 0, RoundingMode.HALF_UP), Units.WATT); // recalculate
// mW to W
logger.trace("New InsightParam standByLimit '{}' for device '{}' received", standByLimit,
InsightParser parser = new InsightParser(insightParams);
Map<String, State> results = parser.parse();
results.forEach((channel, state) -> {
logger.trace("New InsightParam {} '{}' for device '{}' received", channel, state,
getThing().getUID());
updateState(WemoBindingConstants.CHANNEL_STANDBYLIMIT, standByLimit);

if (currentMW.divide(new BigDecimal(1000), 0, RoundingMode.HALF_UP).intValue() > standByLimitMW
.divide(new BigDecimal(1000), 0, RoundingMode.HALF_UP).intValue()) {
updateState(WemoBindingConstants.CHANNEL_ONSTANDBY, OnOffType.OFF);
} else {
updateState(WemoBindingConstants.CHANNEL_ONSTANDBY, OnOffType.ON);
updateState(channel, state);
});

// Update helper channel onStandBy by checking if currentPower > standByLimit.
var standByLimit = (QuantityType<?>) results.get(WemoBindingConstants.CHANNEL_STANDBYLIMIT);
if (standByLimit != null) {
var currentPower = (QuantityType<?>) results.get(WemoBindingConstants.CHANNEL_CURRENTPOWER);
if (currentPower != null) {
updateState(WemoBindingConstants.CHANNEL_ONSTANDBY,
OnOffType.from(currentPower.intValue() <= standByLimit.intValue()));
}
}
}
Expand Down
Loading