2 * Copyright (c) 2010-2022 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.wemo.internal.handler;
15 import java.math.BigDecimal;
16 import java.math.RoundingMode;
18 import java.util.Map.Entry;
19 import java.util.concurrent.ConcurrentHashMap;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.jupnp.UpnpService;
24 import org.openhab.binding.wemo.internal.InsightParser;
25 import org.openhab.binding.wemo.internal.WemoBindingConstants;
26 import org.openhab.binding.wemo.internal.WemoPowerBank;
27 import org.openhab.binding.wemo.internal.config.WemoInsightConfiguration;
28 import org.openhab.binding.wemo.internal.http.WemoHttpCall;
29 import org.openhab.core.io.transport.upnp.UpnpIOService;
30 import org.openhab.core.library.types.OnOffType;
31 import org.openhab.core.library.types.QuantityType;
32 import org.openhab.core.library.unit.Units;
33 import org.openhab.core.thing.Thing;
34 import org.openhab.core.thing.ThingStatus;
35 import org.openhab.core.types.State;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
40 * The {@link WemoInsightHandler} is responsible for handling commands for
41 * a WeMo Insight Switch.
43 * @author Jacob Laursen - Initial contribution
46 public class WemoInsightHandler extends WemoHandler {
48 private final Logger logger = LoggerFactory.getLogger(WemoInsightHandler.class);
49 private final Map<String, String> stateMap = new ConcurrentHashMap<String, String>();
51 private WemoPowerBank wemoPowerBank = new WemoPowerBank();
52 private int currentPowerSlidingSeconds;
53 private int currentPowerDeltaTrigger;
55 public WemoInsightHandler(Thing thing, UpnpIOService upnpIOService, UpnpService upnpService,
56 WemoHttpCall wemoHttpCaller) {
57 super(thing, upnpIOService, upnpService, wemoHttpCaller);
61 public void initialize() {
62 logger.debug("Initializing WemoInsightHandler for thing '{}'", thing.getUID());
64 WemoInsightConfiguration configuration = getConfigAs(WemoInsightConfiguration.class);
65 currentPowerSlidingSeconds = configuration.currentPowerSlidingSeconds;
66 currentPowerDeltaTrigger = configuration.currentPowerDeltaTrigger;
67 wemoPowerBank = new WemoPowerBank(currentPowerSlidingSeconds);
69 updateStatus(ThingStatus.UNKNOWN);
74 public void dispose() {
76 wemoPowerBank.clear();
80 public void onValueReceived(@Nullable String variable, @Nullable String value, @Nullable String service) {
81 logger.debug("Received pair '{}':'{}' (service '{}') for thing '{}'",
82 new Object[] { variable, value, service, this.getThing().getUID() });
84 updateStatus(ThingStatus.ONLINE);
86 if (!"BinaryState".equals(variable) && !"InsightParams".equals(variable)) {
90 if (variable != null && value != null) {
91 this.stateMap.put(variable, value);
94 if (value != null && value.length() > 1) {
95 String insightParams = stateMap.get(variable);
97 if (insightParams != null) {
98 InsightParser parser = new InsightParser(insightParams);
99 Map<String, State> results = parser.parse();
100 for (Entry<String, State> entry : results.entrySet()) {
101 String channel = entry.getKey();
102 State state = entry.getValue();
104 logger.trace("New InsightParam {} '{}' for device '{}' received", channel, state,
105 getThing().getUID());
106 updateState(channel, state);
107 if (channel.equals(WemoBindingConstants.CHANNEL_CURRENT_POWER_RAW)
108 && state instanceof QuantityType) {
109 QuantityType<?> power = state.as(QuantityType.class);
111 updateCurrentPower(power);
116 // Update helper channel onStandBy by checking if currentPower > standByLimit.
117 var standByLimit = (QuantityType<?>) results.get(WemoBindingConstants.CHANNEL_STAND_BY_LIMIT);
118 if (standByLimit != null) {
119 QuantityType<?> currentPower = wemoPowerBank.getPreviousCurrentPower();
120 if (currentPower != null) {
121 updateState(WemoBindingConstants.CHANNEL_ON_STAND_BY,
122 OnOffType.from(currentPower.intValue() <= standByLimit.intValue()));
129 private boolean updateCurrentPower(QuantityType<?> power) {
130 double value = power.doubleValue();
131 var roundedValueState = new QuantityType<>(new BigDecimal(value).setScale(0, RoundingMode.HALF_UP),
133 if (currentPowerSlidingSeconds == 0 || currentPowerDeltaTrigger == 0) {
134 updateState(WemoBindingConstants.CHANNEL_CURRENT_POWER, roundedValueState);
138 wemoPowerBank.apply(value);
139 double averageValue = wemoPowerBank.getCalculatedAverage(value);
141 var roundedAverageValueState = new QuantityType<>(
142 new BigDecimal(averageValue).setScale(0, RoundingMode.HALF_UP), power.getUnit());
144 if (roundedValueState.equals(wemoPowerBank.getPreviousCurrentPower())) {
149 double roundedValue = roundedValueState.doubleValue();
150 QuantityType<?> previousCurrentPower = wemoPowerBank.getPreviousCurrentPower();
152 if (previousCurrentPower == null) {
153 // Always update initially.
154 return updateCurrentPowerBalanced(roundedValue);
156 double previousRoundedValue = previousCurrentPower.doubleValue();
157 if (roundedValue < previousRoundedValue - currentPowerDeltaTrigger
158 || roundedValue > previousRoundedValue + currentPowerDeltaTrigger) {
159 // Update immediately when delta is > 1 W.
160 return updateCurrentPowerBalanced(roundedValue);
162 if (roundedValueState.equals(roundedAverageValueState)) {
163 // Update when rounded value has stabilized.
164 return updateCurrentPowerBalanced(roundedValue);
169 private boolean updateCurrentPowerBalanced(double power) {
170 var state = new QuantityType<>(power, Units.WATT);
171 updateState(WemoBindingConstants.CHANNEL_CURRENT_POWER, state);
172 wemoPowerBank.setPreviousCurrentPower(state);