]> git.basschouten.com Git - openhab-addons.git/blob
2ebf6b4e4a3ef1cf73567ae15617f8d7bfd29cd5
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.smartmeter.internal.conformity;
14
15 import java.util.function.Supplier;
16
17 import javax.measure.Quantity;
18
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.smartmeter.SmartMeterBindingConstants;
22 import org.openhab.binding.smartmeter.internal.MeterDevice;
23 import org.openhab.binding.smartmeter.internal.MeterValue;
24 import org.openhab.binding.smartmeter.internal.ObisCode;
25 import org.openhab.binding.smartmeter.internal.conformity.negate.NegateHandler;
26 import org.openhab.core.library.types.QuantityType;
27 import org.openhab.core.library.unit.Units;
28 import org.openhab.core.thing.Channel;
29 import org.openhab.core.thing.Thing;
30 import org.openhab.core.types.State;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 /**
35  * Some meters have specific semantics on how to interpret the values which are sent from the meter.
36  * This class handles all such known special cases.
37  *
38  * @author Matthias Steigenberger - Initial contribution
39  *
40  */
41 @NonNullByDefault
42 public enum Conformity {
43
44     NONE {
45         @Override
46         public <Q extends Quantity<Q>> State apply(Channel channel, QuantityType<Q> currentState, Thing thing,
47                 MeterDevice<?> device) {
48             return retrieveOverwrittenNegate(channel, currentState, thing, device, null);
49         }
50     },
51     /**
52      * See
53      * https://www.vde.com/resource/blob/951000/252eb3cdf1c7f6cdea10847be399da0d/fnn-lastenheft-edl-1-0-2010-01-13-data.pdf
54      */
55     EDL_FNN {
56         /*
57          * (non-Javadoc)
58          *
59          * @see org.openhab.binding.smartmeter.internal.Conformity#apply(org.openhab.core.thing.Channel,
60          * org.openhab.core.library.types.QuantityType, org.openhab.core.thing.Thing,
61          * org.openhab.binding.smartmeter.internal.MeterDevice)
62          */
63         @Override
64         public <Q extends Quantity<Q>> QuantityType<?> apply(Channel channel, QuantityType<Q> currentState, Thing thing,
65                 MeterDevice<?> device) {
66             return retrieveOverwrittenNegate(channel, currentState, thing, device, () -> {
67                 // Negate if this channel has the unit "Watt" and the negate bit is set. Read from all other
68                 // channels the state and check if there is a negate bit.
69                 String channelObis = channel.getProperties().get(SmartMeterBindingConstants.CHANNEL_PROPERTY_OBIS);
70                 if (channelObis != null) {
71                     MeterValue<?> value = device.getMeterValue(channelObis);
72                     if (value != null && Units.WATT.isCompatible(value.getUnit())) {
73                         for (String obis : device.getObisCodes()) {
74                             try {
75                                 MeterValue<?> otherValue = device.getMeterValue(obis);
76                                 ObisCode obisCode = ObisCode.from(obis);
77                                 if (otherValue != null) {
78                                     if (obisCode.matches((byte) 0x60, (byte) 0x05, (byte) 0x05)) {
79                                         // we found status status obis 96.5.5
80                                         if (NegateHandler.isNegateSet(otherValue.getValue(), 5)) {
81                                             return currentState.negate();
82                                         }
83                                     } else if (obisCode.matches((byte) 0x01, (byte) 0x08, (byte) 0x00)) {
84                                         // check obis 1.8.0 for status if status has negate bit set.
85                                         String status = otherValue.getStatus();
86                                         if (status != null && NegateHandler.isNegateSet(status, 5)) {
87                                             return currentState.negate();
88                                         }
89                                     }
90                                 }
91                             } catch (Exception e) {
92                                 logger.warn("Failed to check negate status for obis {}", obis, e);
93                             }
94                         }
95                     }
96                 }
97                 return currentState;
98             });
99         }
100     };
101
102     private static final Logger logger = LoggerFactory.getLogger(Conformity.class);
103
104     /**
105      * Applies the overwritten negation setting for the channel.
106      *
107      * @param currentState The current value.
108      * @param thing The {@link Thing}
109      * @param device The {@link MeterDevice}.
110      * @param negateProperty The negate property.
111      * @return The negated value.
112      */
113     private static <Q extends Quantity<Q>> QuantityType<Q> applyNegation(QuantityType<Q> currentState, Thing thing,
114             MeterDevice<?> device, String negateProperty) {
115         boolean shouldNegateState = NegateHandler.shouldNegateState(negateProperty, channelId -> {
116             Channel negateChannel = thing.getChannel(channelId);
117             if (negateChannel != null) {
118                 String property = negateChannel.getProperties().get(SmartMeterBindingConstants.CHANNEL_PROPERTY_OBIS);
119                 return property != null ? device.getMeterValue(property) : null;
120             }
121             return null;
122         });
123
124         if (shouldNegateState) {
125             return currentState.negate();
126         }
127         return currentState;
128     }
129
130     /**
131      *
132      * @param channel
133      * @param currentState
134      * @param thing
135      * @param device
136      * @param elseDo If negate property was not overwritten call the given supplier.
137      * @return
138      */
139     protected <Q extends Quantity<Q>> QuantityType<?> retrieveOverwrittenNegate(Channel channel,
140             QuantityType<Q> currentState, Thing thing, MeterDevice<?> device,
141             @Nullable Supplier<QuantityType<Q>> elseDo) {
142         // Negate setting
143         String negateProperty = (String) channel.getConfiguration()
144                 .get(SmartMeterBindingConstants.CONFIGURATION_CHANNEL_NEGATE);
145         if (negateProperty != null && !negateProperty.trim().isEmpty()) {
146             return applyNegation(currentState, thing, device, negateProperty);
147         } else {
148             if (elseDo != null) {
149                 return elseDo.get();
150             }
151             return currentState;
152         }
153     }
154
155     /**
156      * Applies any changes according to the conformity and returns the new value.
157      *
158      * @param channel The {@link Channel} for which the conformity should be applied to.
159      * @param currentState The current state of that {@link Channel}
160      * @param thing The {@link Thing} where the channel belongs to.
161      * @param device The {@link MeterDevice} for the Thing.
162      * @return The applied state.
163      */
164     public abstract <Q extends Quantity<Q>> State apply(Channel channel, QuantityType<Q> currentState, Thing thing,
165             MeterDevice<?> device);
166 }