]> git.basschouten.com Git - openhab-addons.git/blob
0807bbe013a67db8c7acdf1dc6209dd69cc710b7
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.dsmr.internal.device.cosem;
14
15 import java.text.ParseException;
16 import java.util.regex.Matcher;
17 import java.util.regex.Pattern;
18
19 import javax.measure.Quantity;
20 import javax.measure.Unit;
21 import javax.measure.quantity.ElectricCurrent;
22 import javax.measure.quantity.ElectricPotential;
23 import javax.measure.quantity.Energy;
24 import javax.measure.quantity.Power;
25 import javax.measure.quantity.Volume;
26
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.openhab.core.library.types.QuantityType;
30 import org.openhab.core.library.unit.MetricPrefix;
31 import org.openhab.core.library.unit.SIUnits;
32 import org.openhab.core.library.unit.Units;
33
34 /**
35  * {@link CosemQuantity} represents a value with a unit.
36  *
37  * @author Hilbrand Bouwkamp - Initial contribution
38  *
39  * @param <Q> The {@link Quantity} type of the unit of this class
40  */
41 @NonNullByDefault
42 class CosemQuantity<Q extends @Nullable Quantity<Q>> extends CosemValueDescriptor<QuantityType<Q>> {
43
44     public static final CosemQuantity<ElectricCurrent> AMPERE = new CosemQuantity<>(Units.AMPERE);
45     public static final CosemQuantity<Volume> CUBIC_METRE = new CosemQuantity<>(SIUnits.CUBIC_METRE);
46     public static final CosemQuantity<Energy> GIGA_JOULE = new CosemQuantity<>(MetricPrefix.GIGA(Units.JOULE));
47     public static final CosemQuantity<Power> KILO_WATT = new CosemQuantity<>(MetricPrefix.KILO(Units.WATT));
48     public static final CosemQuantity<Energy> KILO_WATT_HOUR = new CosemQuantity<>(Units.KILOWATT_HOUR);
49     public static final CosemQuantity<ElectricPotential> VOLT = new CosemQuantity<>(Units.VOLT);
50     public static final CosemQuantity<Power> WATT = new CosemQuantity<>(Units.WATT);
51     public static final CosemQuantity<Power> KILO_VAR = new CosemQuantity<>(Units.KILOVAR);
52     public static final CosemQuantity<Energy> KILO_VAR_HOUR = new CosemQuantity<>(Units.KILOVAR_HOUR);
53     public static final CosemQuantity<Power> KILO_VA = new CosemQuantity<>(MetricPrefix.KILO(Units.VOLT_AMPERE));
54
55     /**
56      * Pattern to convert a cosem value to a value that can be parsed by {@link QuantityType}.
57      * The specification states that the delimiter between the value and the unit is a '*'-character.
58      * We have seen on the Kaifa 0025 meter that both '*' and the '_' character are used.
59      *
60      * On the Kampstrup 162JxC in some CosemValues the separator is missing
61      *
62      * The above quirks are supported
63      *
64      * We also support unit that do not follow the exact case.
65      */
66     private static final Pattern COSEM_VALUE_WITH_UNIT_PATTERN = Pattern.compile("^([\\d\\.]+)[\\*_]?(.+)$",
67             Pattern.CASE_INSENSITIVE);
68
69     /**
70      * Unit of this CosemValue
71      */
72     private final Unit<Q> unit;
73
74     /**
75      * Creates a new {@link CosemDouble}.
76      *
77      * @param unit the unit of the value
78      */
79     private CosemQuantity(Unit<Q> unit) {
80         this(unit, "");
81     }
82
83     /**
84      * Constructor.
85      *
86      * @param unit Unit of this CosemQuantity instance
87      * @param channelId the channel for this CosemValueDescriptor
88      */
89     public CosemQuantity(Unit<Q> unit, String channelId) {
90         super(channelId);
91         this.unit = unit;
92     }
93
94     /**
95      * Parses a String value (that represents a value with a unit) to a {@link QuantityType} object.
96      *
97      * @param cosemValue the value to parse
98      * @return {@link QuanitytType} on success
99      * @throws ParseException in case unit doesn't match.
100      */
101     @Override
102     protected QuantityType<Q> getStateValue(String cosemValue) throws ParseException {
103         try {
104             final QuantityType<Q> it = new QuantityType<>(prepare(cosemValue));
105             final @Nullable QuantityType<Q> qt = it.toUnit(unit);
106
107             if (qt == null) {
108                 throw new ParseException("Failed to parse value '" + cosemValue + "' as unit " + unit, 0);
109             }
110             return qt;
111         } catch (final IllegalArgumentException nfe) {
112             throw new ParseException("Failed to parse value '" + cosemValue + "' as unit " + unit, 0);
113         }
114     }
115
116     /**
117      * Check if COSEM value has a unit, check and parse the value. We assume here numbers (float or integers)
118      * The specification states that the delimiter between the value and the unit is a '*'-character.
119      * We have seen on the Kaifa 0025 meter that both '*' and the '_' character are used.
120      *
121      * On the Kampstrup 162JxC in some CosemValues the separator is missing. This
122      *
123      * The above quirks are supported
124      *
125      * We also support unit that do not follow the exact case.
126      */
127     private String prepare(String cosemValue) {
128         final Matcher matcher = COSEM_VALUE_WITH_UNIT_PATTERN.matcher(cosemValue.replace("m3", "m³"));
129         if (!matcher.find()) {
130             return cosemValue;
131         }
132
133         try {
134             Integer.parseInt(matcher.group(2));
135             return cosemValue;
136         } catch (final NumberFormatException e) {
137             return matcher.group(1) + ' ' + matcher.group(2);
138         }
139     }
140 }