]> git.basschouten.com Git - openhab-addons.git/blob
88d77ea8e96e3cc07f4d2e366944989149814735
[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.dsmr.internal.device.cosem;
14
15 import java.util.AbstractMap.SimpleEntry;
16 import java.util.Arrays;
17 import java.util.List;
18 import java.util.Map.Entry;
19
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.core.library.unit.Units;
23
24 /**
25  * Enumeration Cosem Object types
26  * <p>
27  * Each Cosem Object type consists of the following attributes:
28  * <p>
29  * <ul>
30  * <li>OBIS Identifier (reduced form)
31  * <li>List of value descriptors (See {@link CosemValueDescriptor})
32  * <li>List of repeating value descriptors (See {@link CosemValueDescriptor}). Repeating value descriptors will always
33  * be the last descriptors.
34  * </ul>
35  *
36  * @author M. Volaart - Initial contribution
37  * @author Hilbrand Bouwkamp - Cosem subclasses made into factory classes and introduced quantity type
38  */
39 @NonNullByDefault
40 public enum CosemObjectType {
41     UNKNOWN(new OBISIdentifier(-1, -1, -1, -1), CosemString.INSTANCE),
42
43     /* General messages */
44     P1_VERSION_OUTPUT(new OBISIdentifier(1, 0, 2, 8), CosemString.INSTANCE),
45     P1_EMUCS_VERSION_OUTPUT(new OBISIdentifier(0, 96, 1, 4), CosemString.INSTANCE),
46     P1_TIMESTAMP(new OBISIdentifier(0, 1, 0, 0), new CosemDate("")),
47     P1_TEXT_CODE(new OBISIdentifier(0, 96, 13, 1), CosemHexString.INSTANCE),
48     P1_TEXT_STRING(new OBISIdentifier(0, 96, 13, 0), CosemHexString.INSTANCE),
49     P1_TEXT_STRING_LONG(new OBISIdentifier(0, 96, 13, null), CosemHexString.INSTANCE),
50
51     /* Generic Meter Cosem Object types */
52     METER_EQUIPMENT_IDENTIFIER(new OBISIdentifier(0, 96, 1, 0), CosemHexString.INSTANCE),
53     METER_DEVICE_TYPE(new OBISIdentifier(0, 24, 1, 0), CosemString.INSTANCE),
54     METER_VALVE_SWITCH_POSITION(new OBISIdentifier(0, 24, 4, 0), CosemDecimal.INSTANCE),
55
56     /* Electricity Meter */
57     EMETER_EQUIPMENT_IDENTIFIER_V2_X(new OBISIdentifier(0, 42, 0, 0), CosemHexString.INSTANCE),
58     EMETER_EQUIPMENT_IDENTIFIER(new OBISIdentifier(0, 96, 1, 1), CosemHexString.INSTANCE),
59     EMETER_VALUE(new OBISIdentifier(0, 24, 2, 1, true), CosemDate.INSTANCE, CosemQuantity.KILO_WATT_HOUR),
60     EMETER_DELIVERY_TARIFF0(new OBISIdentifier(1, 1, 8, 0), CosemQuantity.KILO_WATT_HOUR),
61     EMETER_DELIVERY_TARIFF1(new OBISIdentifier(1, 1, 8, 1), CosemQuantity.KILO_WATT_HOUR),
62     EMETER_DELIVERY_TARIFF2(new OBISIdentifier(1, 1, 8, 2), CosemQuantity.KILO_WATT_HOUR),
63     EMETER_DELIVERY_TARIFF0_ANTIFRAUD(new OBISIdentifier(1, 15, 8, 0), CosemQuantity.KILO_WATT_HOUR),
64     EMETER_DELIVERY_TARIFF1_ANTIFRAUD(new OBISIdentifier(1, 15, 8, 1), CosemQuantity.KILO_WATT_HOUR),
65     EMETER_DELIVERY_TARIFF2_ANTIFRAUD(new OBISIdentifier(1, 15, 8, 2), CosemQuantity.KILO_WATT_HOUR),
66     EMETER_PRODUCTION_TARIFF0(new OBISIdentifier(1, 2, 8, 0), CosemQuantity.KILO_WATT_HOUR),
67     EMETER_PRODUCTION_TARIFF1(new OBISIdentifier(1, 2, 8, 1), CosemQuantity.KILO_WATT_HOUR),
68     EMETER_PRODUCTION_TARIFF2(new OBISIdentifier(1, 2, 8, 2), CosemQuantity.KILO_WATT_HOUR),
69     EMETER_TARIFF_INDICATOR(new OBISIdentifier(0, 96, 14, 0), CosemString.INSTANCE),
70     EMETER_ACTIVE_IMPORT_POWER(new OBISIdentifier(1, 15, 7, 0), CosemQuantity.WATT),
71     EMETER_ACTUAL_DELIVERY(new OBISIdentifier(1, 1, 7, 0), CosemQuantity.KILO_WATT),
72     EMETER_ACTUAL_PRODUCTION(new OBISIdentifier(1, 2, 7, 0), CosemQuantity.KILO_WATT),
73     EMETER_TRESHOLD_A_V2_1(new OBISIdentifier(1, 17, 0, 0), CosemQuantity.AMPERE),
74     EMETER_TRESHOLD_A(new OBISIdentifier(0, 17, 0, 0, true), CosemQuantity.AMPERE),
75     EMETER_FUSE_THRESHOLD_A(new OBISIdentifier(1, 31, 4, 0), CosemQuantity.AMPERE),
76     EMETER_TRESHOLD_KW(new OBISIdentifier(0, 17, 0, 0, true), CosemQuantity.KILO_WATT),
77     EMETER_SWITCH_POSITION_V2_1(new OBISIdentifier(1, 96, 3, 10), CosemDecimal.INSTANCE),
78     EMETER_SWITCH_POSITION(new OBISIdentifier(0, 96, 3, 10), CosemDecimal.INSTANCE),
79     EMETER_POWER_FAILURES(new OBISIdentifier(0, 96, 7, 21), CosemDecimal.INSTANCE),
80     EMETER_LONG_POWER_FAILURES(new OBISIdentifier(0, 96, 7, 9), CosemDecimal.INSTANCE),
81     EMETER_POWER_FAILURE_LOG(new OBISIdentifier(1, 99, 97, 0), 2, new CosemDecimal("entries"),
82             new CosemString("obisId"),
83             /* Next 2 descriptors are repeating */
84             CosemDate.INSTANCE, new CosemQuantity<>(Units.SECOND, "duration")),
85     EMETER_VOLTAGE_SAGS_L1(new OBISIdentifier(1, 32, 32, 0), CosemDecimal.INSTANCE),
86     EMETER_VOLTAGE_SAGS_L2(new OBISIdentifier(1, 52, 32, 0), CosemDecimal.INSTANCE),
87     EMETER_VOLTAGE_SAGS_L3(new OBISIdentifier(1, 72, 32, 0), CosemDecimal.INSTANCE),
88     EMETER_VOLTAGE_SWELLS_L1(new OBISIdentifier(1, 32, 36, 0), CosemDecimal.INSTANCE),
89     EMETER_VOLTAGE_SWELLS_L2(new OBISIdentifier(1, 52, 36, 0), CosemDecimal.INSTANCE),
90     EMETER_VOLTAGE_SWELLS_L3(new OBISIdentifier(1, 72, 36, 0), CosemDecimal.INSTANCE),
91     EMETER_INSTANT_CURRENT_L1(new OBISIdentifier(1, 31, 7, 0), CosemQuantity.AMPERE),
92     EMETER_INSTANT_CURRENT_L2(new OBISIdentifier(1, 51, 7, 0), CosemQuantity.AMPERE),
93     EMETER_INSTANT_CURRENT_L3(new OBISIdentifier(1, 71, 7, 0), CosemQuantity.AMPERE),
94     EMETER_INSTANT_POWER_DELIVERY_L1(new OBISIdentifier(1, 21, 7, 0), CosemQuantity.KILO_WATT),
95     EMETER_INSTANT_POWER_DELIVERY_L2(new OBISIdentifier(1, 41, 7, 0), CosemQuantity.KILO_WATT),
96     EMETER_INSTANT_POWER_DELIVERY_L3(new OBISIdentifier(1, 61, 7, 0), CosemQuantity.KILO_WATT),
97     EMETER_INSTANT_POWER_PRODUCTION_L1(new OBISIdentifier(1, 22, 7, 0), CosemQuantity.KILO_WATT),
98     EMETER_INSTANT_POWER_PRODUCTION_L2(new OBISIdentifier(1, 42, 7, 0), CosemQuantity.KILO_WATT),
99     EMETER_INSTANT_POWER_PRODUCTION_L3(new OBISIdentifier(1, 62, 7, 0), CosemQuantity.KILO_WATT),
100     EMETER_INSTANT_VOLTAGE_L1(new OBISIdentifier(1, 32, 7, 0), CosemQuantity.VOLT),
101     EMETER_INSTANT_VOLTAGE_L2(new OBISIdentifier(1, 52, 7, 0), CosemQuantity.VOLT),
102     EMETER_INSTANT_VOLTAGE_L3(new OBISIdentifier(1, 72, 7, 0), CosemQuantity.VOLT),
103
104     /* Gas Meter */
105     GMETER_EQUIPMENT_IDENTIFIER_V2(new OBISIdentifier(7, 0, 0, 0), CosemHexString.INSTANCE),
106     GMETER_24H_DELIVERY_V2(new OBISIdentifier(7, 23, 1, 0), CosemQuantity.CUBIC_METRE, CosemDate.INSTANCE),
107     GMETER_24H_DELIVERY_COMPENSATED_V2(new OBISIdentifier(7, 23, 2, 0), CosemQuantity.CUBIC_METRE, CosemDate.INSTANCE),
108     GMETER_LAST_VALUE(new OBISIdentifier(0, 24, 2, 3), CosemDate.INSTANCE, CosemQuantity.CUBIC_METRE),
109     GMETER_VALUE_V3(new OBISIdentifier(0, 24, 3, 0, true), CosemDate.INSTANCE, // Time stamp off the reading
110             new CosemString("val1"), // Specification is not clear what this value is
111             new CosemDecimal("val2"), // Specification is not clear what this value is
112             new CosemDecimal("val3"), // Specification is not clear what this value is
113             new CosemString("obisId"), // String containing an OBIS Identifier
114             new CosemString("unit"), // String containing the type (m3)
115             CosemDecimal.INSTANCE),
116     GMETER_VALVE_POSITION_V2_1(new OBISIdentifier(7, 96, 3, 10), CosemDecimal.INSTANCE),
117     GMETER_VALVE_POSITION_V2_2(new OBISIdentifier(7, 24, 4, 0), CosemDecimal.INSTANCE),
118
119     /* Heating Meter */
120     HMETER_EQUIPMENT_IDENTIFIER_V2_2(new OBISIdentifier(5, 0, 0, 0), CosemHexString.INSTANCE),
121     HMETER_VALUE_V2(new OBISIdentifier(5, 1, 0, 0), CosemQuantity.GIGA_JOULE, CosemDate.INSTANCE),
122
123     /* Cooling Meter */
124     CMETER_EQUIPMENT_IDENTIFIER_V2_2(new OBISIdentifier(6, 0, 0, 0), CosemHexString.INSTANCE),
125     CMETER_VALUE_V2(new OBISIdentifier(6, 1, 0, 0), CosemQuantity.GIGA_JOULE, CosemDate.INSTANCE),
126
127     /* Water Meter */
128     WMETER_EQUIPMENT_IDENTIFIER_V2_2(new OBISIdentifier(8, 0, 0, 0), CosemHexString.INSTANCE),
129     WMETER_VALUE_V2(new OBISIdentifier(8, 1, 0, 0), CosemQuantity.CUBIC_METRE, CosemDate.INSTANCE),
130     WMETER_VALUE_V3(new OBISIdentifier(0, 24, 3, 0, true), CosemQuantity.CUBIC_METRE),
131
132     /* M3 Meter (Gas, Water) */
133     M3METER_VALUE(new OBISIdentifier(0, 24, 2, 1, true), CosemDate.INSTANCE, CosemQuantity.CUBIC_METRE),
134
135     /* GJ Meter (Heating, Cooling) */
136     GJMETER_VALUE_V3(new OBISIdentifier(0, 24, 3, 0, true), CosemQuantity.GIGA_JOULE),
137     GJMETER_VALUE_V4(new OBISIdentifier(0, 24, 2, 1, true), CosemDate.INSTANCE, CosemQuantity.GIGA_JOULE),
138
139     /* Generic Meter (DSMR v3 only) */
140     GENMETER_VALUE_V3(new OBISIdentifier(0, 24, 3, 0, true), CosemDecimal.INSTANCE),
141
142     /* Additional Luxembourgish Smarty Electricity */
143     EMETER_TOTAL_IMPORTED_ENERGY_REGISTER_Q(new OBISIdentifier(1, 3, 8, 0), CosemQuantity.KILO_VAR_HOUR),
144     EMETER_TOTAL_IMPORTED_ENERGY_REGISTER_R_RATE1(new OBISIdentifier(1, 3, 8, 1), CosemQuantity.KILO_VAR_HOUR),
145     EMETER_TOTAL_IMPORTED_ENERGY_REGISTER_R_RATE2(new OBISIdentifier(1, 3, 8, 2), CosemQuantity.KILO_VAR_HOUR),
146     EMETER_TOTAL_EXPORTED_ENERGY_REGISTER_Q(new OBISIdentifier(1, 4, 8, 0), CosemQuantity.KILO_VAR_HOUR),
147     EMETER_TOTAL_EXPORTED_ENERGY_REGISTER_R_RATE1(new OBISIdentifier(1, 4, 8, 1), CosemQuantity.KILO_VAR_HOUR),
148     EMETER_TOTAL_EXPORTED_ENERGY_REGISTER_R_RATE2(new OBISIdentifier(1, 4, 8, 2), CosemQuantity.KILO_VAR_HOUR),
149     // The actual reactive's and threshold have no unit in the data and therefore are not quantity types.
150     EMETER_ACTUAL_REACTIVE_DELIVERY(new OBISIdentifier(1, 3, 7, 0), CosemDecimal.INSTANCE_WITH_UNITS),
151     EMETER_ACTUAL_REACTIVE_PRODUCTION(new OBISIdentifier(1, 4, 7, 0), CosemDecimal.INSTANCE_WITH_UNITS),
152     EMETER_ACTIVE_THRESHOLD_SMAX(new OBISIdentifier(0, 17, 0, 0, true), CosemDecimal.INSTANCE_WITH_UNITS),
153     EMETER_INSTANT_REACTIVE_POWER_DELIVERY_L1(new OBISIdentifier(1, 23, 7, 0), CosemQuantity.KILO_VAR),
154     EMETER_INSTANT_REACTIVE_POWER_DELIVERY_L2(new OBISIdentifier(1, 43, 7, 0), CosemQuantity.KILO_VAR),
155     EMETER_INSTANT_REACTIVE_POWER_DELIVERY_L3(new OBISIdentifier(1, 63, 7, 0), CosemQuantity.KILO_VAR),
156     EMETER_INSTANT_REACTIVE_POWER_PRODUCTION_L1(new OBISIdentifier(1, 24, 7, 0), CosemQuantity.KILO_VAR),
157     EMETER_INSTANT_REACTIVE_POWER_PRODUCTION_L2(new OBISIdentifier(1, 44, 7, 0), CosemQuantity.KILO_VAR),
158     EMETER_INSTANT_REACTIVE_POWER_PRODUCTION_L3(new OBISIdentifier(1, 64, 7, 0), CosemQuantity.KILO_VAR);
159
160     /** OBIS reduced identifier */
161     public final OBISIdentifier obisId;
162
163     /** COSEM value descriptors */
164     private final List<CosemValueDescriptor<?>> descriptors;
165     private final List<CosemValueDescriptor<?>> repeatingDescriptors;
166
167     /**
168      * Constructs a new CosemObjectType
169      *
170      * @param obisId {@link OBISIdentifier} containing the obisIdentifier for CosemObjectType
171      * @param descriptors variable parameter list of {@link CosemValueDescriptor}
172      */
173     CosemObjectType(OBISIdentifier obisId, CosemValueDescriptor<?>... descriptors) {
174         this(obisId, 0, descriptors);
175     }
176
177     /**
178      * Constructs a new CosemObjectType
179      *
180      * @param obisId {@link OBISIdentifier} containing the obisIdentifier for CosemObjectType
181      * @param nrOfRepeatingDescriptors nr of repeating descriptors (this are the last n descriptors in the variable list
182      *            descriptors)
183      * @param descriptors variable parameter list of {@link CosemValueDescriptor}
184      */
185     CosemObjectType(OBISIdentifier obisId, int nrOfRepeatingDescriptors, CosemValueDescriptor<?>... descriptors) {
186         this.obisId = obisId;
187         if (nrOfRepeatingDescriptors == 0) {
188             this.descriptors = Arrays.asList(descriptors);
189             this.repeatingDescriptors = List.of();
190         } else {
191             final List<CosemValueDescriptor<?>> allDescriptors = List.of(descriptors);
192
193             /*
194              * The last nrOfRepeatingDescriptors CosemValueDescriptor will go into the repeatingDescriptor list.
195              * The other descriptors will got into the regular list
196              */
197             this.descriptors = allDescriptors.subList(0, allDescriptors.size() - nrOfRepeatingDescriptors);
198             this.repeatingDescriptors = allDescriptors.subList(this.descriptors.size(),
199                     this.descriptors.size() + nrOfRepeatingDescriptors);
200         }
201     }
202
203     /**
204      * Returns the {@link CosemValueDescriptor} for the specified index.
205      * If the list contains repeating descriptors the specified index will mapped onto the repeating list
206      *
207      * e.g. If the list contains 4 descriptors and the last 2 are repeating, idx=6 will return the 4th descriptor.
208      *
209      * The idx {@code is < 0} or outside a non-repeating descriptorslist size null is returned
210      *
211      * @param idx the CosemValueDescriptor to return
212      * @return the CosemValueDescriptor or null if not found.
213      */
214     public @Nullable Entry<String, CosemValueDescriptor<?>> getDescriptor(int idx) {
215         if (idx >= descriptors.size() && !repeatingDescriptors.isEmpty()) {
216             /* We have a repeating list, find the correct repeating descriptor */
217             final int repeatingIdx = (idx - descriptors.size()) % repeatingDescriptors.size();
218
219             final CosemValueDescriptor<?> descriptor = repeatingDescriptors.get(repeatingIdx);
220
221             /* The repeating descriptor must have a specific channel */
222             final int repeatCount = (idx - descriptors.size()) / repeatingDescriptors.size();
223
224             return new SimpleEntry<>(descriptor.getChannelId() + repeatCount, descriptor);
225         } else if (idx < descriptors.size()) {
226             final CosemValueDescriptor<?> descriptor = descriptors.get(idx);
227
228             return new SimpleEntry<>(descriptor.getChannelId(), descriptor);
229         } else {
230             return null;
231         }
232     }
233
234     /**
235      * Returns if this CosemObjectType supports the requested number of values.
236      *
237      * Note that for repeating list the number of values must match the repeating pattern.
238      *
239      * So if the list contains 4 values and the last 2 are repeating, nrOfValues = 6 will return true,
240      * however nrOfvalues = 7 will return false (only 4, 6, 8, etc... is allowed)
241      *
242      * @param nrOfValues number of values to check.
243      *
244      * @return true if this CosemObjectType support the requested number of values, false otherwise.
245      */
246     public boolean supportsNrOfValues(int nrOfValues) {
247         if (repeatingDescriptors.isEmpty()) {
248             return nrOfValues == descriptors.size();
249         } else {
250             /* There are repeating descriptors */
251             return ((nrOfValues - descriptors.size()) % repeatingDescriptors.size()) == 0;
252         }
253     }
254 }