2 * Copyright (c) 2010-2023 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.dsmr.internal.device.cosem;
15 import java.text.ParseException;
16 import java.util.regex.Matcher;
17 import java.util.regex.Pattern;
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;
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;
35 * {@link CosemQuantity} represents a value with a unit.
37 * @author Hilbrand Bouwkamp - Initial contribution
39 * @param <Q> The {@link Quantity} type of the unit of this class
42 class CosemQuantity<Q extends @Nullable Quantity<Q>> extends CosemValueDescriptor<QuantityType<Q>> {
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));
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.
60 * On the Kampstrup 162JxC in some CosemValues the separator is missing
62 * The above quirks are supported
64 * We also support unit that do not follow the exact case.
66 private static final Pattern COSEM_VALUE_WITH_UNIT_PATTERN = Pattern.compile("^([\\d\\.]+)[\\*_]?(.+)$",
67 Pattern.CASE_INSENSITIVE);
70 * Unit of this CosemValue
72 private final Unit<Q> unit;
75 * Creates a new {@link CosemDouble}.
77 * @param unit the unit of the value
79 private CosemQuantity(Unit<Q> unit) {
86 * @param unit Unit of this CosemQuantity instance
87 * @param channelId the channel for this CosemValueDescriptor
89 public CosemQuantity(Unit<Q> unit, String channelId) {
95 * Parses a String value (that represents a value with a unit) to a {@link QuantityType} object.
97 * @param cosemValue the value to parse
98 * @return {@link QuanitytType} on success
99 * @throws ParseException in case unit doesn't match.
102 protected QuantityType<Q> getStateValue(String cosemValue) throws ParseException {
104 final QuantityType<Q> it = new QuantityType<>(prepare(cosemValue));
105 final @Nullable QuantityType<Q> qt = it.toUnit(unit);
108 throw new ParseException("Failed to parse value '" + cosemValue + "' as unit " + unit, 0);
111 } catch (final IllegalArgumentException nfe) {
112 throw new ParseException("Failed to parse value '" + cosemValue + "' as unit " + unit, 0);
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.
121 * On the Kampstrup 162JxC in some CosemValues the separator is missing. This
123 * The above quirks are supported
125 * We also support unit that do not follow the exact case.
127 private String prepare(String cosemValue) {
128 final Matcher matcher = COSEM_VALUE_WITH_UNIT_PATTERN.matcher(cosemValue.replace("m3", "m³"));
129 if (!matcher.find()) {
134 Integer.parseInt(matcher.group(2));
136 } catch (final NumberFormatException e) {
137 return matcher.group(1) + ' ' + matcher.group(2);