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.time.LocalDateTime;
17 import java.time.ZoneId;
18 import java.time.ZonedDateTime;
19 import java.time.format.DateTimeFormatter;
20 import java.time.format.DateTimeParseException;
21 import java.util.regex.Matcher;
22 import java.util.regex.Pattern;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.openhab.core.library.types.DateTimeType;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
30 * CosemDate represents a datetime value and will try to autodetect the format
32 * @author M. Volaart - Initial contribution
33 * @author Hilbrand Bouwkamp - Class now a factory instead of data containing class
36 class CosemDate extends CosemValueDescriptor<DateTimeType> {
38 public static final CosemDate INSTANCE = new CosemDate("timestamp");
40 * Some meters can return the following value when something is wrong.
42 public static final String INVALID_METER_VALUE = "632525252525W";
44 private final Logger logger = LoggerFactory.getLogger(CosemDate.class);
46 public CosemDate(String ohChannelId) {
51 * This enum contains the known date formats for the DSMR-specification
53 private enum CosemDateFormat {
55 * Ignore DST setting for general format. We use local time that is already DST
57 COSEM_DATE_GENERAL("(\\d{12})([S,W]?)", "yyMMddHHmmss"),
58 COSEM_DATE_DSMR_V2("(\\d{2}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2})", "yy-MM-dd HH:mm:ss");
61 * Cached compiled pattern
63 private final Pattern pattern;
66 * Cached java date formatter
68 private final DateTimeFormatter formatter;
71 * Constructs a new CosemDateFormat
73 * @param regex String containing the regular expression to check the value against (the date format
74 * should at least contain 1 regex group
75 * @param javaDateFormat String containing the datetime format to use for parsing
77 private CosemDateFormat(String regex, String javaDateFormat) {
78 pattern = Pattern.compile(regex);
79 formatter = DateTimeFormatter.ofPattern(javaDateFormat);
84 * Parses a String value to an openHAB DateTimeType
86 * The input string must be in the format yyMMddHHmmssX
88 * Based on the DSMR specification X is:
91 * <li>''. Valid for DSMR v3 specification
92 * <li>'S'. Specifies a summer time (DST = 1) datetime
93 * <li>'W'. Specifies a winter time (DST = 0) datetime
96 * @param cosemValue the value to parse
97 * @return {@link DateTimeType} representing the value the cosem value
98 * @throws ParseException if parsing failed
101 protected DateTimeType getStateValue(String cosemValue) throws ParseException {
102 for (CosemDateFormat cosemDateFormat : CosemDateFormat.values()) {
103 logger.trace("Trying pattern: {}", cosemDateFormat.pattern);
105 Matcher m = cosemDateFormat.pattern.matcher(cosemValue);
108 logger.trace("{} matches pattern: {}", cosemValue, cosemDateFormat.pattern);
111 LocalDateTime localDateTime = LocalDateTime.parse(m.group(1), cosemDateFormat.formatter);
112 return new DateTimeType(ZonedDateTime.of(localDateTime, ZoneId.systemDefault()));
113 } catch (DateTimeParseException e) {
114 if (INVALID_METER_VALUE.equals(cosemValue)) {
115 throw new ParseException(
116 "Cosem value: '" + cosemValue + "' might indicate something is wrong with the meter.",
122 throw new ParseException("Cosem value: '" + cosemValue + "' is not a known CosemDate string", 0);