From a9fcd738c1a2de1546689f91e5d1d09ba4b953c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20=27Morty=27=20Str=C3=BCbe?= Date: Wed, 5 Oct 2022 20:20:08 +0200 Subject: [PATCH 01/17] [tacmi] Support JSON-Api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This implementation is based on the JSON-API provided by TA. Signed-off-by: Moritz 'Morty' Strübe --- .../tacmi/internal/TACmiBindingConstants.java | 1 + .../tacmi/internal/TACmiHandlerFactory.java | 6 +- .../binding/tacmi/internal/json/Config.java | 55 +++ .../tacmi/internal/json/TACmiJsonHandler.java | 212 ++++++++++++ .../binding/tacmi/internal/json/obj/Data.java | 30 ++ .../tacmi/internal/json/obj/Header.java | 27 ++ .../binding/tacmi/internal/json/obj/IO.java | 46 +++ .../tacmi/internal/json/obj/JsonResponse.java | 31 ++ .../tacmi/internal/json/obj/Value.java | 226 ++++++++++++ .../resources/OH-INF/i18n/tacmi.properties | 14 + .../resources/OH-INF/thing/thing-types.xml | 43 +++ .../tacmi/internal/json/obj/test/DeSer.java | 52 +++ .../src/test/resources/ex.json | 324 ++++++++++++++++++ 13 files changed, 1066 insertions(+), 1 deletion(-) create mode 100644 bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/Config.java create mode 100644 bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java create mode 100644 bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Data.java create mode 100644 bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Header.java create mode 100644 bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/IO.java create mode 100644 bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/JsonResponse.java create mode 100644 bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Value.java create mode 100644 bundles/org.openhab.binding.tacmi/src/test/java/org/openhab/binding/tacmi/internal/json/obj/test/DeSer.java create mode 100644 bundles/org.openhab.binding.tacmi/src/test/resources/ex.json diff --git a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/TACmiBindingConstants.java b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/TACmiBindingConstants.java index d886aa9c288e8..1c5fb15ee1d95 100644 --- a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/TACmiBindingConstants.java +++ b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/TACmiBindingConstants.java @@ -31,6 +31,7 @@ public class TACmiBindingConstants { public static final ThingTypeUID THING_TYPE_CMI = new ThingTypeUID(BINDING_ID, "cmi"); public static final ThingTypeUID THING_TYPE_COE_BRIDGE = new ThingTypeUID(BINDING_ID, "coe-bridge"); public static final ThingTypeUID THING_TYPE_CMI_SCHEMA = new ThingTypeUID(BINDING_ID, "cmiSchema"); + public static final ThingTypeUID THING_TYPE_CMI_JSON = new ThingTypeUID(BINDING_ID, "cmiJSON"); public static final String CONFIG_DESCRIPTION_API_SCHEMA_DEFAULTS = "channel-type:tacmi:schemaApiDefaults"; diff --git a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/TACmiHandlerFactory.java b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/TACmiHandlerFactory.java index d3b27398aada1..93af255fe10af 100644 --- a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/TACmiHandlerFactory.java +++ b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/TACmiHandlerFactory.java @@ -24,6 +24,7 @@ import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.tacmi.internal.coe.TACmiCoEBridgeHandler; import org.openhab.binding.tacmi.internal.coe.TACmiHandler; +import org.openhab.binding.tacmi.internal.json.TACmiJsonHandler; import org.openhab.binding.tacmi.internal.schema.TACmiSchemaHandler; import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.thing.Bridge; @@ -47,7 +48,8 @@ public class TACmiHandlerFactory extends BaseThingHandlerFactory { private static final Set SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet( - Stream.of(THING_TYPE_CMI, THING_TYPE_COE_BRIDGE, THING_TYPE_CMI_SCHEMA).collect(Collectors.toSet())); + Stream.of(THING_TYPE_CMI, THING_TYPE_COE_BRIDGE, THING_TYPE_CMI_SCHEMA, THING_TYPE_CMI_JSON) + .collect(Collectors.toSet())); private final HttpClient httpClient; private final TACmiChannelTypeProvider channelTypeProvider; @@ -74,6 +76,8 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { return new TACmiCoEBridgeHandler((Bridge) thing); } else if (THING_TYPE_CMI_SCHEMA.equals(thingTypeUID)) { return new TACmiSchemaHandler(thing, httpClient, channelTypeProvider); + } else if (THING_TYPE_CMI_JSON.equals(thingTypeUID)) { + return new TACmiJsonHandler(thing, httpClient); } return null; diff --git a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/Config.java b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/Config.java new file mode 100644 index 0000000000000..ffad394330194 --- /dev/null +++ b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/Config.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.tacmi.internal.json; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Class holding the configuration of the TA Json binding. + * + * @author Moritz 'Morty' Strübe - Initial contribution + * + */ +@NonNullByDefault +public class Config { + + /** + * host address of the C.M.I. + */ + public String host = ""; + + /** + * Username + */ + public String username = ""; + + /** + * Password + */ + public String password = ""; + + /** + * Node-ID + */ + public Integer nodeId = 1; + + /** + * Json-Params + */ + public String params = "I,O,SG"; + + /** + * API page poll interval + */ + public int pollInterval = 60; +} diff --git a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java new file mode 100644 index 0000000000000..7fdb77b079892 --- /dev/null +++ b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java @@ -0,0 +1,212 @@ +/** + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.tacmi.internal.json; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Base64; +import java.util.Collection; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.function.BiFunction; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +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.json.obj.IO; +import org.openhab.binding.tacmi.internal.json.obj.JsonResponse; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.types.Command; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * Handler for the TA Json bindung + * + * @author Moritz 'Morty' Strübe - Initial contribution + * + */ +@NonNullByDefault +public class TACmiJsonHandler extends BaseThingHandler { + + private final Logger logger = LoggerFactory.getLogger(TACmiJsonHandler.class); + private HttpClient httpClient; + @SuppressWarnings("unused") + private @Nullable String authHeader; + private @Nullable String url; + private @Nullable ScheduledFuture scheduledFuture; + private int pollInterval = 60; + + public TACmiJsonHandler(Thing thing, HttpClient httpClient) { + super(thing); + this.httpClient = httpClient; + } + + @Override + public void initialize() { + + final Config config = getConfigAs(Config.class); + + if (config.host.isBlank()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No host configured!"); + return; + } + if (config.username.isBlank()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No username configured!"); + return; + } + if (config.password.isBlank()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No password configured!"); + return; + } + if (config.params.isBlank()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No params configured!"); + return; + } + + if (config.nodeId == 0) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "NodeId not configured!"); + return; + } + + if (config.pollInterval < 10) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Poll interval to short"); + return; + } + + updateStatus(ThingStatus.UNKNOWN); + + this.authHeader = "Basic " + Base64.getEncoder() + .encodeToString((config.username + ":" + config.password).getBytes(StandardCharsets.ISO_8859_1)); + + this.url = "http://" + config.host + "/INCLUDE/api.cgi?" + "jsonnode=" + config.nodeId + "&jsonparam=" + + config.params; + + logger.debug("URL: {}", this.url); + + this.pollInterval = config.pollInterval; + + // we want to trigger the initial refresh 'at once' + this.scheduledFuture = scheduler.scheduleWithFixedDelay(this::refreshData, 0, pollInterval, TimeUnit.SECONDS); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + // We do not support commands + } + + @Override + public void dispose() { + final ScheduledFuture scheduledFuture = this.scheduledFuture; + if (scheduledFuture != null) { + scheduledFuture.cancel(true); + this.scheduledFuture = null; + } + super.dispose(); + } + + private void refreshData() { + try { + final Request req = httpClient.newRequest(this.url).method(HttpMethod.GET).timeout(10000, + TimeUnit.MILLISECONDS); + req.header(HttpHeader.ACCEPT_LANGUAGE, "en"); + final String authH = this.authHeader; + if (authH != null) { + req.header(HttpHeader.AUTHORIZATION, authH); + } + + final ContentResponse httpResponse = req.send(); + if (httpResponse.getStatus() != 200) { + logger.warn("Error requesting update {} / {} \n{}", httpResponse.getStatus(), httpResponse.getReason(), + httpResponse.getContentAsString()); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); + return; + } + logger.trace("Reply:\n{}", httpResponse.getContentAsString()); + Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create(); + JsonResponse jsonResponse = gson.fromJson(httpResponse.getContentAsString(), JsonResponse.class); + if (jsonResponse == null) { + logger.warn("Response is Empty"); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); + return; + } + if (jsonResponse.statusCode != 0) { + logger.error("Response Error: {} ({})", jsonResponse.statusCode, jsonResponse.status); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, jsonResponse.status); + return; + } + updateChannels(jsonResponse); + updateStatus(ThingStatus.ONLINE); + } catch (final InterruptedException e) { + // binding shutdown is in progress + updateStatus(ThingStatus.OFFLINE); + } catch (final Exception e) { + logger.trace("Exception:\n{}", e.getMessage()); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); + } + } + + private void updateChannels(JsonResponse jsonResponse) throws Exception { + final var allChans = new ArrayList(); + + BiFunction, String, Boolean> chanhandler = (jsonArray, prefix) -> { + var channelChanged = false; + for (var inputItem : jsonArray) { + var name = prefix.charAt(0) + inputItem.number.toString(); + final var type = inputItem.getType(); + final var channelType = inputItem.getChannelType(); + Channel channel = this.getThing().getChannel(name); + final var currentChannelType = channel != null ? channel.getChannelTypeUID() : null; + // The null checker does not detect that if channel is null, currentChannelType is also null. + if (channel == null || currentChannelType == null || !currentChannelType.equals(channelType)) { + logger.debug("Creating / updating channel {} of type {} for '{}'", name, type, inputItem.getDesc()); + ChannelUID channelUID = new ChannelUID(this.getThing().getUID(), name); + ChannelBuilder channelBuilder = ChannelBuilder.create(channelUID, type); + channelBuilder.withLabel(prefix + " " + inputItem.number.toString()); + channelBuilder.withDescription(inputItem.getDesc()); + channelBuilder.withType(channelType); + channel = channelBuilder.build(); + channelChanged = true; + } + allChans.add(channel); + updateState(channel.getUID(), inputItem.getState()); + } + return channelChanged; + }; + + boolean modified = false; + modified |= chanhandler.apply(jsonResponse.data.inputs, "Input"); + modified |= chanhandler.apply(jsonResponse.data.outputs, "Output"); + modified |= chanhandler.apply(jsonResponse.data.general, "Global"); + + if (modified) { + final var eThing = editThing(); + eThing.withChannels(allChans); + updateThing(eThing.build()); + } + } +} diff --git a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Data.java b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Data.java new file mode 100644 index 0000000000000..7acd3ece1c45d --- /dev/null +++ b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Data.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.tacmi.internal.json.obj; + +import java.util.Collection; +import java.util.Collections; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Class holding the Data JSON element + * + * @author Moritz 'Morty' Strübe - Initial contribution + */ +@NonNullByDefault +public class Data { + public Collection inputs = Collections.emptyList(); + public Collection outputs = Collections.emptyList(); + public Collection general = Collections.emptyList(); +} diff --git a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Header.java b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Header.java new file mode 100644 index 0000000000000..81df5d80d38b7 --- /dev/null +++ b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Header.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.tacmi.internal.json.obj; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Class holding the Header JSON element + * + * @author Moritz 'Morty' Strübe - Initial contribution + */ +@NonNullByDefault +public class Header { + public Integer version = -1; + public String device = ""; + public Integer timestamp = 0; +} diff --git a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/IO.java b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/IO.java new file mode 100644 index 0000000000000..5ede5328bb12b --- /dev/null +++ b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/IO.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.tacmi.internal.json.obj; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.thing.type.ChannelTypeUID; +import org.openhab.core.types.State; + +/** + * Class holding the IO JSON element + * + * @author Moritz 'Morty' Strübe - Initial contribution + */ +@NonNullByDefault +public class IO { + public Integer number = 0; + public String ad = ""; + public Value value = new Value(); + + public @Nullable String getType() { + return value.getType(); + } + + public State getState() { + return value.getState(); + } + + public String getDesc() { + return value.getDesc(); + } + + public @Nullable ChannelTypeUID getChannelType() { + return value.getChannelType(); + } +} diff --git a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/JsonResponse.java b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/JsonResponse.java new file mode 100644 index 0000000000000..236902c21b28a --- /dev/null +++ b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/JsonResponse.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.tacmi.internal.json.obj; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +import com.google.gson.annotations.SerializedName; + +/** + * Class holding the Data JSON element + * + * @author Moritz 'Morty' Strübe - Initial contribution + */ +@NonNullByDefault +public class JsonResponse { + public Header header = new Header(); + public Data data = new Data(); + public String status = "Not Set (JSON)"; + @SerializedName("Status code") + public Integer statusCode = -7337; +} diff --git a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Value.java b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Value.java new file mode 100644 index 0000000000000..bdda71dee850b --- /dev/null +++ b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Value.java @@ -0,0 +1,226 @@ +/** + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.tacmi.internal.json.obj; + +import java.math.BigInteger; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.measure.Unit; +import javax.measure.quantity.ElectricCurrent; +import javax.measure.quantity.Length; +import javax.measure.quantity.Mass; +import javax.measure.quantity.Power; +import javax.measure.quantity.Speed; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.tacmi.internal.TACmiBindingConstants; +import org.openhab.core.library.dimension.Density; +import org.openhab.core.library.dimension.VolumetricFlowRate; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.MetricPrefix; +import org.openhab.core.library.unit.SIUnits; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.type.ChannelTypeUID; +import org.openhab.core.types.State; + +import tech.units.indriya.format.SimpleUnitFormat; +import tech.units.indriya.function.MultiplyConverter; +import tech.units.indriya.unit.ProductUnit; +import tech.units.indriya.unit.TransformedUnit; + +/** + * Class holding the Value JSON element + * + * @author Moritz 'Morty' Strübe - Initial contribution + */ +@NonNullByDefault +public class Value { + public double value = 0; + public String unit = "-1"; + private @Nullable TypeInfo typeInfo = null; + + // Always return a "valid" type info + // This simplifies the rest of the code, as less error checking is needed. + private TypeInfo getTypeInfo() { + // Need a local variable to satisfy null-checks + var rv = typeInfo; + if (rv != null) { + return rv; + } + + try { + rv = TYPE_MAP.get(Integer.parseInt(unit)); + } catch (Exception e) { + } + if (rv == null) { + rv = new TypeInfo("Undefined: " + unit, "Number", null); + } + typeInfo = rv; + return rv; + } + + public String getDesc() { + return getTypeInfo().desc; + } + + public @Nullable String getType() { + return getTypeInfo().unitname; + } + + public ChannelTypeUID getChannelType() { + final var ti = getTypeInfo(); + if (!ti.unitname.startsWith("Number")) { + return TACmiBindingConstants.CHANNEL_TYPE_SCHEME_STATE_RO_UID; + } + return TACmiBindingConstants.CHANNEL_TYPE_SCHEME_NUMERIC_RO_UID; + } + + public State getState() { + final var ti = getTypeInfo(); + if (ti.unitname.startsWith("Switch")) { + return (value != 0) ? OnOffType.ON : OnOffType.OFF; + } + final var unit = ti.unit; + if (unit != null) { + return new QuantityType<>(value, unit); + } + return new DecimalType(Double.valueOf(value)); + } + + static class TypeInfo { + public final String desc; + public final String unitname; + public final @Nullable Unit<@NonNull ?> unit; + + public TypeInfo(String desc, String unitname, @Nullable Unit<@NonNull ?> unit) { + this.desc = desc; + this.unitname = unitname; + this.unit = unit; + } + } + + static final Unit LITRE_PER_HOUR = new ProductUnit<>( + tech.units.indriya.unit.Units.LITRE.divide(tech.units.indriya.unit.Units.HOUR)); + static final Unit LITRE_PER_DAY = new ProductUnit<>( + tech.units.indriya.unit.Units.LITRE.divide(tech.units.indriya.unit.Units.DAY)); + static final Unit KILOWATT = MetricPrefix.KILO(Units.WATT); + static final Unit MILLIAMPERE = MetricPrefix.MILLI(Units.AMPERE); + static final Unit KILOOHM = MetricPrefix.KILO(Units.WATT); + static final Unit KILOMETRE = MetricPrefix.KILO(SIUnits.METRE); + static final Unit CENTIMETRE = MetricPrefix.CENTI(SIUnits.METRE); + static final Unit MILLIMETRE = MetricPrefix.MILLI(SIUnits.METRE); + static final Unit MILLIMETRE_PER_MINUTE = new TransformedUnit<>("mm/min", Units.MILLIMETRE_PER_HOUR, + MultiplyConverter.ofRational(BigInteger.ONE, BigInteger.valueOf(60))); + static final Unit MILLIMETRE_PER_DAY = new TransformedUnit<>("mm/d", Units.MILLIMETRE_PER_HOUR, + MultiplyConverter.ofRational(BigInteger.valueOf(24), BigInteger.ONE)); + static final Unit GRAM_PER_CUBICMETRE = new TransformedUnit<>("g/m³", Units.KILOGRAM_PER_CUBICMETRE, + MultiplyConverter.ofRational(BigInteger.ONE, BigInteger.valueOf(1000))); + static final Unit TONNE = new TransformedUnit<>("t", SIUnits.KILOGRAM, + MultiplyConverter.ofRational(BigInteger.valueOf(1000), BigInteger.ONE)); + static final Unit GRAMM = new TransformedUnit<>("g", SIUnits.KILOGRAM, + MultiplyConverter.ofRational(BigInteger.ONE, BigInteger.valueOf(1000))); + + static { + SimpleUnitFormat.getInstance().label(LITRE_PER_HOUR, "l/h"); + SimpleUnitFormat.getInstance().label(LITRE_PER_DAY, "l/d"); + SimpleUnitFormat.getInstance().label(KILOWATT, "kW"); + SimpleUnitFormat.getInstance().label(MILLIAMPERE, "mA"); + SimpleUnitFormat.getInstance().label(KILOOHM, "kΩ"); + SimpleUnitFormat.getInstance().label(KILOMETRE, "km"); + SimpleUnitFormat.getInstance().label(CENTIMETRE, "cm"); + SimpleUnitFormat.getInstance().label(MILLIMETRE, "mm"); + SimpleUnitFormat.getInstance().label(MILLIMETRE_PER_MINUTE, "mm/min"); + SimpleUnitFormat.getInstance().label(MILLIMETRE_PER_DAY, "mm/d"); + SimpleUnitFormat.getInstance().label(GRAM_PER_CUBICMETRE, "g/m³"); + SimpleUnitFormat.getInstance().label(TONNE, "t"); + SimpleUnitFormat.getInstance().label(GRAMM, "g"); + } + + static final Map TYPE_MAP; + static { + // @formatter:off + // -1 is reserved to return null + Map lMap = new HashMap(); + lMap.put(0, new TypeInfo("Unknown", "Number", null)); + lMap.put(1, new TypeInfo("°C", "Number:Temperature", SIUnits.CELSIUS)); + lMap.put(2, new TypeInfo("W/m²", "Number:Intensity", Units.WATT_HOUR_PER_SQUARE_METRE)); + lMap.put(3, new TypeInfo("l/h", "Number:VolumetricFlowRate", LITRE_PER_HOUR)); + lMap.put(4, new TypeInfo("sec", "Number:Time", Units.SECOND)); + lMap.put(5, new TypeInfo("min", "Number:Time", Units.MINUTE)); + lMap.put(6, new TypeInfo("l/Imp", "Number", null)); + lMap.put(7, new TypeInfo("K", "Number:Temperature", Units.KELVIN)); + lMap.put(8, new TypeInfo("%", "Number:Dimensionless", Units.PERCENT)); + lMap.put(10, new TypeInfo("kW", "Number:Power", KILOWATT)); + lMap.put(11, new TypeInfo("kWh", "Number:Energy", Units.KILOWATT_HOUR)); + lMap.put(12, new TypeInfo("MWh", "Number:Energy", Units.MEGAWATT_HOUR)); + lMap.put(13, new TypeInfo("V", "Number:ElectricPotential", Units.VOLT)); + lMap.put(14, new TypeInfo("mA", "Number:ElectricCurrent", MILLIAMPERE)); + lMap.put(15, new TypeInfo("hr", "Number:Duration", Units.HOUR)); + lMap.put(16, new TypeInfo("Days", "Number:Duration", Units.DAY)); + lMap.put(17, new TypeInfo("Imp", "Number", null)); + lMap.put(18, new TypeInfo("kΩ", "Number:ElectricResistance", KILOOHM)); + lMap.put(19, new TypeInfo("l", "Number:Volume", Units.LITRE)); + lMap.put(20, new TypeInfo("km/h", "Number:Speed", SIUnits.KILOMETRE_PER_HOUR)); + lMap.put(21, new TypeInfo("Hz", "Number:Frequency", Units.HERTZ)); + lMap.put(22, new TypeInfo("l/min", "Number:VolumetricFlowRate", Units.LITRE_PER_MINUTE)); + lMap.put(23, new TypeInfo("bar", "Number:Pressure", Units.BAR)); + lMap.put(24, new TypeInfo("Unknown", "Number", null)); + lMap.put(25, new TypeInfo("km", "Number:Length", KILOMETRE)); + lMap.put(26, new TypeInfo("m", "Number:Length", SIUnits.METRE)); + lMap.put(27, new TypeInfo("mm", "Number:Length", MILLIMETRE)); + lMap.put(28, new TypeInfo("m³", "Number:Volume", SIUnits.CUBIC_METRE)); + lMap.put(35, new TypeInfo("l/d", "Number:VolumetricFlowRate", LITRE_PER_DAY)); + lMap.put(36, new TypeInfo("m/s", "Number:Speed", Units.METRE_PER_SECOND)); + lMap.put(37, new TypeInfo("m³/min", "Number:VolumetricFlowRate", Units.CUBICMETRE_PER_MINUTE)); + lMap.put(38, new TypeInfo("m³/h", "Number:VolumetricFlowRate", Units.CUBICMETRE_PER_HOUR)); + lMap.put(39, new TypeInfo("m³/d", "Number:VolumetricFlowRate", Units.CUBICMETRE_PER_DAY)); + lMap.put(40, new TypeInfo("mm/min", "Number:Speed", MILLIMETRE_PER_MINUTE)); + lMap.put(41, new TypeInfo("mm/h", "Number:Speed", Units.MILLIMETRE_PER_HOUR)); + lMap.put(42, new TypeInfo("mm/d", "Number:Speed", MILLIMETRE_PER_DAY)); + lMap.put(43, new TypeInfo("ON/OFF", "Switch", null)); + lMap.put(44, new TypeInfo("NO/YES", "Switch", null)); + lMap.put(46, new TypeInfo("°C", "Number:Temperature", SIUnits.CELSIUS)); + lMap.put(50, new TypeInfo("€", "Number", null)); + lMap.put(51, new TypeInfo("$", "Number", null)); + lMap.put(52, new TypeInfo("g/m³", "Number:Density", GRAM_PER_CUBICMETRE)); + lMap.put(53, new TypeInfo("Unknown", "Number", null)); + lMap.put(54, new TypeInfo("°", "Number:Angle", Units.DEGREE_ANGLE)); + lMap.put(56, new TypeInfo("°", "Number:Angle", Units.DEGREE_ANGLE)); + lMap.put(57, new TypeInfo("sec", "Number:Duration", Units.SECOND)); + lMap.put(58, new TypeInfo("Unknown", "Number", null)); + lMap.put(59, new TypeInfo("%", "Number:Dimensionless", Units.PERCENT)); + lMap.put(60, new TypeInfo("h", "Number:Time", Units.HOUR)); + lMap.put(63, new TypeInfo("A", "Number:Current", Units.AMPERE)); + lMap.put(65, new TypeInfo("mbar", "Number:Pressure", Units.MILLIBAR)); + lMap.put(66, new TypeInfo("Pa", "Number:Pressure", SIUnits.PASCAL)); + lMap.put(67, new TypeInfo("ppm", "Number:Dimensionless", Units.PARTS_PER_MILLION)); + lMap.put(68, new TypeInfo("Unknown", "Number", null)); + lMap.put(69, new TypeInfo("W", "Number:Power", Units.WATT)); + lMap.put(70, new TypeInfo("t", "Number:Mass", TONNE)); + lMap.put(71, new TypeInfo("kg", "Number:Mass", SIUnits.KILOGRAM)); + lMap.put(72, new TypeInfo("g", "Number:Mass", GRAMM)); + lMap.put(73, new TypeInfo("cm", "Number:Length", CENTIMETRE)); + lMap.put(74, new TypeInfo("K", "Number:Temperature", Units.KELVIN)); + lMap.put(75, new TypeInfo("lx", "Number:Illuminance", Units.LUX)); + // I assume this should be Bq/m³, but I'll stick to the JSon documentation + lMap.put(76, new TypeInfo("Bg/m³", "Number:Illuminance", Units.BECQUEREL_PER_CUBIC_METRE)); + // @formatter:on + TYPE_MAP = Collections.unmodifiableMap(lMap); + } +} diff --git a/bundles/org.openhab.binding.tacmi/src/main/resources/OH-INF/i18n/tacmi.properties b/bundles/org.openhab.binding.tacmi/src/main/resources/OH-INF/i18n/tacmi.properties index 9ac3589e482aa..4c6ea180efbeb 100644 --- a/bundles/org.openhab.binding.tacmi/src/main/resources/OH-INF/i18n/tacmi.properties +++ b/bundles/org.openhab.binding.tacmi/src/main/resources/OH-INF/i18n/tacmi.properties @@ -7,6 +7,8 @@ addon.tacmi.description = This is the binding for TA C.M.I. thing-type.tacmi.cmi.label = TA C.M.I. CoE Connection thing-type.tacmi.cmi.description = CoE Communication to the "Technische Alternative C.M.I. Control and Monitoring Interface" +thing-type.tacmi.cmiJSON.label = TA C.M.I. JSON API Connection +thing-type.tacmi.cmiJSON.description = Communication via the "JSONAPI" on a "Technische Alternative C.M.I. Control and Monitoring Interface" thing-type.tacmi.cmiSchema.label = TA C.M.I. Schema API Connection thing-type.tacmi.cmiSchema.description = Communication to a special "API" schema page on a "Technische Alternative C.M.I. Control and Monitoring Interface" thing-type.tacmi.coe-bridge.label = TA C.M.I. CoE Bridge @@ -18,6 +20,18 @@ thing-type.config.tacmi.cmi.host.label = C.M.I. IP Address thing-type.config.tacmi.cmi.host.description = Host name of IP address of the CMI thing-type.config.tacmi.cmi.node.label = Node thing-type.config.tacmi.cmi.node.description = The CoE / CAN Node number openHAB should represent +thing-type.config.tacmi.cmiJSON.host.label = C.M.I. IP Address +thing-type.config.tacmi.cmiJSON.host.description = Host name or IP address of the C.M.I. +thing-type.config.tacmi.cmiJSON.nodeId.label = Node Id +thing-type.config.tacmi.cmiJSON.nodeId.description = The node ID of the device you want to monitor (CMI → CAN-Bus) +thing-type.config.tacmi.cmiJSON.params.label = API-Parameters +thing-type.config.tacmi.cmiJSON.params.description = The params to query. E.g. I,O,Sg (See the API documentation for details)
Possible options are: Inputs (I), Outputs (O), General (Sg), Logging Analog (La), Logging Digital (Ld) +thing-type.config.tacmi.cmiJSON.password.label = Password +thing-type.config.tacmi.cmiJSON.password.description = Password for authentication on the C.M.I. +thing-type.config.tacmi.cmiJSON.pollInterval.label = Poll Interval +thing-type.config.tacmi.cmiJSON.pollInterval.description = Poll interval in seconds. The documentation suggests 60s, but less is possible. +thing-type.config.tacmi.cmiJSON.username.label = User Name +thing-type.config.tacmi.cmiJSON.username.description = User name for authentication on the C.M.I. thing-type.config.tacmi.cmiSchema.host.label = C.M.I. IP Address thing-type.config.tacmi.cmiSchema.host.description = Host name or IP address of the C.M.I. thing-type.config.tacmi.cmiSchema.password.label = Password diff --git a/bundles/org.openhab.binding.tacmi/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.tacmi/src/main/resources/OH-INF/thing/thing-types.xml index 6a4277cbb9e73..cd4c1950944af 100644 --- a/bundles/org.openhab.binding.tacmi/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.tacmi/src/main/resources/OH-INF/thing/thing-types.xml @@ -180,4 +180,47 @@ + + + + + + Communication via the "JSONAPI" on a "Technische Alternative C.M.I. Control and Monitoring + Interface" + + + + + Host name or IP address of the C.M.I. + network-address + + + + User name for authentication on the C.M.I. + + + + Password for authentication on the C.M.I. + password + + + + The node ID of the device you want to monitor (CMI → CAN-Bus) + + + + API documentation for details)
+ Possible options are: Inputs (I), Outputs (O), General (Sg), Logging Analog (La), Logging Digital (Ld) ]]> +
+ I,O,Sg +
+ + + Poll interval in seconds. The documentation suggests 60s, but less is possible. + 60 + true + +
+
+ diff --git a/bundles/org.openhab.binding.tacmi/src/test/java/org/openhab/binding/tacmi/internal/json/obj/test/DeSer.java b/bundles/org.openhab.binding.tacmi/src/test/java/org/openhab/binding/tacmi/internal/json/obj/test/DeSer.java new file mode 100644 index 0000000000000..78ab10e1a0dbd --- /dev/null +++ b/bundles/org.openhab.binding.tacmi/src/test/java/org/openhab/binding/tacmi/internal/json/obj/test/DeSer.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.tacmi.internal.json.obj.test; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.FileReader; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.openhab.binding.tacmi.internal.json.obj.IO; +import org.openhab.binding.tacmi.internal.json.obj.JsonResponse; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.stream.JsonReader; + +/** + * Test the GSON object mapper + * + * @author Moritz 'Morty' Strübe - Initial contribution + * + */ +@NonNullByDefault +class DeSer { + @Test + void test() { + Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create(); + JsonReader reader; + reader = assertDoesNotThrow(() -> { + return new JsonReader(new FileReader("src/test/resources/ex.json")); + }); + + JsonResponse resp = gson.fromJson(reader, JsonResponse.class); + + assertEquals(0, resp.statusCode); + assertEquals(5, resp.header.version); + var inp = resp.data.inputs.toArray(new IO[0]); + assertEquals(50.2, inp[0].value.value); + } +} diff --git a/bundles/org.openhab.binding.tacmi/src/test/resources/ex.json b/bundles/org.openhab.binding.tacmi/src/test/resources/ex.json new file mode 100644 index 0000000000000..8516b1edb40b9 --- /dev/null +++ b/bundles/org.openhab.binding.tacmi/src/test/resources/ex.json @@ -0,0 +1,324 @@ +{ + "Header": { + "Version": 5, + "Device": "87", + "Timestamp": 1664795104 + }, + "Data": { + "Inputs": [ + { + "Number": 1, + "AD": "A", + "Value": { + "Value": 50.2, + "Unit": "1" + } + }, + { + "Number": 2, + "AD": "A", + "Value": { + "Value": 45.6, + "Unit": "1" + } + }, + { + "Number": 3, + "AD": "A", + "Value": { + "Value": 39.4, + "Unit": "1" + } + }, + { + "Number": 4, + "AD": "A", + "Value": { + "Value": 9999.9, + "Unit": "1" + } + }, + { + "Number": 5, + "AD": "A", + "Value": { + "Value": 12.2, + "Unit": "1" + } + }, + { + "Number": 6, + "AD": "A", + "Value": { + "Value": 29.3, + "Unit": "1" + } + }, + { + "Number": 7, + "AD": "A", + "Value": { + "Value": 39.2, + "Unit": "1" + } + }, + { + "Number": 8, + "AD": "A", + "Value": { + "Value": 48.8, + "Unit": "1" + } + }, + { + "Number": 9, + "AD": "A", + "Value": { + "Value": 40.4, + "Unit": "1" + } + }, + { + "Number": 10, + "AD": "A", + "Value": { + "Value": 9999.9, + "Unit": "1" + } + }, + { + "Number": 11, + "AD": "D", + "Value": { + "Value": 0, + "Unit": "43" + } + }, + { + "Number": 12, + "AD": "D", + "Value": { + "Value": 0, + "Unit": "43" + } + }, + { + "Number": 13, + "AD": "D", + "Value": { + "Value": 0, + "Unit": "43" + } + }, + { + "Number": 14, + "AD": "D", + "Value": { + "Value": 0, + "Unit": "43" + } + }, + { + "Number": 15, + "AD": "D", + "Value": { + "Value": 0, + "Unit": "43" + } + }, + { + "Number": 16, + "AD": "D", + "Value": { + "Value": 1, + "Unit": "43" + } + } + ], + "Outputs": [ + { + "Number": 5, + "AD": "D", + "Value": { + "Value": 0, + "Unit": "43" + } + }, + { + "Number": 8, + "AD": "D", + "Value": { + "Value": 1, + "Unit": "43" + } + }, + { + "Number": 9, + "AD": "D", + "Value": { + "Value": 1, + "Unit": "43" + } + }, + { + "Number": 10, + "AD": "D", + "Value": { + "Value": 0, + "Unit": "43" + } + }, + { + "Number": 11, + "AD": "D", + "Value": { + "Value": 0, + "Unit": "43" + } + }, + { + "Number": 12, + "AD": "D", + "Value": { + "Value": 0, + "Unit": "43" + } + }, + { + "Number": 13, + "AD": "A", + "Value": { + "State": 0, + "Value": 0.00, + "Unit": "13" + } + }, + { + "Number": 14, + "AD": "A", + "Value": { + "State": 0, + "Value": 0.00, + "Unit": "13" + } + }, + { + "Number": 15, + "AD": "A", + "Value": { + "State": 0, + "Value": 0.00, + "Unit": "13" + } + } + ], + "General": [ + { + "Number": 1, + "AD": "D", + "Value": { + "Value": 0, + "Unit": "43" + } + }, + { + "Number": 2, + "AD": "D", + "Value": { + "Value": 0, + "Unit": "44" + } + }, + { + "Number": 3, + "AD": "D", + "Value": { + "Value": 0, + "Unit": "44" + } + }, + { + "Number": 4, + "AD": "D", + "Value": { + "Value": 0, + "Unit": "44" + } + }, + { + "Number": 5, + "AD": "D", + "Value": { + "Value": 0, + "Unit": "44" + } + }, + { + "Number": 6, + "AD": "D", + "Value": { + "Value": 0, + "Unit": "44" + } + }, + { + "Number": 7, + "AD": "A", + "Value": { + "Value": 50.90, + "Unit": "21" + } + }, + { + "Number": 8, + "AD": "A", + "Value": { + "Value": 1, + "Unit": "0" + } + }, + { + "Number": 9, + "AD": "D", + "Value": { + "Value": 1, + "Unit": "44" + } + }, + { + "Number": 10, + "AD": "D", + "Value": { + "Value": 0, + "Unit": "43" + } + }, + { + "Number": 11, + "AD": "D", + "Value": { + "Value": 0, + "Unit": "43" + } + }, + { + "Number": 12, + "AD": "D", + "Value": { + "Value": 0, + "Unit": "43" + } + }, + { + "Number": 13, + "AD": "D", + "Value": { + "Value": 0, + "Unit": "43" + } + } + ] + }, + "Status": "OK", + "Status code": 0 +} \ No newline at end of file From 161e75f91c750c8476b2ea470b7ea0772407797a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20=27Morty=27=20Str=C3=BCbe?= Date: Fri, 30 Dec 2022 18:59:47 +0100 Subject: [PATCH 02/17] [tacmi] Add pattern to schema-numeric-ro channel-type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Moritz 'Morty' Strübe --- .../src/main/resources/OH-INF/thing/thing-types.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.tacmi/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.tacmi/src/main/resources/OH-INF/thing/thing-types.xml index cd4c1950944af..8750e2ba62fac 100644 --- a/bundles/org.openhab.binding.tacmi/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.tacmi/src/main/resources/OH-INF/thing/thing-types.xml @@ -163,7 +163,7 @@ Number A numeric value read from C.M.I. - + From 19a7b159f5cf4e621bc56e10f7525fb967e6d8fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20=27Morty=27=20Str=C3=BCbe?= Date: Sat, 4 Nov 2023 15:09:14 +0100 Subject: [PATCH 03/17] [tacmi] Make verify happy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Moritz 'Morty' Strübe --- .../src/main/resources/OH-INF/thing/thing-types.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bundles/org.openhab.binding.tacmi/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.tacmi/src/main/resources/OH-INF/thing/thing-types.xml index 8750e2ba62fac..b3bccbfce0e8a 100644 --- a/bundles/org.openhab.binding.tacmi/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.tacmi/src/main/resources/OH-INF/thing/thing-types.xml @@ -151,27 +151,23 @@ An On/Off state read from C.M.I. - Switch A modifiable On/Off state read from C.M.I. - Number A numeric value read from C.M.I. - String A state value read from C.M.I. - DateTime From 9f661c0d75abdcc4e557e041a01f8ec23a6e811b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20=27Morty=27=20Str=C3=BCbe?= Date: Sun, 5 Jan 2025 13:18:32 +0100 Subject: [PATCH 04/17] [tacmi] Reorganize documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reorganize the documentation into main topics and separate Schema and CoE sections (instead of intermingling the to topics). No modifications but minor adjustments to the headings were made. Signed-off-by: Moritz 'Morty' Strübe --- bundles/org.openhab.binding.tacmi/README.md | 139 ++++++++++---------- 1 file changed, 66 insertions(+), 73 deletions(-) diff --git a/bundles/org.openhab.binding.tacmi/README.md b/bundles/org.openhab.binding.tacmi/README.md index b1ac7cdbd6148..b2aa539df858f 100644 --- a/bundles/org.openhab.binding.tacmi/README.md +++ b/bundles/org.openhab.binding.tacmi/README.md @@ -22,7 +22,27 @@ Depending on what you want to achieve, either the "Schema API Page" or the CoE w As rough guidance: Anything you want to provide to the TA equipment it has to work / operate with the CoE might be better. If you plan things mainly for user interaction the "Schema API Page" might be better. -## Prerequisites +## Supported Bridge and Things + +- TA C.M.I. **Schema API Connection** - Thing \ +This thing reflecting one of our 'schema API page' as defined in the prerequisites. +This thing doesn't need the bridge. +Multiple of these pages on different C.M.I.'s could be defined within an openHAB instance. + +- TA C.M.I. **CoE Bridge** \ +In order to get the CAN over Ethernet (COE) envionment working a `coe-bridge` has to be created. +The bridge itself opens the UDP port 5441 for communication with the C.M.I. devices. +The bridge could be used for multiple C.M.I. devices. + +- TA C.M.I. **CoE Connection** - Thing \ +This thing reflects a connection to a node behind a specific C.M.I.. +This node could be every CAN-Capable device from TA which allows to define a CAN-Input. + +## Discovery + +Autodiscovering is not supported. We have to define the things manually. + +## Schema API ### Setting up the "Schema API Page" @@ -53,47 +73,7 @@ The first sample is a sensor reading, but also the 'operation mode' of a heating In this screenshot you also see the schema page id - the parenthesized number on the bottom page overview, in this sample 4. -### CoE Configuration - -#### Configure CAN outputs in TAPPS2 - -You need to configure CAN outputs in your Functional data on the UVR16x2. -This can be done by using the TAPPS2 application from TA. Follow the user guide on how to do this. - -#### Configure your CMI for CoE - -Now follow the User Guide of the CMI on how to setup CAN over Ethernet (COE). -Here you will map your outputs that you configured in the previous step. -This can be accomplished via the GUI on the CMI or via the coe.csv file. -As the target device you need to put the IP of your openHAB server. -Don’t forget to reboot the CMI after you uploaded the coe.csv file. - -## Supported Bridge and Things - -- TA C.M.I. schema API connection - Thing - -This thing reflecting one of our 'schema API page' as defined in the prerequisites. -This thing doesn't need the bridge. -Multiple of these pages on different C.M.I.'s could be defined within an openHAB instance. - -- TA C.M.I. CoE Bridge - -In order to get the CAN over Ethernet (COE) envionment working a `coe-bridge` has to be created. -The bridge itself opens the UDP port 5441 for communication with the C.M.I. devices. -The bridge could be used for multiple C.M.I. devices. - -- TA C.M.I. CoE Connection - Thing - -This thing reflects a connection to a node behind a specific C.M.I.. -This node could be every CAN-Capable device from TA which allows to define a CAN-Input. - -## Discovery - -Autodiscovering is not supported. We have to define the things manually. - -## Thing Configuration - -### TA C.M.I. schema API connection +### Connection Configuration The _TA C.M.I. Schema API Connection_ has to be manually configured. @@ -109,22 +89,7 @@ The thing has the following configuration parameters: This thing doesn't need a bridge. Multiple of these things for different C.M.I.'s could be defined within an openHAB instance. -### TA C.M.I. CoE Connection - -The _TA C.M.I. CoE Connection_ has to be manually configured. - -This thing reflects a connection to a node behind a specific C.M.I.. This node could be every CAN-Capable device from TA which allows to define a CAN-Input. - -| Parameter Label | Parameter ID | Description | Accepted values | -|-------------------------|-----------------|---------------------------------------------------------------------------------------------------------------|------------------------| -| C.M.I. IP Address | host | Host name or IP address of the C.M.I | host name or ip | -| Node | node | The CoE / CAN Node number openHAB should represent | 1-64 | - -The thing has no channels by default - they have to be added manually matching the configured inputs / outputs for the related CAN Node. Digital and Analog channels are supported. Please read TA's documentation related to the CAN-protocol - multiple analog (4) and digital (16) channels are combined so please be aware of this design limitation. - -## Channels - -### TA C.M.I. schema API connection +### TA C.M.I. schema API Channels The channels provided by this thing depend on the configuration of the "schema API page". All the channels are dynamically created to match it. @@ -142,8 +107,50 @@ The behavior in detail: - `On-Fetch` (`1`): This is the default for read-only values. This means the channel is updated every time the schema page is polled. Ideally for values you want to monitor and log into charts. - `On-Change` (`2`): When channel values can be changed via OH it is better to only update the channel when the value changes. The binding will cache the previous value and only send an update when it changes to the previous known value. This is especially useful if you intend to link other things (like i.e. Zigbee or Shelly switches) to the TA via OH that can be controlled by different sources. This prevents unintended toggles or even toggle-loops. +### Some additional hints and comments + +You might already have noticed that some state information is in German. +As I have set the `Accept-Language`-Http-Header to `en` for all request and found no other way setting a language for the schema pages I assume it is a lack of internationalization in the C.M.I. +You could circumvent this by creating map files to map things properly to your language. + +If you want to see the possible options of a multi-state field you could open the _schema API page_ with your web browser and click on the object. +A Popup with an option field will be shown showing all possible options, like in this screenshot: + +![screenshot-operation-mode-values](doc/images/operation-mode-values.png) + +Please be also aware that there are field having more 'state' values than options, i.E. a manual output override: It has 'Auto/On', 'Auto/Off', 'Manual/On', 'Manual/Off' as state, but only 'Auto', 'Manual/On' and 'Manual/Off' as updateable option. +You only set it to 'Auto' and the extension On/Off is added depending on the system's current state. + +## CoE + +### Configure CAN outputs in TAPPS2 + +You need to configure CAN outputs in your Functional data on the UVR16x2. +This can be done by using the TAPPS2 application from TA. Follow the user guide on how to do this. + +### Configure your CMI for CoE + +Now follow the User Guide of the CMI on how to setup CAN over Ethernet (COE). +Here you will map your outputs that you configured in the previous step. +This can be accomplished via the GUI on the CMI or via the coe.csv file. +As the target device you need to put the IP of your openHAB server. +Don’t forget to reboot the CMI after you uploaded the coe.csv file. + ### TA C.M.I. CoE Connection +The _TA C.M.I. CoE Connection_ has to be manually configured. + +This thing reflects a connection to a node behind a specific C.M.I.. This node could be every CAN-Capable device from TA which allows to define a CAN-Input. + +| Parameter Label | Parameter ID | Description | Accepted values | +|-------------------------|-----------------|---------------------------------------------------------------------------------------------------------------|------------------------| +| C.M.I. IP Address | host | Host name or IP address of the C.M.I | host name or ip | +| Node | node | The CoE / CAN Node number openHAB should represent | 1-64 | + +The thing has no channels by default - they have to be added manually matching the configured inputs / outputs for the related CAN Node. Digital and Analog channels are supported. Please read TA's documentation related to the CAN-protocol - multiple analog (4) and digital (16) channels are combined so please be aware of this design limitation. + +### CoE Connection Channels + Some comments on the CoE Connection and channel configuration: As you might already have taken notice when studying the TA's manual, there are always a multiple CoE-values updated within a single CoE-message. This is a design decision made by TA. @@ -212,7 +219,7 @@ The known measure types are: | 16 | dimensionless | [none] | use for multiplexers, etc | | 17.. | repeating again from 1, e.g 17==1, 18==2, ... | -## Full Example +## Full Example (CoE/Schema API) As there is no common configuration as everything depends on the configuration of the TA devices. So we just can provide some samples providing the basics so you can build the configuration matching your system. @@ -264,18 +271,4 @@ sitemap heatingTA label="heatingTA" } ``` -## Some additional hints and comments - -Some additional hints and comments: - -You might already have noticed that some state information is in German. -As I have set the `Accept-Language`-Http-Header to `en` for all request and found no other way setting a language for the schema pages I assume it is a lack of internationalization in the C.M.I. -You could circumvent this by creating map files to map things properly to your language. - -If you want to see the possible options of a multi-state field you could open the _schema API page_ with your web browser and click on the object. -A Popup with an option field will be shown showing all possible options, like in this screenshot: -![screenshot-operation-mode-values](doc/images/operation-mode-values.png) - -Please be also aware that there are field having more 'state' values than options, i.E. a manual output override: It has 'Auto/On', 'Auto/Off', 'Manual/On', 'Manual/Off' as state, but only 'Auto', 'Manual/On' and 'Manual/Off' as updateable option. -You only set it to 'Auto' and the extension On/Off is added depending on the system's current state. From 85394e9a2c4e3467526e945ae5da9aa546a109e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20=27Morty=27=20Str=C3=BCbe?= Date: Sun, 5 Jan 2025 13:32:51 +0100 Subject: [PATCH 05/17] [tacmi] Add JSON API documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add documentation on how to use the JSON API. Signed-off-by: Moritz 'Morty' Strübe --- bundles/org.openhab.binding.tacmi/README.md | 44 +++++++++++++++------ 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/bundles/org.openhab.binding.tacmi/README.md b/bundles/org.openhab.binding.tacmi/README.md index b2aa539df858f..98783be56831d 100644 --- a/bundles/org.openhab.binding.tacmi/README.md +++ b/bundles/org.openhab.binding.tacmi/README.md @@ -18,6 +18,11 @@ CoE (CAN over Ethernet) Connection - Send ON/OFF to digital CAN-inputs defined in TAPPS2 - Send numeric values to analog CAN-inputs defined in TAPPS2 +JSON API + +- Read selected data via the JSON API (inputs, outputs, logging). + See [API documentation](https://wiki.ta.co.at/C.M.I._JSON-API) for details + Depending on what you want to achieve, either the "Schema API Page" or the CoE way might be better. As rough guidance: Anything you want to provide to the TA equipment it has to work / operate with the CoE might be better. If you plan things mainly for user interaction the "Schema API Page" might be better. @@ -25,22 +30,23 @@ If you plan things mainly for user interaction the "Schema API Page" might be be ## Supported Bridge and Things - TA C.M.I. **Schema API Connection** - Thing \ -This thing reflecting one of our 'schema API page' as defined in the prerequisites. -This thing doesn't need the bridge. -Multiple of these pages on different C.M.I.'s could be defined within an openHAB instance. - + This thing reflecting one of our 'schema API page' as defined in the prerequisites. + This thing doesn't need the bridge. + Multiple of these pages on different C.M.I.'s could be defined within an openHAB instance. - TA C.M.I. **CoE Bridge** \ -In order to get the CAN over Ethernet (COE) envionment working a `coe-bridge` has to be created. -The bridge itself opens the UDP port 5441 for communication with the C.M.I. devices. -The bridge could be used for multiple C.M.I. devices. - + In order to get the CAN over Ethernet (COE) envionment working a `coe-bridge` has to be created. + The bridge itself opens the UDP port 5441 for communication with the C.M.I. devices. + The bridge could be used for multiple C.M.I. devices. - TA C.M.I. **CoE Connection** - Thing \ -This thing reflects a connection to a node behind a specific C.M.I.. -This node could be every CAN-Capable device from TA which allows to define a CAN-Input. + This thing reflects a connection to a node behind a specific C.M.I.. + This node could be every CAN-Capable device from TA which allows to define a CAN-Input. +- TA C.M.I. **JSON API Connection** - Thing \ + This is a thing connection that regularly polls the CMI using the JSON API. ## Discovery -Autodiscovering is not supported. We have to define the things manually. +Autodiscovering is not supported. +You have to define the things manually. ## Schema API @@ -271,4 +277,20 @@ sitemap heatingTA label="heatingTA" } ``` +## JSON API + +Before setting up the JSON API, it is worth reading the [API documention](https://wiki.ta.co.at/C.M.I._JSON-API). +Once configured, the exposed items should show up as channels. + +### Configuring the JSON API + +The _TA C.M.I. JSON API Connection_ has to be manually configured. +| Parameter Label | Parameter ID | Description | Accepted values | +| ----------------- | ------------ | ------------------------------------------------------------------------------- | ----------------------------- | +| C.M.I. IP Address | host | Host name or IP address of the C.M.I. | Any String | +| User name | username | User name for authentication on the C.M.I. | Any String | +| Password | password | Password for authentication on the C.M.I. | Any String | +| Node Id | nodeId | The node ID of the device you want to monitor (CMI → CAN-Bus) | An Integer that is not 0 | +| API-Parameters | params | The params to query. E.g. I,O,Sg (See the [API documentation](https://wiki.ta.co.at/C.M.I._JSON-API) for details)
Possible options are: Inputs (I), Outputs (O), General (Sg), Logging Analog (La), Logging Digital (Ld) | Any String | +| Poll Interval | pollInterval | Poll interval in seconds. The documentation suggests 60s, but less is possible. | An integer between 10 and 900 | From abf2323e01adc9401c32529b75fddb77bde5d7f6 Mon Sep 17 00:00:00 2001 From: lsiepel Date: Sat, 22 Mar 2025 23:09:49 +0100 Subject: [PATCH 06/17] Update bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java Signed-off-by: lsiepel --- .../openhab/binding/tacmi/internal/json/TACmiJsonHandler.java | 1 - 1 file changed, 1 deletion(-) diff --git a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java index 7fdb77b079892..37db5e69d25f4 100644 --- a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java +++ b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java @@ -68,7 +68,6 @@ public TACmiJsonHandler(Thing thing, HttpClient httpClient) { @Override public void initialize() { - final Config config = getConfigAs(Config.class); if (config.host.isBlank()) { From c404dfeeca358bb995b9792ca1524a0d58ee417a Mon Sep 17 00:00:00 2001 From: lsiepel Date: Sat, 22 Mar 2025 23:10:49 +0100 Subject: [PATCH 07/17] Update bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java Signed-off-by: lsiepel --- .../openhab/binding/tacmi/internal/json/TACmiJsonHandler.java | 1 - 1 file changed, 1 deletion(-) diff --git a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java index 37db5e69d25f4..464626f20a7b6 100644 --- a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java +++ b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java @@ -164,7 +164,6 @@ private void refreshData() { // binding shutdown is in progress updateStatus(ThingStatus.OFFLINE); } catch (final Exception e) { - logger.trace("Exception:\n{}", e.getMessage()); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); } } From bbd402229fd5b7c0564f0b6677a8cb26f71edcfb Mon Sep 17 00:00:00 2001 From: lsiepel Date: Sat, 22 Mar 2025 23:11:27 +0100 Subject: [PATCH 08/17] Update bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java Signed-off-by: lsiepel --- .../openhab/binding/tacmi/internal/json/TACmiJsonHandler.java | 1 - 1 file changed, 1 deletion(-) diff --git a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java index 464626f20a7b6..4f9ab4297797d 100644 --- a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java +++ b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java @@ -55,7 +55,6 @@ public class TACmiJsonHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(TACmiJsonHandler.class); private HttpClient httpClient; - @SuppressWarnings("unused") private @Nullable String authHeader; private @Nullable String url; private @Nullable ScheduledFuture scheduledFuture; From 501380ac345b2149ee5d7959d23425a392cafd73 Mon Sep 17 00:00:00 2001 From: lsiepel Date: Sat, 22 Mar 2025 23:12:10 +0100 Subject: [PATCH 09/17] Update bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java Signed-off-by: lsiepel --- .../openhab/binding/tacmi/internal/json/TACmiJsonHandler.java | 1 - 1 file changed, 1 deletion(-) diff --git a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java index 4f9ab4297797d..49010110f2eaa 100644 --- a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java +++ b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java @@ -153,7 +153,6 @@ private void refreshData() { return; } if (jsonResponse.statusCode != 0) { - logger.error("Response Error: {} ({})", jsonResponse.statusCode, jsonResponse.status); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, jsonResponse.status); return; } From ff3672cf7ff1fa8ab48effc803cc899db24dcdb1 Mon Sep 17 00:00:00 2001 From: lsiepel Date: Sat, 22 Mar 2025 23:28:17 +0100 Subject: [PATCH 10/17] Update bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Data.java Signed-off-by: lsiepel --- .../java/org/openhab/binding/tacmi/internal/json/obj/Data.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Data.java b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Data.java index 7acd3ece1c45d..e9fe50bbf15b8 100644 --- a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Data.java +++ b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Data.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2010-2025 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional From 2c5ae9746de344316cb5e0edefe3caa3dae44b2d Mon Sep 17 00:00:00 2001 From: lsiepel Date: Sat, 22 Mar 2025 23:28:25 +0100 Subject: [PATCH 11/17] Update bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/Config.java Signed-off-by: lsiepel --- .../java/org/openhab/binding/tacmi/internal/json/Config.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/Config.java b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/Config.java index ffad394330194..5ade47a89d0fc 100644 --- a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/Config.java +++ b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/Config.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2010-2025 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional From 3958baed632a518a143162d7b9ca2e38d1212b31 Mon Sep 17 00:00:00 2001 From: lsiepel Date: Sat, 22 Mar 2025 23:28:34 +0100 Subject: [PATCH 12/17] Update bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java Signed-off-by: lsiepel --- .../openhab/binding/tacmi/internal/json/TACmiJsonHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java index 49010110f2eaa..1dedee18aaf9e 100644 --- a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java +++ b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/TACmiJsonHandler.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2010-2025 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional From cacb61b2f169c187c1d7ccef0c04a3ee94029363 Mon Sep 17 00:00:00 2001 From: lsiepel Date: Sat, 22 Mar 2025 23:28:42 +0100 Subject: [PATCH 13/17] Update bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/IO.java Signed-off-by: lsiepel --- .../java/org/openhab/binding/tacmi/internal/json/obj/IO.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/IO.java b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/IO.java index 5ede5328bb12b..f72c554c75588 100644 --- a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/IO.java +++ b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/IO.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2010-2025 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional From eb2d9dbc4546c55922a6c1578818f8bb949e3675 Mon Sep 17 00:00:00 2001 From: lsiepel Date: Sat, 22 Mar 2025 23:28:53 +0100 Subject: [PATCH 14/17] Update bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Value.java Signed-off-by: lsiepel --- .../java/org/openhab/binding/tacmi/internal/json/obj/Value.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Value.java b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Value.java index bdda71dee850b..54e63501fb6e4 100644 --- a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Value.java +++ b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Value.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2010-2025 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional From d6b6dc013b66eb744a0b8e70736389647f24154a Mon Sep 17 00:00:00 2001 From: lsiepel Date: Sat, 22 Mar 2025 23:29:01 +0100 Subject: [PATCH 15/17] Update bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/JsonResponse.java Signed-off-by: lsiepel --- .../openhab/binding/tacmi/internal/json/obj/JsonResponse.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/JsonResponse.java b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/JsonResponse.java index 236902c21b28a..6b60004f556ec 100644 --- a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/JsonResponse.java +++ b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/JsonResponse.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2010-2025 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional From d75a59ab031ab2ca5378496183265795052a3fff Mon Sep 17 00:00:00 2001 From: lsiepel Date: Sat, 22 Mar 2025 23:29:09 +0100 Subject: [PATCH 16/17] Update bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Header.java Signed-off-by: lsiepel --- .../org/openhab/binding/tacmi/internal/json/obj/Header.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Header.java b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Header.java index 81df5d80d38b7..d05bbe486b25b 100644 --- a/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Header.java +++ b/bundles/org.openhab.binding.tacmi/src/main/java/org/openhab/binding/tacmi/internal/json/obj/Header.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2010-2025 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional From f7f9c31736f022aaa7f2826ecc31c1f1dfdf797b Mon Sep 17 00:00:00 2001 From: lsiepel Date: Sat, 22 Mar 2025 23:29:16 +0100 Subject: [PATCH 17/17] Update bundles/org.openhab.binding.tacmi/src/test/java/org/openhab/binding/tacmi/internal/json/obj/test/DeSer.java Signed-off-by: lsiepel --- .../org/openhab/binding/tacmi/internal/json/obj/test/DeSer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.tacmi/src/test/java/org/openhab/binding/tacmi/internal/json/obj/test/DeSer.java b/bundles/org.openhab.binding.tacmi/src/test/java/org/openhab/binding/tacmi/internal/json/obj/test/DeSer.java index 78ab10e1a0dbd..4787316c27f07 100644 --- a/bundles/org.openhab.binding.tacmi/src/test/java/org/openhab/binding/tacmi/internal/json/obj/test/DeSer.java +++ b/bundles/org.openhab.binding.tacmi/src/test/java/org/openhab/binding/tacmi/internal/json/obj/test/DeSer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2010-2025 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional