]> git.basschouten.com Git - openhab-addons.git/blob
501966b123b4458dd2d7e9ca14d0e43417eee944
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.wemo.internal.handler;
14
15 import java.math.BigDecimal;
16 import java.math.RoundingMode;
17 import java.util.Map;
18 import java.util.Map.Entry;
19 import java.util.concurrent.ConcurrentHashMap;
20
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;
38
39 /**
40  * The {@link WemoInsightHandler} is responsible for handling commands for
41  * a WeMo Insight Switch.
42  *
43  * @author Jacob Laursen - Initial contribution
44  */
45 @NonNullByDefault
46 public class WemoInsightHandler extends WemoHandler {
47
48     private final Logger logger = LoggerFactory.getLogger(WemoInsightHandler.class);
49     private final Map<String, String> stateMap = new ConcurrentHashMap<String, String>();
50
51     private WemoPowerBank wemoPowerBank = new WemoPowerBank();
52     private int currentPowerSlidingSeconds;
53     private int currentPowerDeltaTrigger;
54
55     public WemoInsightHandler(Thing thing, UpnpIOService upnpIOService, UpnpService upnpService,
56             WemoHttpCall wemoHttpCaller) {
57         super(thing, upnpIOService, upnpService, wemoHttpCaller);
58     }
59
60     @Override
61     public void initialize() {
62         logger.debug("Initializing WemoInsightHandler for thing '{}'", thing.getUID());
63
64         WemoInsightConfiguration configuration = getConfigAs(WemoInsightConfiguration.class);
65         currentPowerSlidingSeconds = configuration.currentPowerSlidingSeconds;
66         currentPowerDeltaTrigger = configuration.currentPowerDeltaTrigger;
67         wemoPowerBank = new WemoPowerBank(currentPowerSlidingSeconds);
68
69         updateStatus(ThingStatus.UNKNOWN);
70         super.initialize();
71     }
72
73     @Override
74     public void dispose() {
75         super.dispose();
76         wemoPowerBank.clear();
77     }
78
79     @Override
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() });
83
84         updateStatus(ThingStatus.ONLINE);
85
86         if (!"BinaryState".equals(variable) && !"InsightParams".equals(variable)) {
87             return;
88         }
89
90         if (variable != null && value != null) {
91             this.stateMap.put(variable, value);
92         }
93
94         if (value != null && value.length() > 1) {
95             String insightParams = stateMap.get(variable);
96
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();
103
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);
110                         if (power != null) {
111                             updateCurrentPower(power);
112                         }
113                     }
114                 }
115
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()));
123                     }
124                 }
125             }
126         }
127     }
128
129     private boolean updateCurrentPower(QuantityType<?> power) {
130         double value = power.doubleValue();
131         var roundedValueState = new QuantityType<>(new BigDecimal(value).setScale(0, RoundingMode.HALF_UP),
132                 power.getUnit());
133         if (currentPowerSlidingSeconds == 0 || currentPowerDeltaTrigger == 0) {
134             updateState(WemoBindingConstants.CHANNEL_CURRENT_POWER, roundedValueState);
135             return true;
136         }
137
138         wemoPowerBank.apply(value);
139         double averageValue = wemoPowerBank.getCalculatedAverage(value);
140
141         var roundedAverageValueState = new QuantityType<>(
142                 new BigDecimal(averageValue).setScale(0, RoundingMode.HALF_UP), power.getUnit());
143
144         if (roundedValueState.equals(wemoPowerBank.getPreviousCurrentPower())) {
145             // No change, skip.
146             return false;
147         }
148
149         double roundedValue = roundedValueState.doubleValue();
150         QuantityType<?> previousCurrentPower = wemoPowerBank.getPreviousCurrentPower();
151
152         if (previousCurrentPower == null) {
153             // Always update initially.
154             return updateCurrentPowerBalanced(roundedValue);
155         }
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);
161         }
162         if (roundedValueState.equals(roundedAverageValueState)) {
163             // Update when rounded value has stabilized.
164             return updateCurrentPowerBalanced(roundedValue);
165         }
166         return false;
167     }
168
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);
173         return true;
174     }
175 }