mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-25 14:55:55 +01:00
[tacmi] Unit of Measurement fixes, added missing DateTime support (#17481)
* [tacmi] Use US Locale to format float numbers. The German locale uses a comma as a separator for decimal numbers, which means that the C.M.I. only uses the full number. Signed-off-by: Christian Niessner <github-marvkis@christian-niessner.de> Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
parent
3be45b9c8d
commit
172c124054
@ -15,6 +15,10 @@ package org.openhab.binding.tacmi.internal.schema;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@ -33,6 +37,7 @@ import org.eclipse.jetty.util.StringUtil;
|
||||
import org.openhab.binding.tacmi.internal.TACmiBindingConstants;
|
||||
import org.openhab.binding.tacmi.internal.TACmiChannelTypeProvider;
|
||||
import org.openhab.binding.tacmi.internal.schema.ApiPageEntry.Type;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
@ -196,7 +201,18 @@ public class ApiPageParser extends AbstractSimpleMarkupHandler {
|
||||
sb = sb.delete(0, 0);
|
||||
}
|
||||
if (this.fieldType == FieldType.READ_ONLY || this.fieldType == FieldType.FORM_VALUE) {
|
||||
int len = sb.length();
|
||||
int lids = sb.lastIndexOf(":");
|
||||
if (len - lids == 3) {
|
||||
int lids2 = sb.lastIndexOf(":", lids - 1);
|
||||
if (lids2 > 0 && (lids - lids2 >= 3 && lids - lids2 <= 7)) {
|
||||
// the given value might be a time. validate it
|
||||
String timeCandidate = sb.substring(lids2 + 1).trim();
|
||||
if (timeCandidate.length() == 5 && timeCandidate.matches("[0-9]{2}:[0-9]{2}")) {
|
||||
lids = lids2;
|
||||
}
|
||||
}
|
||||
}
|
||||
int fsp = sb.indexOf(" ");
|
||||
if (fsp < 0 || lids < 0 || fsp > lids) {
|
||||
logger.debug("Invalid format for setting {}:{}:{} [{}] : {}", id, line, col, this.fieldType,
|
||||
@ -350,7 +366,7 @@ public class ApiPageParser extends AbstractSimpleMarkupHandler {
|
||||
// for the older pre-X2 devices (i.e. the UVR 1611) we get a comma. So we
|
||||
// we replace all ',' with '.' to check if it's a valid number...
|
||||
String val = valParts[0].replace(',', '.');
|
||||
BigDecimal bd = new BigDecimal(val);
|
||||
float bd = Float.parseFloat(val);
|
||||
if (valParts.length == 2) {
|
||||
if ("°C".equals(valParts[1])) {
|
||||
channelType = "Number:Temperature";
|
||||
@ -374,15 +390,14 @@ public class ApiPageParser extends AbstractSimpleMarkupHandler {
|
||||
state = new QuantityType<>(bd, Units.HERTZ);
|
||||
} else if ("kW".equals(valParts[1])) {
|
||||
channelType = "Number:Power";
|
||||
bd = bd.multiply(new BigDecimal(1000));
|
||||
bd = bd *= 1000;
|
||||
state = new QuantityType<>(bd, Units.WATT);
|
||||
} else if ("kWh".equals(valParts[1])) {
|
||||
channelType = "Number:Power";
|
||||
bd = bd.multiply(new BigDecimal(1000));
|
||||
channelType = "Number:Energy";
|
||||
state = new QuantityType<>(bd, Units.KILOWATT_HOUR);
|
||||
} else if ("l/h".equals(valParts[1])) {
|
||||
channelType = "Number:Volume";
|
||||
bd = bd.divide(new BigDecimal(60));
|
||||
channelType = "Number:VolumetricFlowRate";
|
||||
bd = bd /= 60;
|
||||
state = new QuantityType<>(bd, Units.LITRE_PER_MINUTE);
|
||||
} else {
|
||||
channelType = "Number";
|
||||
@ -402,16 +417,27 @@ public class ApiPageParser extends AbstractSimpleMarkupHandler {
|
||||
type = Type.NUMERIC_FORM;
|
||||
}
|
||||
} catch (NumberFormatException nfe) {
|
||||
// not a number...
|
||||
channelType = "String";
|
||||
ctuid = null;
|
||||
// check for time....
|
||||
String[] valParts = vs.split(":");
|
||||
if (valParts.length == 2) {
|
||||
channelType = "DateTime";
|
||||
// convert it to zonedDateTime with today as date and the
|
||||
// default timezone.
|
||||
var zdt = LocalTime.parse(vs, DateTimeFormatter.ofPattern("HH:mm")).atDate(LocalDate.now())
|
||||
.atZone(ZoneId.systemDefault());
|
||||
state = new DateTimeType(zdt);
|
||||
type = Type.NUMERIC_FORM;
|
||||
} else {
|
||||
// not a number and not time...
|
||||
channelType = "String";
|
||||
state = new StringType(vs);
|
||||
type = Type.STATE_FORM;
|
||||
}
|
||||
if (this.fieldType == FieldType.READ_ONLY || this.address == null) {
|
||||
ctuid = TACmiBindingConstants.CHANNEL_TYPE_SCHEME_STATE_RO_UID;
|
||||
type = Type.READ_ONLY_STATE;
|
||||
} else {
|
||||
ctuid = null;
|
||||
type = Type.STATE_FORM;
|
||||
}
|
||||
state = new StringType(vs);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -440,7 +466,29 @@ public class ApiPageParser extends AbstractSimpleMarkupHandler {
|
||||
logger.warn("Error loading API Scheme: {} ", ex.getMessage());
|
||||
}
|
||||
}
|
||||
if (channel == null || !Objects.equals(ctuid, channel.getChannelTypeUID())) {
|
||||
if (e != null && !channelType.equals(e.channel.getAcceptedItemType())) {
|
||||
// channel type has changed. we have to rebuild the channel.
|
||||
this.channels.remove(channel);
|
||||
channel = null;
|
||||
}
|
||||
if (channel != null && ctuid == null && cx2e != null) {
|
||||
// custom channel type - check if it already exists and recreate when needed...
|
||||
ChannelTypeUID curCtuid = channel.getChannelTypeUID();
|
||||
if (curCtuid == null) {
|
||||
// we have to re-create and re-register the channel uuid
|
||||
logger.debug("Re-Registering channel type UUID for: {} ", shortName);
|
||||
var ct = buildAndRegisterChannelType(shortName, type, cx2e);
|
||||
var channelBuilder = ChannelBuilder.create(channel);
|
||||
channelBuilder.withType(ct.getUID());
|
||||
channel = channelBuilder.build(); // update channel
|
||||
} else {
|
||||
// check if channel uuid still exists and re-carete when needed
|
||||
ChannelType ct = channelTypeProvider.getChannelType(curCtuid, null);
|
||||
if (ct == null) {
|
||||
buildAndRegisterChannelType(shortName, type, cx2e);
|
||||
}
|
||||
}
|
||||
} else if (channel == null || !Objects.equals(ctuid, channel.getChannelTypeUID())) {
|
||||
logger.debug("Creating / updating channel {} of type {} for '{}'", shortName, channelType, description);
|
||||
this.configChanged = true;
|
||||
ChannelUID channelUID = new ChannelUID(this.taCmiSchemaHandler.getThing().getUID(), shortName);
|
||||
@ -456,15 +504,6 @@ public class ApiPageParser extends AbstractSimpleMarkupHandler {
|
||||
logger.warn("Error configurating channel for {}: channeltype cannot be determined!", shortName);
|
||||
}
|
||||
channel = channelBuilder.build(); // add configuration property...
|
||||
} else if (ctuid == null && cx2e != null) {
|
||||
// custom channel type - check if it already exists and recreate when needed...
|
||||
ChannelTypeUID curCtuid = channel.getChannelTypeUID();
|
||||
if (curCtuid != null) {
|
||||
ChannelType ct = channelTypeProvider.getChannelType(curCtuid, null);
|
||||
if (ct == null) {
|
||||
buildAndRegisterChannelType(shortName, type, cx2e);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.configChanged = true;
|
||||
e = new ApiPageEntry(type, channel, address, cx2e, state);
|
||||
@ -543,8 +582,11 @@ public class ApiPageParser extends AbstractSimpleMarkupHandler {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TIME:
|
||||
itemType = "DateTime";
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
throw new IllegalStateException("Unhandled OptionType: " + cx2e.optionType);
|
||||
}
|
||||
ChannelTypeBuilder<?> ctb = ChannelTypeBuilder
|
||||
.state(new ChannelTypeUID(TACmiBindingConstants.BINDING_ID, shortName), shortName, itemType)
|
||||
|
@ -33,6 +33,7 @@ public class ChangerX2Entry {
|
||||
enum OptionType {
|
||||
NUMBER,
|
||||
SELECT,
|
||||
TIME,
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -111,7 +111,7 @@ public class ChangerX2Parser extends AbstractSimpleMarkupHandler {
|
||||
String type = attributes.get("type");
|
||||
if ("number".equals(type)) {
|
||||
this.optionType = OptionType.NUMBER;
|
||||
// we transfer the limits from the input elemnt...
|
||||
// we transfer the limits from the input element...
|
||||
this.options.put(ChangerX2Entry.NUMBER_MIN, attributes.get(ChangerX2Entry.NUMBER_MIN));
|
||||
this.options.put(ChangerX2Entry.NUMBER_MAX, attributes.get(ChangerX2Entry.NUMBER_MAX));
|
||||
this.options.put(ChangerX2Entry.NUMBER_STEP, attributes.get(ChangerX2Entry.NUMBER_STEP));
|
||||
@ -120,6 +120,45 @@ public class ChangerX2Parser extends AbstractSimpleMarkupHandler {
|
||||
col, attributes);
|
||||
}
|
||||
}
|
||||
} else if ((this.parserState == ParserState.INIT || this.parserState == ParserState.INPUT)
|
||||
&& "input".equals(elementName) && "changetotimeh".equals(id)) {
|
||||
this.parserState = ParserState.INPUT_DATA;
|
||||
if (attributes != null) {
|
||||
this.optionFieldName = attributes.get("name");
|
||||
String type = attributes.get("type");
|
||||
if ("number".equals(attributes.get("type"))) {
|
||||
this.optionType = OptionType.TIME;
|
||||
// validate hour limits
|
||||
if (!"0".equals(attributes.get(ChangerX2Entry.NUMBER_MIN))
|
||||
|| !"24".equals(attributes.get(ChangerX2Entry.NUMBER_MAX))) {
|
||||
logger.warn(
|
||||
"Error parsing options for {}: Unexpected MIN/MAX values for hour input field in {}:{}: {}",
|
||||
channelName, line, col, attributes);
|
||||
}
|
||||
;
|
||||
} else {
|
||||
logger.warn("Error parsing options for {}: Unhandled input field in {}:{}: {}", channelName, line,
|
||||
col, attributes);
|
||||
}
|
||||
}
|
||||
} else if ((this.parserState == ParserState.INPUT_DATA || this.parserState == ParserState.INPUT)
|
||||
&& "input".equals(elementName) && "changetotimem".equals(id)) {
|
||||
this.parserState = ParserState.INPUT_DATA;
|
||||
if (attributes != null) {
|
||||
if ("number".equals(attributes.get("type"))) {
|
||||
this.optionType = OptionType.TIME;
|
||||
if (!"0".equals(attributes.get(ChangerX2Entry.NUMBER_MIN))
|
||||
|| !"59".equals(attributes.get(ChangerX2Entry.NUMBER_MAX))) {
|
||||
logger.warn(
|
||||
"Error parsing options for {}: Unexpected MIN/MAX values for minute input field in {}:{}: {}",
|
||||
channelName, line, col, attributes);
|
||||
}
|
||||
;
|
||||
} else {
|
||||
logger.warn("Error parsing options for {}: Unhandled input field in {}:{}: {}", channelName, line,
|
||||
col, attributes);
|
||||
}
|
||||
}
|
||||
} else if (this.parserState == ParserState.SELECT && "option".equals(elementName)) {
|
||||
this.parserState = ParserState.SELECT_OPTION;
|
||||
this.optionType = OptionType.SELECT;
|
||||
@ -136,6 +175,8 @@ public class ChangerX2Parser extends AbstractSimpleMarkupHandler {
|
||||
throws ParseException {
|
||||
if (this.parserState == ParserState.INPUT && "input".equals(elementName)) {
|
||||
this.parserState = ParserState.INIT;
|
||||
} else if (this.parserState == ParserState.INPUT_DATA && "input".equals(elementName)) {
|
||||
this.parserState = ParserState.INPUT;
|
||||
} else if (this.parserState == ParserState.SELECT && "select".equals(elementName)) {
|
||||
this.parserState = ParserState.INIT;
|
||||
} else if (this.parserState == ParserState.SELECT_OPTION && "option".equals(elementName)) {
|
||||
@ -159,6 +200,8 @@ public class ChangerX2Parser extends AbstractSimpleMarkupHandler {
|
||||
channelName, line, col, value, prev, id);
|
||||
}
|
||||
}
|
||||
} else if (this.parserState == ParserState.INPUT && "span".equals(elementName)) {
|
||||
// span's are ignored...
|
||||
} else {
|
||||
logger.debug("Error parsing options for {}: Unexpected CloseElement in {}:{}: {}", channelName, line, col,
|
||||
elementName);
|
||||
|
@ -17,6 +17,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
@ -38,6 +39,7 @@ import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.openhab.binding.tacmi.internal.TACmiChannelTypeProvider;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.Channel;
|
||||
@ -253,8 +255,18 @@ public class TACmiSchemaHandler extends BaseThingHandler {
|
||||
case NUMERIC_FORM:
|
||||
ChangerX2Entry cx2en = e.changerX2Entry;
|
||||
if (cx2en != null) {
|
||||
reqUpdate = prepareRequest(buildUri("INCLUDE/change.cgi?changeadrx2=" + cx2en.address
|
||||
+ "&changetox2=" + command.format("%.2f")));
|
||||
String val;
|
||||
if (command instanceof Number qt) {
|
||||
val = String.format(Locale.US, "%.2f", qt.floatValue());
|
||||
} else if (command instanceof DateTimeType dtt) {
|
||||
// time is transferred as minutes since midnight...
|
||||
var zdt = dtt.getZonedDateTime();
|
||||
val = Integer.toString(zdt.getHour() * 60 + zdt.getMinute());
|
||||
} else {
|
||||
val = command.format("%.2f");
|
||||
}
|
||||
reqUpdate = prepareRequest(
|
||||
buildUri("INCLUDE/change.cgi?changeadrx2=" + cx2en.address + "&changetox2=" + val));
|
||||
reqUpdate.header(HttpHeader.REFERER, this.serverBase + "schema.html"); // required...
|
||||
} else {
|
||||
logger.debug("Got command for uninitalized channel {}: {}", channelUID, command);
|
||||
|
Loading…
Reference in New Issue
Block a user