2 * Copyright (c) 2010-2020 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.smartmeter.internal.conformity;
15 import java.util.function.Supplier;
17 import javax.measure.Quantity;
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.SmartHomeUnits;
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;
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.
38 * @author Matthias Steigenberger - Initial contribution
42 public enum Conformity {
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);
53 * https://www.vde.com/resource/blob/951000/252eb3cdf1c7f6cdea10847be399da0d/fnn-lastenheft-edl-1-0-2010-01-13-data.pdf
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)
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 MeterValue<?> value = device.getMeterValue(channelObis);
71 if (value != null && SmartHomeUnits.WATT.isCompatible(value.getUnit())) {
72 for (String obis : device.getObisCodes()) {
74 MeterValue<?> otherValue = device.getMeterValue(obis);
75 ObisCode obisCode = ObisCode.from(obis);
76 if (otherValue != null) {
77 if (obisCode.matches((byte) 0x60, (byte) 0x05, (byte) 0x05)) {
78 // we found status status obis 96.5.5
79 if (NegateHandler.isNegateSet(otherValue.getValue(), 5)) {
80 return currentState.negate();
82 } else if (obisCode.matches((byte) 0x01, (byte) 0x08, (byte) 0x00)) {
83 // check obis 1.8.0 for status if status has negate bit set.
84 String status = otherValue.getStatus();
85 if (status != null && NegateHandler.isNegateSet(status, 5)) {
86 return currentState.negate();
90 } catch (Exception e) {
91 logger.warn("Failed to check negate status for obis {}", obis, e);
100 private static final Logger logger = LoggerFactory.getLogger(Conformity.class);
103 * Applies the overwritten negation setting for the channel.
105 * @param currentState The current value.
106 * @param thing The {@link Thing}
107 * @param device The {@link MeterDevice}.
108 * @param negateProperty The negate property.
109 * @return The negated value.
111 private static <Q extends Quantity<Q>> QuantityType<Q> applyNegation(QuantityType<Q> currentState, Thing thing,
112 MeterDevice<?> device, String negateProperty) {
113 boolean shouldNegateState = NegateHandler.shouldNegateState(negateProperty, channelId -> {
114 Channel negateChannel = thing.getChannel(channelId);
115 if (negateChannel != null) {
116 String property = negateChannel.getProperties().get(SmartMeterBindingConstants.CHANNEL_PROPERTY_OBIS);
117 return property != null ? device.getMeterValue(property) : null;
122 if (shouldNegateState) {
123 return currentState.negate();
131 * @param currentState
134 * @param elseDo If negate property was not overwritten call the given supplier.
137 protected <Q extends Quantity<Q>> QuantityType<?> retrieveOverwrittenNegate(Channel channel,
138 QuantityType<Q> currentState, Thing thing, MeterDevice<?> device,
139 @Nullable Supplier<QuantityType<Q>> elseDo) {
141 String negateProperty = (String) channel.getConfiguration()
142 .get(SmartMeterBindingConstants.CONFIGURATION_CHANNEL_NEGATE);
143 if (negateProperty != null && !negateProperty.trim().isEmpty()) {
144 return applyNegation(currentState, thing, device, negateProperty);
146 if (elseDo != null) {
154 * Applies any changes according to the conformity and returns the new value.
156 * @param channel The {@link Channel} for which the conformity should be applied to.
157 * @param currentState The current state of that {@link Channel}
158 * @param thing The {@link Thing} where the channel belongs to.
159 * @param device The {@link MeterDevice} for the Thing.
160 * @return The applied state.
162 public abstract <Q extends Quantity<Q>> State apply(Channel channel, QuantityType<Q> currentState, Thing thing,
163 MeterDevice<?> device);