]> git.basschouten.com Git - openhab-addons.git/blob
1fe0339a2bc0a872f9e01fb7aeccae3745870f25
[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.solarwatt.internal.handler;
14
15 import static org.openhab.binding.solarwatt.internal.SolarwattBindingConstants.*;
16
17 import java.math.BigDecimal;
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.Set;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.solarwatt.internal.channel.SolarwattChannelTypeProvider;
25 import org.openhab.binding.solarwatt.internal.domain.model.Device;
26 import org.openhab.binding.solarwatt.internal.domain.model.EVStation;
27 import org.openhab.binding.solarwatt.internal.domain.model.Location;
28 import org.openhab.binding.solarwatt.internal.domain.model.PowerMeter;
29 import org.openhab.core.library.types.QuantityType;
30 import org.openhab.core.library.unit.Units;
31 import org.openhab.core.thing.Thing;
32 import org.openhab.core.types.State;
33
34 /**
35  * The concrete device handlers process the location specific commands.
36  *
37  * @author Sven Carstens - Initial contribution
38  */
39 @NonNullByDefault
40 public class LocationHandler extends SimpleDeviceHandler {
41
42     public LocationHandler(Thing thing, SolarwattChannelTypeProvider channelTypeProvider) {
43         super(thing, channelTypeProvider);
44     }
45
46     /**
47      * Add calculated states for unmetered consum.
48      *
49      * First calculate and then call super to submit the state
50      */
51     @Override
52     protected void updateDeviceChannels() {
53         // add calculated states
54         this.updateCalculated();
55
56         // submits all states
57         super.updateDeviceChannels();
58     }
59
60     /**
61      * Add calculated channels for unmetered consum.
62      *
63      * First calculate and then call super to submit the channels
64      */
65     @Override
66     protected void initDeviceChannels() {
67         // add calculated channels
68         final EnergyManagerHandler bridgeHandler = this.getEnergyManagerHandler();
69         if (bridgeHandler != null) {
70             // update the unmetered channel via subtracting the outerconsumers
71             // from the powerConsumed
72             Location locationDevice = (Location) this.getDevice();
73             if (locationDevice != null) {
74                 locationDevice.addChannel(CHANNEL_POWER_DIRECT_CONSUMED.getChannelName(), Units.WATT,
75                         Device.WATT_CATEGORY, false);
76                 locationDevice.addChannel(CHANNEL_WORK_DIRECT_CONSUMED.getChannelName(), Units.WATT_HOUR,
77                         Device.WATT_HOUR_CATEGORY, false);
78                 locationDevice.addChannel(CHANNEL_POWER_CONSUMED_UNMETERED.getChannelName(), Units.WATT,
79                         Device.WATT_CATEGORY, false);
80                 locationDevice.addChannel(CHANNEL_WORK_CONSUMED_UNMETERED.getChannelName(), Units.WATT_HOUR,
81                         Device.WATT_HOUR_CATEGORY, false);
82             }
83         }
84
85         // submit all channels
86         super.initDeviceChannels();
87     }
88
89     private void updateCalculated() {
90         final EnergyManagerHandler bridgeHandler = this.getEnergyManagerHandler();
91         final Location locationDevice = (Location) this.getDevice();
92         if (bridgeHandler != null && locationDevice != null) {
93             this.calculateDirectConsumption(locationDevice);
94             this.calculateUnmeteredConsumption(bridgeHandler, locationDevice);
95         }
96     }
97
98     private void calculateUnmeteredConsumption(EnergyManagerHandler bridgeHandler, Location locationDevice) {
99         // update the unmetered channels via subtracting
100         // the outerconsumers from the powerConsumed
101         BigDecimal powerConsumed = locationDevice.getBigDecimalFromChannel(CHANNEL_POWER_CONSUMED.getChannelName());
102         BigDecimal workConsumed = locationDevice.getBigDecimalFromChannel(CHANNEL_WORK_CONSUMED.getChannelName());
103
104         final List<BigDecimal> powerOuter = new ArrayList<>();
105         final List<BigDecimal> workOuter = new ArrayList<>();
106
107         Set<String> outerConsumers = locationDevice.getDevicesMap().getOuterConsumer();
108         if (outerConsumers != null) {
109             outerConsumers.stream().map(bridgeHandler::getDeviceFromGuid).forEach(outerDevice -> {
110                 if (outerDevice instanceof PowerMeter) {
111                     powerOuter.add(outerDevice.getBigDecimalFromChannel(CHANNEL_POWER_IN.getChannelName()));
112                     workOuter.add(outerDevice.getBigDecimalFromChannel(CHANNEL_WORK_IN.getChannelName()));
113                 } else if (outerDevice instanceof EVStation) {
114                     powerOuter.add(outerDevice.getBigDecimalFromChannel(CHANNEL_POWER_AC_IN.getChannelName()));
115                     workOuter.add(outerDevice.getBigDecimalFromChannel(CHANNEL_WORK_AC_IN.getChannelName()));
116                 }
117             });
118
119             BigDecimal powerConsumedUnmetered = powerOuter.stream().reduce(powerConsumed, BigDecimal::subtract);
120             if (powerConsumedUnmetered.compareTo(BigDecimal.ONE) > 0) {
121                 // sometimes the powerConsumed is exactly the power of the unmetered devices
122                 // the resulting zero for unmetered consumption is not correct
123                 locationDevice.addStateBigDecimal(CHANNEL_POWER_CONSUMED_UNMETERED, powerConsumedUnmetered, Units.WATT);
124             }
125             locationDevice.addStateBigDecimal(CHANNEL_WORK_CONSUMED_UNMETERED,
126                     workOuter.stream().reduce(workConsumed, BigDecimal::subtract), Units.WATT_HOUR);
127         }
128     }
129
130     private void calculateDirectConsumption(Location locationDevice) {
131         // calculate direct consumption for display purposes
132         locationDevice.addState(CHANNEL_POWER_DIRECT_CONSUMED.getChannelName(),
133                 this.calculateQuantityDifference(locationDevice.getState(CHANNEL_POWER_SELF_CONSUMED.getChannelName()),
134                         locationDevice.getState(CHANNEL_POWER_BUFFERED_FROM_PRODUCERS.getChannelName())));
135
136         locationDevice.addState(CHANNEL_WORK_DIRECT_CONSUMED.getChannelName(),
137                 this.calculateQuantityDifference(locationDevice.getState(CHANNEL_WORK_SELF_CONSUMED.getChannelName()),
138                         locationDevice.getState(CHANNEL_WORK_BUFFERED_FROM_PRODUCERS.getChannelName())));
139     }
140
141     /**
142      * Helper to generate a new state calculated from the difference between two states.
143      *
144      * channelTarget = channelValue - channelSubtract
145      *
146      * @param stateValue value from which we subtract
147      * @param stateSubtract value to substrct
148      * @return {@link State} of calculated value
149      */
150     private @Nullable State calculateQuantityDifference(@Nullable State stateValue, @Nullable State stateSubtract) {
151         if (stateValue != null && stateSubtract != null) {
152             @SuppressWarnings("rawtypes")
153             QuantityType quantityValue = stateValue.as(QuantityType.class);
154             @SuppressWarnings("rawtypes")
155             QuantityType quantitySubtract = stateSubtract.as(QuantityType.class);
156
157             if (quantityValue != null && quantitySubtract != null) {
158                 return quantityValue.subtract(quantitySubtract);
159             }
160         }
161
162         return null;
163     }
164 }