2 * Copyright (c) 2010-2024 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.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;
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 if (channelObis != null) {
71 MeterValue<?> value = device.getMeterValue(channelObis);
72 if (value != null && Units.WATT.isCompatible(value.getUnit())) {
73 for (String obis : device.getObisCodes()) {
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();
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();
91 } catch (Exception e) {
92 logger.warn("Failed to check negate status for obis {}", obis, e);
102 private static final Logger logger = LoggerFactory.getLogger(Conformity.class);
105 * Applies the overwritten negation setting for the channel.
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.
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;
124 if (shouldNegateState) {
125 return currentState.negate();
133 * @param currentState
136 * @param elseDo If negate property was not overwritten call the given supplier.
139 protected <Q extends Quantity<Q>> QuantityType<?> retrieveOverwrittenNegate(Channel channel,
140 QuantityType<Q> currentState, Thing thing, MeterDevice<?> device,
141 @Nullable Supplier<QuantityType<Q>> elseDo) {
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);
148 if (elseDo != null) {
156 * Applies any changes according to the conformity and returns the new value.
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.
164 public abstract <Q extends Quantity<Q>> State apply(Channel channel, QuantityType<Q> currentState, Thing thing,
165 MeterDevice<?> device);