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.knx.internal.dpt;
15 import java.math.BigDecimal;
16 import java.math.RoundingMode;
17 import java.text.ParseException;
18 import java.text.SimpleDateFormat;
19 import java.util.Arrays;
20 import java.util.Calendar;
21 import java.util.Date;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Locale;
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.openhab.binding.knx.internal.KNXTypeMapper;
30 import org.openhab.core.library.types.DateTimeType;
31 import org.openhab.core.library.types.DecimalType;
32 import org.openhab.core.library.types.HSBType;
33 import org.openhab.core.library.types.IncreaseDecreaseType;
34 import org.openhab.core.library.types.OnOffType;
35 import org.openhab.core.library.types.OpenClosedType;
36 import org.openhab.core.library.types.PercentType;
37 import org.openhab.core.library.types.QuantityType;
38 import org.openhab.core.library.types.StopMoveType;
39 import org.openhab.core.library.types.StringType;
40 import org.openhab.core.library.types.UpDownType;
41 import org.openhab.core.types.Type;
42 import org.openhab.core.types.UnDefType;
43 import org.osgi.service.component.annotations.Component;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
47 import tuwien.auto.calimero.KNXException;
48 import tuwien.auto.calimero.KNXFormatException;
49 import tuwien.auto.calimero.KNXIllegalArgumentException;
50 import tuwien.auto.calimero.datapoint.Datapoint;
51 import tuwien.auto.calimero.dptxlator.DPT;
52 import tuwien.auto.calimero.dptxlator.DPTXlator;
53 import tuwien.auto.calimero.dptxlator.DPTXlator1BitControlled;
54 import tuwien.auto.calimero.dptxlator.DPTXlator2ByteFloat;
55 import tuwien.auto.calimero.dptxlator.DPTXlator2ByteUnsigned;
56 import tuwien.auto.calimero.dptxlator.DPTXlator3BitControlled;
57 import tuwien.auto.calimero.dptxlator.DPTXlator4ByteFloat;
58 import tuwien.auto.calimero.dptxlator.DPTXlator4ByteSigned;
59 import tuwien.auto.calimero.dptxlator.DPTXlator4ByteUnsigned;
60 import tuwien.auto.calimero.dptxlator.DPTXlator64BitSigned;
61 import tuwien.auto.calimero.dptxlator.DPTXlator8BitSigned;
62 import tuwien.auto.calimero.dptxlator.DPTXlator8BitUnsigned;
63 import tuwien.auto.calimero.dptxlator.DPTXlatorBoolean;
64 import tuwien.auto.calimero.dptxlator.DPTXlatorDate;
65 import tuwien.auto.calimero.dptxlator.DPTXlatorDateTime;
66 import tuwien.auto.calimero.dptxlator.DPTXlatorRGB;
67 import tuwien.auto.calimero.dptxlator.DPTXlatorSceneControl;
68 import tuwien.auto.calimero.dptxlator.DPTXlatorSceneNumber;
69 import tuwien.auto.calimero.dptxlator.DPTXlatorString;
70 import tuwien.auto.calimero.dptxlator.DPTXlatorTime;
71 import tuwien.auto.calimero.dptxlator.DPTXlatorUtf8;
72 import tuwien.auto.calimero.dptxlator.TranslatorTypes;
75 * This class provides type mapping between all openHAB core types and KNX data point types.
77 * Each 'MainType' delivered from calimero, has a default mapping
78 * for all it's children to an openHAB Typeclass.
79 * All these 'MainType' mapping's are put into 'dptMainTypeMap'.
81 * Default 'MainType' mapping's we can override by a specific mapping.
82 * All specific mapping's are put into 'dptTypeMap'.
84 * If for a 'MainType' there is currently no specific mapping registered,
85 * you can find a commented example line, with it's correct 'DPTXlator' class.
87 * @author Kai Kreuzer - initial contribution
88 * @author Volker Daube - improvements
89 * @author Jan N. Klug - improvements
90 * @author Helmut Lehmeyer - Java8, generic DPT Mapper
94 public class KNXCoreTypeMapper implements KNXTypeMapper {
96 private final Logger logger = LoggerFactory.getLogger(KNXCoreTypeMapper.class);
98 private static final String TIME_DAY_FORMAT = new String("EEE, HH:mm:ss");
99 private static final String TIME_FORMAT = new String("HH:mm:ss");
100 private static final String DATE_FORMAT = new String("yyyy-MM-dd");
103 * stores the openHAB type class for (supported) KNX datapoint types in a generic way.
104 * dptTypeMap stores more specific type class and exceptions.
106 private final Map<Integer, Class<? extends Type>> dptMainTypeMap;
108 /** stores the openHAB type class for all (supported) KNX datapoint types */
109 private final Map<String, Class<? extends Type>> dptTypeMap;
111 /** stores the default KNX DPT to use for each openHAB type */
112 private final Map<Class<? extends Type>, String> defaultDptMap;
114 public KNXCoreTypeMapper() {
115 @SuppressWarnings("unused")
116 final List<Class<?>> xlators = Arrays.<Class<?>> asList(DPTXlator1BitControlled.class,
117 DPTXlator2ByteFloat.class, DPTXlator2ByteUnsigned.class, DPTXlator3BitControlled.class,
118 DPTXlator4ByteFloat.class, DPTXlator4ByteSigned.class, DPTXlator4ByteUnsigned.class,
119 DPTXlator64BitSigned.class, DPTXlator8BitSigned.class, DPTXlator8BitUnsigned.class,
120 DPTXlatorBoolean.class, DPTXlatorDate.class, DPTXlatorDateTime.class, DPTXlatorRGB.class,
121 DPTXlatorSceneControl.class, DPTXlatorSceneNumber.class, DPTXlatorString.class, DPTXlatorTime.class,
122 DPTXlatorUtf8.class);
124 dptTypeMap = new HashMap<>();
125 dptMainTypeMap = new HashMap<>();
129 * 1.000: General bool
130 * 1.001: DPT_Switch values: 0 = off 1 = on
131 * 1.002: DPT_Bool values: 0 = false 1 = true
132 * 1.003: DPT_Enable values: 0 = disable 1 = enable
133 * 1.004: DPT_Ramp values: 0 = no ramp 1 = ramp
134 * 1.005: DPT_Alarm values: 0 = no alarm 1 = alarm
135 * 1.006: DPT_BinaryValue values: 0 = low 1 = high
136 * 1.007: DPT_Step values: 0 = decrease 1 = increase
137 * 1.008: DPT_UpDown values: 0 = up 1 = down
138 * 1.009: DPT_OpenClose values: 0 = open 1 = close
139 * 1.010: DPT_Start values: 0 = stop 1 = start
140 * 1.011: DPT_State values: 0 = inactive 1 = active
141 * 1.012: DPT_Invert values: 0 = not inverted 1 = inverted
142 * 1.013: DPT_DimSendStyle values: 0 = start/stop 1 = cyclic
143 * 1.014: DPT_InputSource values: 0 = fixed 1 = calculated
144 * 1.015: DPT_Reset values: 0 = no action 1 = reset
145 * 1.016: DPT_Ack values: 0 = no action 1 = acknowledge
146 * 1.017: DPT_Trigger values: 0 = trigger 1 = trigger
147 * 1.018: DPT_Occupancy values: 0 = not occupied 1 = occupied
148 * 1.019: DPT_Window_Door values: 0 = closed 1 = open
149 * 1.021: DPT_LogicalFunction values: 0 = OR 1 = AND
150 * 1.022: DPT_Scene_AB values: 0 = scene A 1 = scene B
151 * 1.023: DPT_ShutterBlinds_Mode values: 0 = only move up/down 1 = move up/down + step-stop
152 * 1.100: DPT_Heat/Cool values: 0 = cooling 1 = heating
154 dptMainTypeMap.put(1, OnOffType.class);
155 /** Exceptions Datapoint Types "B1", Main number 1 */
156 dptTypeMap.put(DPTXlatorBoolean.DPT_UPDOWN.getID(), UpDownType.class);
157 dptTypeMap.put(DPTXlatorBoolean.DPT_OPENCLOSE.getID(), OpenClosedType.class);
158 dptTypeMap.put(DPTXlatorBoolean.DPT_START.getID(), StopMoveType.class);
159 dptTypeMap.put(DPTXlatorBoolean.DPT_WINDOW_DOOR.getID(), OpenClosedType.class);
160 dptTypeMap.put(DPTXlatorBoolean.DPT_SCENE_AB.getID(), DecimalType.class);
164 * 2.001: DPT_Switch_Control values: 0 = off 1 = on
165 * 2.002: DPT_Bool_Control values: 0 = false 1 = true
166 * 2.003: DPT_Enable_Control values: 0 = disable 1 = enable
167 * 2.004: DPT_Ramp_Control values: 0 = no ramp 1 = ramp
168 * 2.005: DPT_Alarm_Control values: 0 = no alarm 1 = alarm
169 * 2.006: DPT_BinaryValue_Control values: 0 = low 1 = high
170 * 2.007: DPT_Step_Control values: 0 = decrease 1 = increase
171 * 2.008: DPT_Direction1_Control values: 0 = up 1 = down
172 * 2.009: DPT_Direction2_Control values: 0 = open 1 = close
173 * 2.010: DPT_Start_Control values: 0 = stop 1 = start
174 * 2.011: DPT_State_Control values: 0 = inactive 1 = active
175 * 2.012: DPT_Invert_Control values: 0 = not inverted 1 = inverted
177 dptMainTypeMap.put(2, DecimalType.class);
178 /** Exceptions Datapoint Types "B2", Main number 2 */
179 // Example: dptTypeMap.put(DPTXlator1BitControlled.DPT_SWITCH_CONTROL.getID(), DecimalType.class);
183 * 3.007: DPT_Control_Dimming values: 0 = decrease 1 = increase
184 * 3.008: DPT_Control_Blinds values: 0 = up 1 = down
186 dptMainTypeMap.put(3, IncreaseDecreaseType.class);
187 /** Exceptions Datapoint Types "B1U3", Main number 3 */
188 dptTypeMap.put(DPTXlator3BitControlled.DPT_CONTROL_BLINDS.getID(), UpDownType.class);
192 * 4.001: DPT_Char_ASCII
193 * 4.002: DPT_Char_8859_1
195 dptMainTypeMap.put(4, StringType.class);
199 * 5.000: General byte
200 * 5.001: DPT_Scaling values: 0...100 %
201 * 5.003: DPT_Angle values: 0...360 °
202 * 5.004: DPT_Percent_U8 (8 Bit) values: 0...255 %
203 * 5.005: DPT_DecimalFactor values: 0...255 ratio
204 * 5.006: DPT_Tariff values: 0...254
205 * 5.010: DPT_Value_1_Ucount Unsigned count values: 0...255 counter pulses
207 dptMainTypeMap.put(5, DecimalType.class);
208 /** Exceptions Types "8-Bit Unsigned Value", Main number 5 */
209 dptTypeMap.put(DPTXlator8BitUnsigned.DPT_SCALING.getID(), PercentType.class);
210 dptTypeMap.put(DPTXlator8BitUnsigned.DPT_PERCENT_U8.getID(), PercentType.class);
214 * 6.001: DPT_Percent_V8 (8 Bit) values: -128...127 %
215 * 6.010: DPT_Value_1_Count values: signed -128...127 counter pulses
216 * 6.020: DPT_Status_Mode3 with mode values: 0/0/0/0/0 0...1/1/1/1/1 2
218 dptMainTypeMap.put(6, DecimalType.class);
219 /** Exceptions Datapoint Types "8-Bit Signed Value", Main number 6 */
220 dptTypeMap.put(DPTXlator8BitSigned.DPT_PERCENT_V8.getID(), PercentType.class);
221 dptTypeMap.put(DPTXlator8BitSigned.DPT_STATUS_MODE3.getID(), StringType.class);
225 * 7.000: General unsigned integer
226 * 7.001: DPT_Value_2_Ucount values: 0...65535 pulses
227 * 7.002: DPT_TimePeriodMsec values: 0...65535 res 1 ms
228 * 7.003: DPT_TimePeriod10MSec values: 0...655350 res 10 ms
229 * 7.004: DPT_TimePeriod100MSec values: 0...6553500 res 100 ms
230 * 7.005: DPT_TimePeriodSec values: 0...65535 s
231 * 7.006: DPT_TimePeriodMin values: 0...65535 min
232 * 7.007: DPT_TimePeriodHrs values: 0...65535 h
233 * 7.010: DPT_PropDataType values: 0...65535
234 * 7.011: DPT_Length_mm values: 0...65535 mm
235 * 7.012: DPT_UElCurrentmA values: 0...65535 mA
236 * 7.013: DPT_Brightness values: 0...65535 lx
237 * 7.600: DPT_Colour_Temperature values: 0...65535 K, 2000K 3000K 5000K 8000K
239 dptMainTypeMap.put(7, DecimalType.class);
240 /** Exceptions Datapoint Types "2-Octet Unsigned Value", Main number 7 */
241 dptTypeMap.put(DPTXlator2ByteFloat.DPT_HUMIDITY.getID(), PercentType.class);
245 * 8.000: General integer
246 * 8.001: DPT_Value_2_Count
247 * 8.002: DPT_DeltaTimeMsec
248 * 8.003: DPT_DeltaTime10MSec
249 * 8.004: DPT_DeltaTime100MSec
250 * 8.005: DPT_DeltaTimeSec
251 * 8.006: DPT_DeltaTimeMin
252 * 8.007: DPT_DeltaTimeHrs
253 * 8.010: DPT_Percent_V16
254 * 8.011: DPT_Rotation_Angle
255 * 8.012: DPT_Length_m
257 dptMainTypeMap.put(8, DecimalType.class);
261 * 9.000: General float
262 * 9.001: DPT_Value_Temp values: -273...+670760 °C
263 * 9.002: DPT_Value_Tempd values: -670760...+670760 K
264 * 9.003: DPT_Value_Tempa values: -670760...+670760 K/h
265 * 9.004: DPT_Value_Lux values: 0...+670760 lx
266 * 9.005: DPT_Value_Wsp values: 0...+670760 m/s
267 * 9.006: DPT_Value_Pres values: 0...+670760 Pa
268 * 9.007: DPT_Value_Humidity values: 0...+670760 %
269 * 9.008: DPT_Value_AirQuality values: 0...+670760 ppm
270 * 9.010: DPT_Value_Time1 values: -670760...+670760 s
271 * 9.011: DPT_Value_Time2 values: -670760...+670760 ms
272 * 9.020: DPT_Value_Volt values: -670760...+670760 mV
273 * 9.021: DPT_Value_Curr values: -670760...+670760 mA
274 * 9.022: DPT_PowerDensity values: -670760...+670760 W/m²
275 * 9.023: DPT_KelvinPerPercent values: -670760...+670760 K/%
276 * 9.024: DPT_Power values: -670760...+670760 kW
277 * 9.025: DPT_Value_Volume_Flow values: -670760...+670760 l/h
278 * 9.026: DPT_Rain_Amount values: -671088.64...670760.96 l/m²
279 * 9.027: DPT_Value_Temp_F values: -459.6...670760.96 °F
280 * 9.028: DPT_Value_Wsp_kmh values: 0...670760.96 km/h
281 * 9.029: DPT_Value_Absolute_Humidity: 0...670760 g/m³
282 * 9.030: DPT_Concentration_μgm3: 0...670760 µg/m³
284 dptMainTypeMap.put(9, DecimalType.class);
285 /** Exceptions Datapoint Types "2-Octet Float Value", Main number 9 */
286 dptTypeMap.put(DPTXlator2ByteFloat.DPT_HUMIDITY.getID(), PercentType.class);
290 * 10.001: DPT_TimeOfDay values: 1 = Monday...7 = Sunday, 0 = no-day, 00:00:00 Sun, 23:59:59 dow, hh:mm:ss
292 dptMainTypeMap.put(10, DateTimeType.class);
293 /** Exceptions Datapoint Types "Time", Main number 10 */
294 // Example: dptTypeMap.put(DPTXlatorTime.DPT_TIMEOFDAY.getID(), DateTimeType.class);
298 * 11.001: DPT_Date values: 1990-01-01...2089-12-31, yyyy-mm-dd
300 dptMainTypeMap.put(11, DateTimeType.class);
301 /** Exceptions Datapoint Types “Date”", Main number 11 */
302 // Example: dptTypeMap.put(DPTXlatorDate.DPT_DATE.getID(), DateTimeType.class);
306 * 12.000: General unsigned long
307 * 12.001: DPT_Value_4_Ucount values: 0...4294967295 counter pulses
308 * 12.100: DPT_LongTimePeriod_Sec values: 0...4294967295 s
309 * 12.101: DPT_LongTimePeriod_Min values: 0...4294967295 min
310 * 12.102: DPT_LongTimePeriod_Hrs values: 0...4294967295 h
311 * 12.1200: DPT_VolumeLiquid_Litre values: 0..4294967295 l
312 * 12.1201: DPT_Volume_m3 values: 0..4294967295 m3
314 dptMainTypeMap.put(12, DecimalType.class);
315 /** Exceptions Datapoint Types "4-Octet Unsigned Value", Main number 12 */
316 // Example: dptTypeMap.put(DPTXlator4ByteUnsigned.DPT_VALUE_4_UCOUNT.getID(), DecimalType.class);
320 * 13.000: General long
321 * 13.001: DPT_Value_4_Count values: -2147483648...2147483647 counter pulses
322 * 13.002: DPT_FlowRate_m3h values: -2147483648...2147483647 m3/h
323 * 13.010: DPT_ActiveEnergy values: -2147483648...2147483647 Wh
324 * 13.011: DPT_ApparantEnergy values: -2147483648...2147483647 VAh
325 * 13.012: DPT_ReactiveEnergy values: -2147483648...2147483647 VARh
326 * 13.013: DPT_ActiveEnergy_kWh values: -2147483648...2147483647 kWh
327 * 13.014: DPT_ApparantEnergy_kVAh values: -2147483648...2147483647 kVAh
328 * 13.015: DPT_ReactiveEnergy_kVARh values: -2147483648...2147483647 kVAR
329 * 13.016: DPT_ActiveEnergy_MWh4 values: -2147483648...2147483647 MWh
330 * 13.100: DPT_LongDeltaTimeSec values: -2147483648...2147483647 s
331 * 13.1200: DPT_DeltaVolumeLiquid_Litre values: -2147483648...2147483647 l
332 * 13.1201: DPT_DeltaVolume_m3 values: -2147483648...2147483647 m³
334 dptMainTypeMap.put(13, DecimalType.class);
335 /** Exceptions Datapoint Types "4-Octet Signed Value", Main number 13 */
336 // Example: dptTypeMap.put(DPTXlator4ByteSigned.DPT_COUNT.getID(), DecimalType.class);
339 * MainType: 14, Range: [-3.40282347e+38f...3.40282347e+38f]
340 * 14.000: Acceleration, values: ms⁻²
341 * 14.001: Acceleration, angular, values: rad s⁻²
342 * 14.002: Activation energy, values: J/mol
343 * 14.003: Activity, values: s⁻¹
344 * 14.004: Mol, values: mol
345 * 14.005: Amplitude, values:
346 * 14.006: Angle, values: rad
347 * 14.007: Angle, values: °
348 * 14.008: Momentum, values: Js
349 * 14.009: Angular velocity, values: rad/s
350 * 14.010: Area, values: m²
351 * 14.011: Capacitance, values: F
352 * 14.012: Charge density (surface), values: C m⁻²
353 * 14.013: Charge density (volume), values: C m⁻³
354 * 14.014: Compressibility, values: m²/N
355 * 14.015: Conductance, values: Ω⁻¹
356 * 14.016: Conductivity, electrical, values: Ω⁻¹m⁻¹
357 * 14.017: Density, values: kg m⁻³
358 * 14.018: Electric charge, values: C
359 * 14.019: Electric current, values: A
360 * 14.020: Electric current density, values: A m⁻²
361 * 14.021: Electric dipole moment, values: Cm
362 * 14.022: Electric displacement, values: C m⁻²
363 * 14.023: Electric field strength, values: V/m
364 * 14.024: Electric flux, values: Vm
365 * 14.025: Electric flux density, values: C m⁻²
366 * 14.026: Electric polarization, values: C m⁻²
367 * 14.027: Electric potential, values: V
368 * 14.028: Electric potential difference, values: V
369 * 14.029: Electromagnetic moment, values: A m²
370 * 14.030: Electromotive force, values: V
371 * 14.031: Energy, values: J
372 * 14.032: Force, values: N
373 * 14.033: Frequency, values: Hz
374 * 14.034: Frequency, angular, values: rad/s
375 * 14.035: Heat capacity, values: J/K
376 * 14.036: Heat flow rate, values: W
377 * 14.037: Heat quantity, values: J
378 * 14.038: Impedance, values: Ω
379 * 14.039: Length, values: m
380 * 14.040: Quantity of Light, values: J
381 * 14.041: Luminance, values: cd m⁻²
382 * 14.042: Luminous flux, values: lm
383 * 14.043: Luminous intensity, values: cd
384 * 14.044: Magnetic field strength, values: A/m
385 * 14.045: Magnetic flux, values: Wb
386 * 14.046: Magnetic flux density, values: T
387 * 14.047: Magnetic moment, values: A m²
388 * 14.048: Magnetic polarization, values: T
389 * 14.049: Magnetization, values: A/m
390 * 14.050: Magneto motive force, values: A
391 * 14.051: Mass, values: kg
392 * 14.052: Mass flux, values: kg/s
393 * 14.053: Momentum, values: N/s
394 * 14.054: Phase angle, radiant, values: rad
395 * 14.055: Phase angle, degree, values: °
396 * 14.056: Power, values: W
397 * 14.057: Power factor, values:
398 * 14.058: Pressure, values: Pa
399 * 14.059: Reactance, values: Ω
400 * 14.060: Resistance, values: Ω
401 * 14.061: Resistivity, values: Ωm
402 * 14.062: Self inductance, values: H
403 * 14.063: Solid angle, values: sr
404 * 14.064: Sound intensity, values: W m⁻²
405 * 14.065: Speed, values: m/s
406 * 14.066: Stress, values: Pa
407 * 14.067: Surface tension, values: N/m
408 * 14.068: Temperature in Celsius Degree, values: °C
409 * 14.069: Temperature, absolute, values: K
410 * 14.070: Temperature difference, values: K
411 * 14.071: Thermal capacity, values: J/K
412 * 14.072: Thermal conductivity, values: W/m K⁻¹
413 * 14.073: Thermoelectric power, values: V/K
414 * 14.074: Time, values: s
415 * 14.075: Torque, values: Nm
416 * 14.076: Volume, values: m³
417 * 14.077: Volume flux, values: m³/s
418 * 14.078: Weight, values: N
419 * 14.079: Work, values: J
420 * 14.080: apparent power: VA
422 dptMainTypeMap.put(14, DecimalType.class);
423 /** Exceptions Datapoint Types "4-Octet Float Value", Main number 14 */
424 // Example: dptTypeMap.put(DPTXlator4ByteFloat.DPT_ACCELERATION_ANGULAR.getID(), DecimalType.class);
428 * 16.000: ASCII string
429 * 16.001: ISO-8859-1 string (Latin 1)
431 dptMainTypeMap.put(16, StringType.class);
432 /** Exceptions Datapoint Types "String", Main number 16 */
433 dptTypeMap.put(DPTXlatorString.DPT_STRING_8859_1.getID(), StringType.class);
434 dptTypeMap.put(DPTXlatorString.DPT_STRING_ASCII.getID(), StringType.class);
438 * 17.001: Scene Number, values: 0...63
440 dptMainTypeMap.put(17, DecimalType.class);
441 /** Exceptions Datapoint Types "Scene Number", Main number 17 */
442 // Example: dptTypeMap.put(DPTXlatorSceneNumber.DPT_SCENE_NUMBER.getID(), DecimalType.class);
446 * 18.001: Scene Control, values: 0...63, 0 = activate, 1 = learn
448 dptMainTypeMap.put(18, DecimalType.class);
449 /** Exceptions Datapoint Types "Scene Control", Main number 18 */
450 // Example: dptTypeMap.put(DPTXlatorSceneControl.DPT_SCENE_CONTROL.getID(), DecimalType.class);
454 * 19.001: Date with time, values: 0 = 1900, 255 = 2155, 01/01 00:00:00, 12/31 24:00:00 yr/mth/day hr:min:sec
456 dptMainTypeMap.put(19, DateTimeType.class);
457 /** Exceptions Datapoint Types "DateTime", Main number 19 */
458 // Example: dptTypeMap.put(DPTXlatorDateTime.DPT_DATE_TIME.getID(), DateTimeType.class);
462 * 20.001: System Clock Mode, enumeration [0..2]
463 * 20.002: Building Mode, enumeration [0..2]
464 * 20.003: Occupancy Mode, enumeration [0..2]
465 * 20.004: Priority, enumeration [0..3]
466 * 20.005: Light Application Mode, enumeration [0..2]
467 * 20.006: Application Area, enumeration [0..14]
468 * 20.007: Alarm Class Type, enumeration [0..3]
469 * 20.008: PSU Mode, enumeration [0..2]
470 * 20.011: Error Class System, enumeration [0..18]
471 * 20.012: Error Class HVAC, enumeration [0..4]
472 * 20.013: Time Delay, enumeration [0..25]
473 * 20.014: Beaufort Wind Force Scale, enumeration [0..12]
474 * 20.017: Sensor Select, enumeration [0..4]
475 * 20.020: Actuator Connect Type, enumeration [1..2]
476 * 20.100: Fuel Type, enumeration [0..3]
477 * 20.101: Burner Type, enumeration [0..3]
478 * 20.102: HVAC Mode, enumeration [0..4]
479 * 20.103: DHW Mode, enumeration [0..4]
480 * 20.104: Load Priority, enumeration [0..2]
481 * 20.105: HVAC Control Mode, enumeration [0..20]
482 * 20.106: HVAC Emergency Mode, enumeration [0..5]
483 * 20.107: Changeover Mode, enumeration [0..2]
484 * 20.108: Valve Mode, enumeration [1..5]
485 * 20.109: Damper Mode, enumeration [1..4]
486 * 20.110: Heater Mode, enumeration [1..3]
487 * 20.111: Fan Mode, enumeration [0..2]
488 * 20.112: Master/Slave Mode, enumeration [0..2]
489 * 20.113: Status Room Setpoint, enumeration [0..2]
490 * 20.114: Metering Device Type, enumeration [0..41/255]
491 * 20.120: Air Damper Actuator Type, enumeration [1..2]
492 * 20.121: Backup Mode, enumeration [0..1]
493 * 20.122: Start Synchronization, enumeration [0..2]
494 * 20.600: Behavior Lock/Unlock, enumeration [0..6]
495 * 20.601: Behavior Bus Power Up/Down, enumeration [0..4]
496 * 20.602: DALI Fade Time, enumeration [0..15]
497 * 20.603: Blinking Mode, enumeration [0..2]
498 * 20.604: Light Control Mode, enumeration [0..1]
499 * 20.605: Switch PB Model, enumeration [1..2]
500 * 20.606: PB Action, enumeration [0..3]
501 * 20.607: Dimm PB Model, enumeration [1..4]
502 * 20.608: Switch On Mode, enumeration [0..2]
503 * 20.609: Load Type Set, enumeration [0..2]
504 * 20.610: Load Type Detected, enumeration [0..3]
505 * 20.801: SAB Except Behavior, enumeration [0..4]
506 * 20.802: SAB Behavior Lock/Unlock, enumeration [0..6]
507 * 20.803: SSSB Mode, enumeration [1..4]
508 * 20.804: Blinds Control Mode, enumeration [0..1]
509 * 20.1000: Comm Mode, enumeration [0..255]
510 * 20.1001: Additional Info Type, enumeration [0..7]
511 * 20.1002: RF Mode Select, enumeration [0..2]
512 * 20.1003: RF Filter Select, enumeration [0..3]
513 * 20.1200: M-Bus Breaker/Valve State, enumeration [0..255]
514 * 20.1202: Gas Measurement Condition, enumeration [0..3]
517 dptMainTypeMap.put(20, StringType.class);
518 /** Exceptions Datapoint Types, Main number 20 */
519 // Example since calimero 2.4: dptTypeMap.put(DPTXlator8BitEnum.DptSystemClockMode.getID(), StringType.class);
523 * 21.001: General Status, values: 0...31
524 * 21.002: Device Control, values: 0...7
525 * 21.100: Forcing Signal, values: 0...255
526 * 21.101: Forcing Signal Cool, values: 0...1
527 * 21.102: Room Heating Controller Status, values: 0...255
528 * 21.103: Solar Dhw Controller Status, values: 0...7
529 * 21.104: Fuel Type Set, values: 0...7
530 * 21.105: Room Cooling Controller Status, values: 0...1
531 * 21.106: Ventilation Controller Status, values: 0...15
532 * 21.601: Light Actuator Error Info, values: 0...127
533 * 21.1000: R F Comm Mode Info, values: 0...7
534 * 21.1001: R F Filter Modes, values: 0...7
535 * 21.1010: Channel Activation State, values: 0...255
537 dptMainTypeMap.put(21, StringType.class);
538 /** Exceptions Datapoint Types, Main number 21 */
539 // Example since calimero 2.4: dptTypeMap.put(DptXlator8BitSet.DptGeneralStatus.getID(), StringType.class);
545 dptMainTypeMap.put(28, StringType.class);
546 /** Exceptions Datapoint Types "String" UTF-8, Main number 28 */
547 // Example: dptTypeMap.put(DPTXlatorUtf8.DPT_UTF8.getID(), StringType.class);
551 * 29.010: Active Energy, values: -9223372036854775808...9223372036854775807 Wh
552 * 29.011: Apparent energy, values: -9223372036854775808...9223372036854775807 VAh
553 * 29.012: Reactive energy, values: -9223372036854775808...9223372036854775807 VARh
555 dptMainTypeMap.put(29, DecimalType.class);
556 /** Exceptions Datapoint Types "64-Bit Signed Value", Main number 29 */
557 // Example: dptTypeMap.put(DPTXlator64BitSigned.DPT_ACTIVE_ENERGY.getID(), DecimalType.class);
561 * 229.001: Metering Value, values: -2147483648...2147483647
563 dptMainTypeMap.put(229, DecimalType.class);
564 /** Exceptions Datapoint Types "4-Octet Signed Value", Main number 229 */
565 // Example: dptTypeMap.put(DptXlatorMeteringValue.DptMeteringValue.getID(), DecimalType.class);
568 * MainType: 232, 3 bytes
569 * 232.600: DPT_Colour_RGB, values: 0 0 0...255 255 255, r g b
571 dptMainTypeMap.put(232, HSBType.class);
572 /** Exceptions Datapoint Types "RGB Color", Main number 232 */
573 // Example: dptTypeMap.put(DPTXlatorRGB.DPT_RGB.getID(), HSBType.class);
575 defaultDptMap = new HashMap<>();
576 defaultDptMap.put(OnOffType.class, DPTXlatorBoolean.DPT_SWITCH.getID());
577 defaultDptMap.put(UpDownType.class, DPTXlatorBoolean.DPT_UPDOWN.getID());
578 defaultDptMap.put(StopMoveType.class, DPTXlatorBoolean.DPT_START.getID());
579 defaultDptMap.put(OpenClosedType.class, DPTXlatorBoolean.DPT_WINDOW_DOOR.getID());
580 defaultDptMap.put(IncreaseDecreaseType.class, DPTXlator3BitControlled.DPT_CONTROL_DIMMING.getID());
581 defaultDptMap.put(PercentType.class, DPTXlator8BitUnsigned.DPT_SCALING.getID());
582 defaultDptMap.put(DecimalType.class, DPTXlator2ByteFloat.DPT_TEMPERATURE.getID());
583 defaultDptMap.put(DateTimeType.class, DPTXlatorTime.DPT_TIMEOFDAY.getID());
584 defaultDptMap.put(StringType.class, DPTXlatorString.DPT_STRING_8859_1.getID());
585 defaultDptMap.put(HSBType.class, DPTXlatorRGB.DPT_RGB.getID());
589 * This function computes the target unit for type conversion from OH quantity type to DPT types.
590 * Calimero library provides units which can be used for most of the DPTs. There are some deviations
591 * from the OH unit scheme which are handled.
593 private String quantityTypeToDPTValue(QuantityType<?> qt, int mainNumber, int subNumber, String dpUnit)
594 throws KNXException {
595 String targetOhUnit = dpUnit;
596 double scaleFactor = 1.0;
597 switch (mainNumber) {
608 // special case: temperature deltas specified in different units
609 // ignore the offset, but run a conversion to handle prefixes like mK
610 // scaleFactor is needed to properly handle °F
612 final String unit = qt.getUnit().toString();
613 // find out if the unit is based on °C or K, getSystemUnit() does not help here as it always
615 if (unit.contains("°C")) {
617 } else if (unit.contains("°F")) {
619 scaleFactor = 5.0 / 9.0;
620 } else if (unit.contains("K")) {
628 final String unit = qt.getUnit().toString();
629 if (unit.contains("°C")) {
630 targetOhUnit = "°C/h";
631 } else if (unit.contains("°F")) {
632 targetOhUnit = "°F/h";
633 scaleFactor = 5.0 / 9.0;
634 } else if (unit.contains("K")) {
635 targetOhUnit = "K/h";
642 final String unit = qt.getUnit().toString();
643 if (unit.contains("°C")) {
644 targetOhUnit = "°C/%";
645 } else if (unit.contains("°F")) {
646 targetOhUnit = "°F/%";
647 scaleFactor = 5.0 / 9.0;
648 } else if (unit.contains("K")) {
649 targetOhUnit = "K/%";
660 // Calimero uses "litre"
669 // Calimero uses VARh, OH uses varh
670 targetOhUnit = targetOhUnit.replace("VARh", "varh");
673 // OH does not accept kVAh, only VAh
674 targetOhUnit = targetOhUnit.replace("kVAh", "VAh");
675 scaleFactor = 1.0 / 1000.0;
681 targetOhUnit = targetOhUnit.replace("Ω\u207B¹", "S");
682 // Calimero uses a special unicode character to specify units like m*s^-2
683 // this needs to be rewritten to m/s²
684 final int posMinus = targetOhUnit.indexOf("\u207B");
686 targetOhUnit = targetOhUnit.substring(0, posMinus - 1) + "/" + targetOhUnit.charAt(posMinus - 1)
687 + targetOhUnit.substring(posMinus + 1);
691 // OH does not support unut Js, need to expand
692 targetOhUnit = "J*s";
695 targetOhUnit = "C*m";
702 targetOhUnit = "A*m²";
705 if (qt.getUnit().toString().contains("J")) {
708 targetOhUnit = "lm*s";
712 targetOhUnit = "Ohm*m";
715 targetOhUnit = "N*m";
722 // Calimero uses VARh, OH uses varh
723 targetOhUnit = targetOhUnit.replace("VARh", "varh");
728 // replace e.g. m3 by m³
729 targetOhUnit = targetOhUnit.replace("3", "³").replace("2", "²");
731 final QuantityType<?> result = qt.toUnit(targetOhUnit);
732 if (result == null) {
733 throw new KNXException("incompatible types: " + qt.getUnit().toString() + ", " + targetOhUnit);
735 return String.valueOf(result.doubleValue() * scaleFactor);
739 public @Nullable String toDPTValue(Type type, @Nullable String dptID) {
741 int mainNumber = getMainNumber(dptID);
742 if (mainNumber == -1) {
743 logger.error("toDPTValue couldn't identify mainnumber in dptID: {}", dptID);
746 int subNumber = getSubNumber(dptID);
747 if (subNumber == -1) {
748 logger.debug("toType: couldn't identify sub number in dptID: {}.", dptID);
753 DPTXlator translator = TranslatorTypes.createTranslator(mainNumber, dptID);
754 dpt = translator.getType();
755 } catch (KNXException e) {
760 // check for HSBType first, because it extends PercentType as well
761 if (type instanceof HSBType) {
762 switch (mainNumber) {
765 case 3: // * 5.003: Angle, values: 0...360 °
766 return ((HSBType) type).getHue().toString();
767 case 1: // * 5.001: Scaling, values: 0...100 %
769 return ((HSBType) type).getBrightness().toString();
774 HSBType hc = ((HSBType) type);
775 return "r:" + convertPercentToByte(hc.getRed()) + " g:"
776 + convertPercentToByte(hc.getGreen()) + " b:"
777 + convertPercentToByte(hc.getBlue());
780 HSBType hc = ((HSBType) type);
781 return "r:" + hc.getRed().intValue() + " g:" + hc.getGreen().intValue() + " b:"
782 + hc.getBlue().intValue();
784 } else if (type instanceof OnOffType) {
785 return type.equals(OnOffType.OFF) ? dpt.getLowerValue() : dpt.getUpperValue();
786 } else if (type instanceof UpDownType) {
787 return type.equals(UpDownType.UP) ? dpt.getLowerValue() : dpt.getUpperValue();
788 } else if (type instanceof IncreaseDecreaseType) {
789 DPT valueDPT = ((DPTXlator3BitControlled.DPT3BitControlled) dpt).getControlDPT();
790 return type.equals(IncreaseDecreaseType.DECREASE) ? valueDPT.getLowerValue() + " 5"
791 : valueDPT.getUpperValue() + " 5";
792 } else if (type instanceof OpenClosedType) {
793 return type.equals(OpenClosedType.CLOSED) ? dpt.getLowerValue() : dpt.getUpperValue();
794 } else if (type instanceof StopMoveType) {
795 return type.equals(StopMoveType.STOP) ? dpt.getLowerValue() : dpt.getUpperValue();
796 } else if (type instanceof PercentType) {
797 return String.valueOf(((DecimalType) type).intValue());
798 } else if (type instanceof DecimalType) {
799 switch (mainNumber) {
801 DPT valueDPT = ((DPTXlator1BitControlled.DPT1BitControlled) dpt).getValueDPT();
802 switch (((DecimalType) type).intValue()) {
804 return "0 " + valueDPT.getLowerValue();
806 return "0 " + valueDPT.getUpperValue();
808 return "1 " + valueDPT.getLowerValue();
810 return "1 " + valueDPT.getUpperValue();
813 int intVal = ((DecimalType) type).intValue();
815 return "learn " + (intVal - 0x80);
817 return "activate " + intVal;
820 return ((DecimalType) type).toBigDecimal().stripTrailingZeros().toPlainString();
822 } else if (type instanceof StringType) {
823 return type.toString();
824 } else if (type instanceof DateTimeType) {
825 return formatDateTime((DateTimeType) type, dptID);
826 } else if (type instanceof QuantityType) {
827 final QuantityType<?> qt = (QuantityType<?>) type;
828 return quantityTypeToDPTValue(qt, mainNumber, subNumber, dpt.getUnit());
830 } catch (Exception e) {
831 logger.warn("An exception occurred converting type {} to dpt id {}: error message={}", type, dptID,
836 logger.debug("toDPTValue: Couldn't convert type {} to dpt id {} (no mapping).", type, dptID);
842 public @Nullable Type toType(Datapoint datapoint, byte[] data) {
844 DPTXlator translator = TranslatorTypes.createTranslator(datapoint.getMainNumber(), datapoint.getDPT());
845 translator.setData(data);
846 String value = translator.getValue();
848 String id = translator.getType().getID();
849 logger.trace("toType datapoint DPT = {}", datapoint.getDPT());
851 int mainNumber = getMainNumber(id);
852 if (mainNumber == -1) {
853 logger.debug("toType: couldn't identify mainnumber in dptID: {}.", id);
856 int subNumber = getSubNumber(id);
857 if (subNumber == -1) {
858 logger.debug("toType: couldn't identify sub number in dptID: {}.", id);
862 * Following code section deals with specific mapping of values from KNX to openHAB types were the String
863 * received from the DPTXlator is not sufficient to set the openHAB type or has bugs
865 switch (mainNumber) {
867 DPTXlatorBoolean translatorBoolean = (DPTXlatorBoolean) translator;
870 return translatorBoolean.getValueBoolean() ? UpDownType.DOWN : UpDownType.UP;
872 return translatorBoolean.getValueBoolean() ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
874 return translatorBoolean.getValueBoolean() ? StopMoveType.MOVE : StopMoveType.STOP;
876 return translatorBoolean.getValueBoolean() ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
878 return DecimalType.valueOf(translatorBoolean.getValueBoolean() ? "1" : "0");
880 return translatorBoolean.getValueBoolean() ? OnOffType.ON : OnOffType.OFF;
883 DPTXlator1BitControlled translator1BitControlled = (DPTXlator1BitControlled) translator;
884 int decValue = (translator1BitControlled.getControlBit() ? 2 : 0)
885 + (translator1BitControlled.getValueBit() ? 1 : 0);
886 return new DecimalType(decValue);
888 DPTXlator3BitControlled translator3BitControlled = (DPTXlator3BitControlled) translator;
889 if (translator3BitControlled.getStepCode() == 0) {
890 logger.debug("toType: KNX DPT_Control_Dimming: break received.");
891 return UnDefType.NULL;
895 return translator3BitControlled.getControlBit() ? IncreaseDecreaseType.INCREASE
896 : IncreaseDecreaseType.DECREASE;
898 return translator3BitControlled.getControlBit() ? UpDownType.DOWN : UpDownType.UP;
902 DPTXlatorSceneControl translatorSceneControl = (DPTXlatorSceneControl) translator;
903 int decimalValue = translatorSceneControl.getSceneNumber();
904 if (value.startsWith("learn")) {
905 decimalValue += 0x80;
907 value = String.valueOf(decimalValue);
911 DPTXlatorDateTime translatorDateTime = (DPTXlatorDateTime) translator;
912 if (translatorDateTime.isFaultyClock()) {
913 // Not supported: faulty clock
914 logger.debug("toType: KNX clock msg ignored: clock faulty bit set, which is not supported");
916 } else if (!translatorDateTime.isValidField(DPTXlatorDateTime.YEAR)
917 && translatorDateTime.isValidField(DPTXlatorDateTime.DATE)) {
918 // Not supported: "/1/1" (month and day without year)
920 "toType: KNX clock msg ignored: no year, but day and month, which is not supported");
922 } else if (translatorDateTime.isValidField(DPTXlatorDateTime.YEAR)
923 && !translatorDateTime.isValidField(DPTXlatorDateTime.DATE)) {
924 // Not supported: "1900" (year without month and day)
926 "toType: KNX clock msg ignored: no day and month, but year, which is not supported");
928 } else if (!translatorDateTime.isValidField(DPTXlatorDateTime.YEAR)
929 && !translatorDateTime.isValidField(DPTXlatorDateTime.DATE)
930 && !translatorDateTime.isValidField(DPTXlatorDateTime.TIME)) {
931 // Not supported: No year, no date and no time
932 logger.debug("toType: KNX clock msg ignored: no day and month or year, which is not supported");
936 Calendar cal = Calendar.getInstance();
937 if (translatorDateTime.isValidField(DPTXlatorDateTime.YEAR)
938 && !translatorDateTime.isValidField(DPTXlatorDateTime.TIME)) {
939 // Pure date format, no time information
940 cal.setTimeInMillis(translatorDateTime.getValueMilliseconds());
941 value = new SimpleDateFormat(DateTimeType.DATE_PATTERN).format(cal.getTime());
942 return DateTimeType.valueOf(value);
943 } else if (!translatorDateTime.isValidField(DPTXlatorDateTime.YEAR)
944 && translatorDateTime.isValidField(DPTXlatorDateTime.TIME)) {
945 // Pure time format, no date information
947 cal.set(Calendar.HOUR_OF_DAY, translatorDateTime.getHour());
948 cal.set(Calendar.MINUTE, translatorDateTime.getMinute());
949 cal.set(Calendar.SECOND, translatorDateTime.getSecond());
950 value = new SimpleDateFormat(DateTimeType.DATE_PATTERN).format(cal.getTime());
951 return DateTimeType.valueOf(value);
952 } else if (translatorDateTime.isValidField(DPTXlatorDateTime.YEAR)
953 && translatorDateTime.isValidField(DPTXlatorDateTime.TIME)) {
954 // Date format and time information
955 cal.setTimeInMillis(translatorDateTime.getValueMilliseconds());
956 value = new SimpleDateFormat(DateTimeType.DATE_PATTERN).format(cal.getTime());
957 return DateTimeType.valueOf(value);
962 Class<? extends Type> typeClass = toTypeClass(id);
963 if (typeClass == null) {
967 if (typeClass.equals(PercentType.class)) {
968 return new PercentType(BigDecimal.valueOf(Math.round(translator.getNumericValue())));
970 if (typeClass.equals(DecimalType.class)) {
971 return new DecimalType(translator.getNumericValue());
973 if (typeClass.equals(StringType.class)) {
974 return StringType.valueOf(value);
977 if (typeClass.equals(DateTimeType.class)) {
978 String date = formatDateTime(value, datapoint.getDPT());
979 if (date.isEmpty()) {
980 logger.debug("toType: KNX clock msg ignored: date object null or empty {}.", date);
983 return DateTimeType.valueOf(date);
987 if (typeClass.equals(HSBType.class)) {
988 // value has format of "r:<red value> g:<green value> b:<blue value>"
989 int r = Integer.parseInt(value.split(" ")[0].split(":")[1]);
990 int g = Integer.parseInt(value.split(" ")[1].split(":")[1]);
991 int b = Integer.parseInt(value.split(" ")[2].split(":")[1]);
993 return HSBType.fromRGB(r, g, b);
996 } catch (KNXFormatException kfe) {
997 logger.info("Translator couldn't parse data for datapoint type '{}' (KNXFormatException).",
999 } catch (KNXIllegalArgumentException kiae) {
1000 logger.info("Translator couldn't parse data for datapoint type '{}' (KNXIllegalArgumentException).",
1001 datapoint.getDPT());
1002 } catch (KNXException e) {
1003 logger.warn("Failed creating a translator for datapoint type '{}'.", datapoint.getDPT(), e);
1010 * Converts a datapoint type id into an openHAB type class
1012 * @param dptId the datapoint type id
1013 * @return the openHAB type (command or state) class or {@code null} if the datapoint type id is not supported.
1016 public @Nullable Class<? extends Type> toTypeClass(@Nullable String dptId) {
1018 Class<? extends Type> ohClass = dptTypeMap.get(dptId);
1019 if (ohClass == null) {
1020 int mainNumber = getMainNumber(dptId);
1021 if (mainNumber == -1) {
1022 logger.debug("Couldn't convert KNX datapoint type id into openHAB type class for dptId: {}.", dptId);
1025 ohClass = dptMainTypeMap.get(mainNumber);
1031 * Converts an openHAB type class into a datapoint type id.
1033 * @param typeClass the openHAB type class
1034 * @return the datapoint type id
1036 public @Nullable String toDPTid(Class<? extends Type> typeClass) {
1037 return defaultDptMap.get(typeClass);
1041 * Formats the given <code>value</code> according to the datapoint type
1042 * <code>dpt</code> to a String which can be processed by {@link DateTimeType}.
1047 * @return a formatted String like </code>yyyy-MM-dd'T'HH:mm:ss</code> which
1048 * is target format of the {@link DateTimeType}
1050 private String formatDateTime(String value, String dpt) {
1054 if (DPTXlatorDate.DPT_DATE.getID().equals(dpt)) {
1055 date = new SimpleDateFormat(DATE_FORMAT).parse(value);
1056 } else if (DPTXlatorTime.DPT_TIMEOFDAY.getID().equals(dpt)) {
1057 if (value.contains("no-day")) {
1059 * KNX "no-day" needs special treatment since openHAB's DateTimeType doesn't support "no-day".
1060 * Workaround: remove the "no-day" String, parse the remaining time string, which will result in a
1061 * date of "1970-01-01".
1062 * Replace "no-day" with the current day name
1064 StringBuffer stb = new StringBuffer(value);
1065 int start = stb.indexOf("no-day");
1066 int end = start + "no-day".length();
1067 stb.replace(start, end, String.format(Locale.US, "%1$ta", Calendar.getInstance()));
1068 value = stb.toString();
1071 date = new SimpleDateFormat(TIME_DAY_FORMAT, Locale.US).parse(value);
1072 } catch (ParseException pe) {
1073 date = new SimpleDateFormat(TIME_FORMAT, Locale.US).parse(value);
1076 } catch (ParseException pe) {
1077 // do nothing but logging
1078 logger.warn("Could not parse '{}' to a valid date", value);
1081 return date != null ? new SimpleDateFormat(DateTimeType.DATE_PATTERN).format(date) : "";
1085 * Formats the given internal <code>dateType</code> to a knx readable String
1086 * according to the target datapoint type <code>dpt</code>.
1089 * @param dpt the target datapoint type
1091 * @return a String which contains either an ISO8601 formatted date (yyyy-mm-dd),
1092 * a formatted 24-hour clock with the day of week prepended (Mon, 12:00:00) or
1093 * a formatted 24-hour clock (12:00:00)
1095 * @throws IllegalArgumentException if none of the datapoint types DPT_DATE or
1096 * DPT_TIMEOFDAY has been used.
1098 private static String formatDateTime(DateTimeType dateType, @Nullable String dpt) {
1099 if (DPTXlatorDate.DPT_DATE.getID().equals(dpt)) {
1100 return dateType.format("%tF");
1101 } else if (DPTXlatorTime.DPT_TIMEOFDAY.getID().equals(dpt)) {
1102 return dateType.format(Locale.US, "%1$ta, %1$tT");
1103 } else if (DPTXlatorDateTime.DPT_DATE_TIME.getID().equals(dpt)) {
1104 return dateType.format(Locale.US, "%tF %1$tT");
1106 throw new IllegalArgumentException("Could not format date to datapoint type '" + dpt + "'");
1111 * Retrieves sub number from a DTP ID such as "14.001"
1113 * @param dptID String with DPT ID
1114 * @return sub number or -1
1116 private int getSubNumber(@Nullable String dptID) {
1118 if (dptID == null) {
1119 throw new IllegalArgumentException("Parameter dptID cannot be null");
1122 int dptSepratorPosition = dptID.indexOf('.');
1123 if (dptSepratorPosition > 0) {
1125 result = Integer.parseInt(dptID.substring(dptSepratorPosition + 1, dptID.length()));
1126 } catch (NumberFormatException nfe) {
1127 logger.error("toType couldn't identify main and/or sub number in dptID (NumberFormatException): {}",
1129 } catch (IndexOutOfBoundsException ioobe) {
1130 logger.error("toType couldn't identify main and/or sub number in dptID (IndexOutOfBoundsException): {}",
1138 * Retrieves main number from a DTP ID such as "14.001"
1140 * @param dptID String with DPT ID
1141 * @return main number or -1
1143 private int getMainNumber(@Nullable String dptID) {
1145 if (dptID == null) {
1146 throw new IllegalArgumentException("Parameter dptID cannot be null");
1149 int dptSepratorPosition = dptID.indexOf('.');
1150 if (dptSepratorPosition > 0) {
1152 result = Integer.parseInt(dptID.substring(0, dptSepratorPosition));
1153 } catch (NumberFormatException nfe) {
1154 logger.error("toType couldn't identify main and/or sub number in dptID (NumberFormatException): {}",
1156 } catch (IndexOutOfBoundsException ioobe) {
1157 logger.error("toType couldn't identify main and/or sub number in dptID (IndexOutOfBoundsException): {}",
1165 * convert 0...100% to 1 byte 0..255
1168 * @return int 0..255
1170 private int convertPercentToByte(PercentType percent) {
1171 return percent.toBigDecimal().multiply(BigDecimal.valueOf(255))
1172 .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP).intValue();