From f707659f00aa3c5aef5aee5cb47b932d54180474 Mon Sep 17 00:00:00 2001 From: Marco Descher Date: Fri, 22 Sep 2023 20:09:42 +0200 Subject: [PATCH 01/12] wip --- bundles/org.openhab.binding.kermi/NOTICE | 13 ++ bundles/org.openhab.binding.kermi/README.md | 103 +++++++++ bundles/org.openhab.binding.kermi/pom.xml | 17 ++ .../src/main/feature/feature.xml | 9 + .../KermiBaseDeviceConfiguration.java | 17 ++ .../kermi/internal/KermiBindingConstants.java | 57 +++++ .../internal/KermiBridgeConfiguration.java | 7 + .../internal/KermiCommunicationException.java | 40 ++++ .../kermi/internal/KermiHandlerFactory.java | 87 ++++++++ .../kermi/internal/api/BaseResponse.java | 62 ++++++ .../binding/kermi/internal/api/Config.java | 75 +++++++ .../kermi/internal/api/DatapointValue.java | 51 +++++ .../kermi/internal/api/DeviceInfo.java | 162 ++++++++++++++ .../kermi/internal/api/DeviceOption.java | 40 ++++ .../kermi/internal/api/GetDeviceResponse.java | 5 + .../internal/api/GetDevicesResponse.java | 7 + .../kermi/internal/api/KermiHttpUtil.java | 175 +++++++++++++++ .../internal/api/VisualizationDatapoint.java | 29 +++ .../handler/KermiBaseThingHandler.java | 208 ++++++++++++++++++ .../internal/handler/KermiBridgeHandler.java | 133 +++++++++++ ...KermiDrinkingWaterHeatingThingHandler.java | 100 +++++++++ .../KermiHeatpumpManagerThingHandler.java | 68 ++++++ .../handler/KermiHeatpumpThingHandler.java | 97 ++++++++ .../kermi/internal/model/KermiSiteInfo.java | 52 +++++ .../internal/model/KermiSiteInfoUtil.java | 26 +++ .../src/main/resources/OH-INF/addon/addon.xml | 10 + .../resources/OH-INF/i18n/kermi.properties | 3 + .../main/resources/OH-INF/thing/bridge.xml | 27 +++ .../resources/OH-INF/thing/thing-types.xml | 64 ++++++ .../internal/model/KermiSiteInfoUtilTest.java | 32 +++ ...iceInfoByDeviceIdDrinkingWaterHeating.json | 148 +++++++++++++ 31 files changed, 1924 insertions(+) create mode 100644 bundles/org.openhab.binding.kermi/NOTICE create mode 100644 bundles/org.openhab.binding.kermi/README.md create mode 100644 bundles/org.openhab.binding.kermi/pom.xml create mode 100644 bundles/org.openhab.binding.kermi/src/main/feature/feature.xml create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBaseDeviceConfiguration.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBridgeConfiguration.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiCommunicationException.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiHandlerFactory.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/BaseResponse.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Config.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointValue.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceInfo.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceOption.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDeviceResponse.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDevicesResponse.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/KermiHttpUtil.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/VisualizationDatapoint.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiDrinkingWaterHeatingThingHandler.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiHeatpumpManagerThingHandler.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiHeatpumpThingHandler.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtil.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/addon/addon.xml create mode 100644 bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/i18n/kermi.properties create mode 100644 bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/bridge.xml create mode 100644 bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/thing-types.xml create mode 100644 bundles/org.openhab.binding.kermi/src/test/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtilTest.java create mode 100644 bundles/org.openhab.binding.kermi/src/test/resources/getDeviceInfoByDeviceIdDrinkingWaterHeating.json diff --git a/bundles/org.openhab.binding.kermi/NOTICE b/bundles/org.openhab.binding.kermi/NOTICE new file mode 100644 index 0000000000000..38d625e349232 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/NOTICE @@ -0,0 +1,13 @@ +This content is produced and maintained by the openHAB project. + +* Project home: https://www.openhab.org + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/openhab/openhab-addons diff --git a/bundles/org.openhab.binding.kermi/README.md b/bundles/org.openhab.binding.kermi/README.md new file mode 100644 index 0000000000000..e9fe549f00ab1 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/README.md @@ -0,0 +1,103 @@ +# Notes + +https://www.kermi.com/en/de/indoor-climate/products/heat-pumps-and-storage/x-center-controller/ + +# Kermi Binding + +_Give some details about what this binding is meant for - a protocol, system, specific device._ + +_If possible, provide some resources like pictures (only PNG is supported currently), a video, etc. to give an impression of what can be done with this binding._ +_You can place such resources into a `doc` folder next to this README.md._ + +_Put each sentence in a separate line to improve readability of diffs._ + +## Supported Things + +_Please describe the different supported things / devices including their ThingTypeUID within this section._ +_Which different types are supported, which models were tested etc.?_ +_Note that it is planned to generate some part of this based on the XML files within ```src/main/resources/OH-INF/thing``` of your binding._ + +- `bridge`: Short description of the Bridge, if any +- `sample`: Short description of the Thing with the ThingTypeUID `sample` + +## Discovery + +_Describe the available auto-discovery features here._ +_Mention for what it works and what needs to be kept in mind when using it._ + +## Binding Configuration + +_If your binding requires or supports general configuration settings, please create a folder ```cfg``` and place the configuration file ```.cfg``` inside it._ +_In this section, you should link to this file and provide some information about the options._ +_The file could e.g. look like:_ + +``` +# Configuration for the Kermi Binding +# +# Default secret key for the pairing of the Kermi Thing. +# It has to be between 10-40 (alphanumeric) characters. +# This may be changed by the user for security reasons. +secret=openHABSecret +``` + +_Note that it is planned to generate some part of this based on the information that is available within ```src/main/resources/OH-INF/binding``` of your binding._ + +_If your binding does not offer any generic configurations, you can remove this section completely._ + +## Thing Configuration + +_Describe what is needed to manually configure a thing, either through the UI or via a thing-file._ +_This should be mainly about its mandatory and optional configuration parameters._ + +_Note that it is planned to generate some part of this based on the XML files within ```src/main/resources/OH-INF/thing``` of your binding._ + +### `sample` Thing Configuration + +| Name | Type | Description | Default | Required | Advanced | +|-----------------|---------|---------------------------------------|---------|----------|----------| +| hostname | text | Hostname or IP address of the device | N/A | yes | no | +| password | text | Password to access the device | N/A | yes | no | +| refreshInterval | integer | Interval the device is polled in sec. | 600 | no | yes | + +## Channels + +_Here you should provide information about available channel types, what their meaning is and how they can be used._ + +_Note that it is planned to generate some part of this based on the XML files within ```src/main/resources/OH-INF/thing``` of your binding._ + +| Channel | Type | Read/Write | Description | +|---------|--------|------------|-----------------------------| +| control | Switch | RW | This is the control channel | + +## Full Example + +_Provide a full usage example based on textual configuration files._ +_*.things, *.items examples are mandatory as textual configuration is well used by many users._ +_*.sitemap examples are optional._ + +### Thing Configuration + +```java +Example thing configuration goes here. +``` +### Item Configuration + +```java +Example item configuration goes here. +``` + +### Sitemap Configuration + +```perl +Optional Sitemap configuration goes here. +Remove this section, if not needed. +``` + +## Any custom content here! + +_Feel free to add additional sections for whatever you think should also be mentioned about your binding!_ + +# NOTES + +1. Bridge init - check socket connection to host +2. heatpump-manager - get devices diff --git a/bundles/org.openhab.binding.kermi/pom.xml b/bundles/org.openhab.binding.kermi/pom.xml new file mode 100644 index 0000000000000..3ba506666823d --- /dev/null +++ b/bundles/org.openhab.binding.kermi/pom.xml @@ -0,0 +1,17 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 4.0.3-SNAPSHOT + + + org.openhab.binding.kermi + + openHAB Add-ons :: Bundles :: Kermi Binding + + diff --git a/bundles/org.openhab.binding.kermi/src/main/feature/feature.xml b/bundles/org.openhab.binding.kermi/src/main/feature/feature.xml new file mode 100644 index 0000000000000..a8b2ce368ff13 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/feature/feature.xml @@ -0,0 +1,9 @@ + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features + + + openhab-runtime-base + mvn:org.openhab.addons.bundles/org.openhab.binding.kermi/${project.version} + + diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBaseDeviceConfiguration.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBaseDeviceConfiguration.java new file mode 100644 index 0000000000000..030676bbf3243 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBaseDeviceConfiguration.java @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal; + +public class KermiBaseDeviceConfiguration { + public Integer address; +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java new file mode 100644 index 0000000000000..fda4729721574 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.ThingTypeUID; + +/** + * The {@link KermiBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author marco@descher.at - Initial contribution + */ +@NonNullByDefault +public class KermiBindingConstants { + + private static final String BINDING_ID = "kermi"; + + // List of all Thing Type UIDs + public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge"); + public static final ThingTypeUID THING_TYPE_DRINKINGWATER_HEATING = new ThingTypeUID(BINDING_ID, + "drinkingwater-heating"); + public static final ThingTypeUID THING_TYPE_ROOM_HEATING = new ThingTypeUID(BINDING_ID, "room-heating"); + public static final ThingTypeUID THING_TYPE_HEATPUMP = new ThingTypeUID(BINDING_ID, "heatpump"); + public static final ThingTypeUID THING_TYPE_HEATPUMP_MANAGER = new ThingTypeUID(BINDING_ID, "heatpump-manager"); + + // Device Constants + public static final String DEVICE_ID_HEATPUMP_MANAGER = "00000000-0000-0000-0000-000000000000"; + public static final int DEVICE_TYPE_HEATING_SYSTEM = 95; + public static final int DEVICE_TYPE_HEATPUMP = 97; + public static final int DEVICE_TYPE_HEATPUMP_MANAGER = 0; + + public static final String WELL_KNOWN_NAME_BS_TWE_TEMP_ACT = "BufferSystem_TweTemperatureActual"; + public static final String WELL_KNOWN_NAME_FS_COOL_TEMP_ACT = "BufferSystem_CoolingTemperatureActual"; + public static final String WELL_KNOWN_NAME_FS_HEAT_TEMP_ACT = "BufferSystem_HeatingTemperatureActual"; + public static final String WELL_KNOWN_NAME_COMB_HEATPUMP_STATE = "Rubin_CombinedHeatpumpState"; + public static final String WELL_KNOWN_NAME_COMB_HEATPUMP_CURR_COP = "Rubin_CurrentCOP"; + + // All Urls + public static final String HPM_GETDEVICESBYFILTER_URL = "http://%IP%/api/Device/GetDevicesByFilter/00000000-0000-0000-0000-000000000000"; + public static final String HPM_GETDEVICE_URL = "http://%IP%/api/Device/GetDevice/00000000-0000-0000-0000-000000000000"; + + public static String parseUrl(String url, String ip) { + return url.replace("%IP%", ip == null ? "" : ip.trim()); + } + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBridgeConfiguration.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBridgeConfiguration.java new file mode 100644 index 0000000000000..b6f2d4370d2d1 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBridgeConfiguration.java @@ -0,0 +1,7 @@ +package org.openhab.binding.kermi.internal; + +public class KermiBridgeConfiguration { + public String hostname; + public String password; + public Integer refreshInterval; +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiCommunicationException.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiCommunicationException.java new file mode 100644 index 0000000000000..b081fc8dca91d --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiCommunicationException.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal; + +import java.io.IOException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Exception for unexpected response from or communication failure with the Kermi controller. + * + * @author Marco Descher - Initial contribution + */ +@NonNullByDefault +public class KermiCommunicationException extends IOException { + private static final long serialVersionUID = 619020705591964155L; + + public KermiCommunicationException(String message) { + super(message); + } + + public KermiCommunicationException(Throwable ex) { + super(ex); + } + + public KermiCommunicationException(String message, @Nullable Throwable cause) { + super(message, cause); + } +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiHandlerFactory.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiHandlerFactory.java new file mode 100644 index 0000000000000..4dab33e8d8aa4 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiHandlerFactory.java @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal; + +import static org.openhab.binding.kermi.internal.KermiBindingConstants.*; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.kermi.internal.api.KermiHttpUtil; +import org.openhab.binding.kermi.internal.handler.KermiBridgeHandler; +import org.openhab.binding.kermi.internal.handler.KermiDrinkingWaterHeatingThingHandler; +import org.openhab.binding.kermi.internal.handler.KermiHeatpumpManagerThingHandler; +import org.openhab.binding.kermi.internal.handler.KermiHeatpumpThingHandler; +import org.openhab.binding.kermi.internal.model.KermiSiteInfo; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.binding.BaseThingHandlerFactory; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Component; + +/** + * The {@link KermiHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author marco@descher.at - Initial contribution + */ +@NonNullByDefault +@Component(configurationPid = "binding.kermi", service = ThingHandlerFactory.class) +public class KermiHandlerFactory extends BaseThingHandlerFactory { + + private KermiHttpUtil httpUtil; + private KermiSiteInfo kermiSiteInfo; + + public KermiHandlerFactory() { + httpUtil = new KermiHttpUtil(); + kermiSiteInfo = new KermiSiteInfo(); + } + + private static final Set SUPPORTED_THING_TYPES_UIDS = new HashSet() { + + private static final long serialVersionUID = 1L; + { + add(THING_TYPE_BRIDGE); + add(THING_TYPE_HEATPUMP_MANAGER); + add(THING_TYPE_HEATPUMP); + add(THING_TYPE_DRINKINGWATER_HEATING); + add(THING_TYPE_ROOM_HEATING); + } + }; + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (thingTypeUID.equals(THING_TYPE_BRIDGE)) { + return new KermiBridgeHandler((Bridge) thing, httpUtil, kermiSiteInfo); + } else if (thingTypeUID.equals(THING_TYPE_HEATPUMP_MANAGER)) { + return new KermiHeatpumpManagerThingHandler(thing, httpUtil, kermiSiteInfo); + } else if (thingTypeUID.equals(THING_TYPE_DRINKINGWATER_HEATING)) { + return new KermiDrinkingWaterHeatingThingHandler(thing, httpUtil, kermiSiteInfo); + } else if (thingTypeUID.equals(THING_TYPE_HEATPUMP)) { + return new KermiHeatpumpThingHandler(thing, httpUtil, kermiSiteInfo); + } + + return null; + } +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/BaseResponse.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/BaseResponse.java new file mode 100644 index 0000000000000..6fb62df8a4060 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/BaseResponse.java @@ -0,0 +1,62 @@ +package org.openhab.binding.kermi.internal.api; + +import com.google.gson.annotations.SerializedName; + +public class BaseResponse { + + @SerializedName("ResponseData") + private T responseData; + + @SerializedName("StatusCode") + private int statusCode; + + @SerializedName("ExceptionData") + private Object exceptionData; + + @SerializedName("DisplayText") + private String displayText; + + @SerializedName("DetailedText") + private String detailedText; + + public T getResponseData() { + return responseData; + } + + public void setResponseData(T responseData) { + this.responseData = responseData; + } + + public int getStatusCode() { + return statusCode; + } + + public void setStatusCode(int statusCode) { + this.statusCode = statusCode; + } + + public Object getExceptionData() { + return exceptionData; + } + + public void setExceptionData(Object exceptionData) { + this.exceptionData = exceptionData; + } + + public String getDisplayText() { + return displayText; + } + + public void setDisplayText(String displayText) { + this.displayText = displayText; + } + + public String getDetailedText() { + return detailedText; + } + + public void setDetailedText(String detailedText) { + this.detailedText = detailedText; + } + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Config.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Config.java new file mode 100644 index 0000000000000..cbe3a0a0b8513 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Config.java @@ -0,0 +1,75 @@ +package org.openhab.binding.kermi.internal.api; + +import java.util.Map; + +import com.google.gson.annotations.SerializedName; + +public class Config { + + @SerializedName("DatapointConfigId") + private String datapointConfigId; + + @SerializedName("DisplayName") + private String displayName; + + @SerializedName("Description") + private String description; + + @SerializedName("WellKnownName") + private String wellKnownName; + + @SerializedName("Unit") + private String unit; + + @SerializedName("PossibleValues") + private Map possibleValues; + + public String getDatapointConfigId() { + return datapointConfigId; + } + + public void setDatapointConfigId(String datapointConfigId) { + this.datapointConfigId = datapointConfigId; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getWellKnownName() { + return wellKnownName; + } + + public void setWellKnownName(String wellKnownName) { + this.wellKnownName = wellKnownName; + } + + public String getUnit() { + return unit; + } + + public void setUnit(String unit) { + this.unit = unit; + } + + public Map getPossibleValues() { + return possibleValues; + } + + public void setPossibleValues(Map possibleValues) { + this.possibleValues = possibleValues; + } + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointValue.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointValue.java new file mode 100644 index 0000000000000..cf93ae51105d8 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointValue.java @@ -0,0 +1,51 @@ +package org.openhab.binding.kermi.internal.api; + +import com.google.gson.annotations.SerializedName; + +public class DatapointValue { + + @SerializedName("Value") + private Object value; + + @SerializedName("DatapointConfigId") + private String datapointConfigId; + + @SerializedName("DeviceId") + private String deviceId; + + @SerializedName("Flags") + private int flags; + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } + + public String getDatapointConfigId() { + return datapointConfigId; + } + + public void setDatapointConfigId(String datapointConfigId) { + this.datapointConfigId = datapointConfigId; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public int getFlags() { + return flags; + } + + public void setFlags(int flags) { + this.flags = flags; + } + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceInfo.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceInfo.java new file mode 100644 index 0000000000000..abb26e7cc707b --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceInfo.java @@ -0,0 +1,162 @@ +package org.openhab.binding.kermi.internal.api; + +import java.util.List; + +import com.google.gson.annotations.SerializedName; + +public class DeviceInfo { + + @SerializedName("DeviceId") + private String deviceId; + + @SerializedName("Protocol") + private int protocol; + + @SerializedName("PortAddress") + private String portAddress; + + @SerializedName("SoftwareVersion") + private String softwareVersion; + + @SerializedName("Address") + private String address; + + @SerializedName("HomeServerSenderAddress") + private String homeServerAddress; + + @SerializedName("Name") + private String name; + + @SerializedName("Description") + private String description; + + @SerializedName("Serial") + private String serial; + + @SerializedName("ParentMenuEntryId") + private String parentMenuEntryId; + + @SerializedName("ParentDeviceId") + private String parentDeviceId; + + @SerializedName("DeviceType") + private String deviceType; + + @SerializedName("DeviceOptions") + private List deviceOptions; + + @SerializedName("VisualizationDatapoints") + private List visualizationDatapoints; + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public int getProtocol() { + return protocol; + } + + public void setProtocol(int protocol) { + this.protocol = protocol; + } + + public String getPortAddress() { + return portAddress; + } + + public void setPortAddress(String portAddress) { + this.portAddress = portAddress; + } + + public String getSoftwareVersion() { + return softwareVersion; + } + + public void setSoftwareVersion(String softwareVersion) { + this.softwareVersion = softwareVersion; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getHomeServerAddress() { + return homeServerAddress; + } + + public void setHomeServerAddress(String homeServerAddress) { + this.homeServerAddress = homeServerAddress; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getSerial() { + return serial; + } + + public void setSerial(String serial) { + this.serial = serial; + } + + public String getParentMenuEntryId() { + return parentMenuEntryId; + } + + public void setParentMenuEntryId(String parentMenuEntryId) { + this.parentMenuEntryId = parentMenuEntryId; + } + + public String getParentDeviceId() { + return parentDeviceId; + } + + public void setParentDeviceId(String parentDeviceId) { + this.parentDeviceId = parentDeviceId; + } + + public String getDeviceType() { + return deviceType; + } + + public void setDeviceType(String deviceType) { + this.deviceType = deviceType; + } + + public List getDeviceOptions() { + return deviceOptions; + } + + public void setDeviceOptions(List deviceOptions) { + this.deviceOptions = deviceOptions; + } + + public List getVisualizationDatapoints() { + return visualizationDatapoints; + } + + public void setVisualizationDatapoints(List visualizationDatapoints) { + this.visualizationDatapoints = visualizationDatapoints; + } +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceOption.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceOption.java new file mode 100644 index 0000000000000..1be2f844e9520 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceOption.java @@ -0,0 +1,40 @@ +package org.openhab.binding.kermi.internal.api; + +import com.google.gson.annotations.SerializedName; + +public class DeviceOption { + + @SerializedName("OptionId") + private String optionId; + + @SerializedName("Name") + private String name; + + @SerializedName("IsActivated") + private boolean isActivated; + + public String getOptionId() { + return optionId; + } + + public void setOptionId(String optionId) { + this.optionId = optionId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isActivated() { + return isActivated; + } + + public void setActivated(boolean isActivated) { + this.isActivated = isActivated; + } + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDeviceResponse.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDeviceResponse.java new file mode 100644 index 0000000000000..10664d1f2209a --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDeviceResponse.java @@ -0,0 +1,5 @@ +package org.openhab.binding.kermi.internal.api; + +public class GetDeviceResponse extends BaseResponse { + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDevicesResponse.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDevicesResponse.java new file mode 100644 index 0000000000000..1f21517d64cf0 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDevicesResponse.java @@ -0,0 +1,7 @@ +package org.openhab.binding.kermi.internal.api; + +import java.util.List; + +public class GetDevicesResponse extends BaseResponse> { + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/KermiHttpUtil.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/KermiHttpUtil.java new file mode 100644 index 0000000000000..76d4afa0e78d9 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/KermiHttpUtil.java @@ -0,0 +1,175 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal.api; + +import static org.openhab.binding.kermi.internal.KermiBindingConstants.parseUrl; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ExecutionException; + +import org.eclipse.jetty.client.HttpResponseException; +import org.openhab.binding.kermi.internal.KermiBindingConstants; +import org.openhab.binding.kermi.internal.KermiCommunicationException; +import org.openhab.core.io.net.http.HttpUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +import io.micrometer.core.instrument.util.StringUtils; + +public class KermiHttpUtil { + + private static final String CONTENT_TYPE = "application/json; charset=utf-8"; + + private final Logger logger = LoggerFactory.getLogger(KermiHttpUtil.class); + + private String hostname = ""; + private String password = ""; + private HttpUtil httpUtil; + private Properties httpHeaders; + private Gson gson; + + private Map deviceInfo; + + public KermiHttpUtil() { + httpHeaders = new Properties(); + gson = new Gson(); + httpUtil = new HttpUtil(); + } + + public void executeCheckBridgeOnline() throws KermiCommunicationException { + executeUrl("GET", "http://" + hostname, null, null); + } + + public GetDevicesResponse getDevicesByFilter() throws KermiCommunicationException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("WithDetails", Boolean.FALSE); + jsonObject.addProperty("WithChildDevices", Boolean.FALSE); + jsonObject.addProperty("Recursive", Boolean.FALSE); + jsonObject.add("DeviceTypes", new JsonArray()); + jsonObject.add("MenuEntries", new JsonArray()); + String executeUrl = executeUrl("POST", parseUrl(KermiBindingConstants.HPM_GETDEVICESBYFILTER_URL, hostname), + jsonObject.toString(), CONTENT_TYPE); + return gson.fromJson(executeUrl, GetDevicesResponse.class); + } + + public GetDeviceResponse getDeviceInfoByDeviceId(String deviceId) throws KermiCommunicationException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("DeviceId", deviceId); + jsonObject.addProperty("WithDetails", Boolean.TRUE); + jsonObject.addProperty("Recursive", Boolean.TRUE); + String executeUrl = executeUrl("POST", parseUrl(KermiBindingConstants.HPM_GETDEVICE_URL, hostname), + jsonObject.toString(), CONTENT_TYPE); + return gson.fromJson(executeUrl, GetDeviceResponse.class); + } + + /** + * Issue a HTTP GET request and retry on failure + * + * @param url the url to execute + * @param timeout the socket timeout in milliseconds to wait for data + * @return the response body + * @throws KermiCommunicationException when the request execution failed or interrupted + */ + public synchronized String executeUrl(String httpMethod, String url, String content, String contentType) + throws KermiCommunicationException { + + if (StringUtils.isBlank(hostname)) { + return "Not connected"; + } + + int attemptCount = 1; + try { + while (true) { + Throwable lastException = null; + String result = null; + try { + InputStream _content = (content != null) + ? new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)) + : null; + result = HttpUtil.executeUrl(httpMethod, url, httpHeaders, _content, contentType, 5000); + logger.debug("[{} {}] {}", httpMethod, url, result); + } catch (IOException e) { + // HttpUtil::executeUrl wraps InterruptedException into IOException. + // Unwrap and rethrow it so that we don't retry on InterruptedException + if (e.getCause() instanceof InterruptedException) { + throw (InterruptedException) e.getCause(); + } + + if (e.getCause() instanceof ExecutionException) { + ExecutionException iex = (ExecutionException) e.getCause(); + if (iex != null && iex.getCause() instanceof HttpResponseException) { + HttpResponseException hre = (HttpResponseException) iex.getCause(); + if (401 == hre.getResponse().getStatus()) { + logger.debug("Perform login"); + attemptCount = 0; + performLogin(); + } + } + } + lastException = e; + } + + if (result != null) { + if (attemptCount > 1) { + logger.debug("Attempt #{} successful {}", attemptCount, url); + } + return result; + } + + if (attemptCount >= 3) { + logger.debug("Failed connecting to {} after {} attempts.", url, attemptCount, lastException); + // throw new FroniusCommunicationException("Unable to connect", lastException); + } + + logger.debug("HTTP error on attempt #{} {}", attemptCount, url); + Thread.sleep(500 * attemptCount); + attemptCount++; + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new KermiCommunicationException("Interrupted", e); + } + } + + private void performLogin() throws KermiCommunicationException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("Password", password); + executeUrl("POST", getBaseApiUrl() + "Security/Login", jsonObject.toString(), CONTENT_TYPE); + } + + private String getBaseApiUrl() { + return "http://" + hostname + "/api/"; + } + + public void setHostname(String hostname) { + this.hostname = hostname; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setDeviceInfo(Map deviceInfo) { + this.deviceInfo = deviceInfo; + } + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/VisualizationDatapoint.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/VisualizationDatapoint.java new file mode 100644 index 0000000000000..29afb993546a5 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/VisualizationDatapoint.java @@ -0,0 +1,29 @@ +package org.openhab.binding.kermi.internal.api; + +import com.google.gson.annotations.SerializedName; + +public class VisualizationDatapoint { + + @SerializedName("Config") + private Config config; + + @SerializedName("DatapointValue") + private DatapointValue datapointValue; + + public Config getConfig() { + return config; + } + + public void setConfig(Config config) { + this.config = config; + } + + public DatapointValue getDatapointValue() { + return datapointValue; + } + + public void setDatapointValue(DatapointValue datapointValue) { + this.datapointValue = datapointValue; + } + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java new file mode 100644 index 0000000000000..b6590f320f5e4 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java @@ -0,0 +1,208 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal.handler; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.kermi.internal.KermiBridgeConfiguration; +import org.openhab.binding.kermi.internal.KermiCommunicationException; +import org.openhab.binding.kermi.internal.api.BaseResponse; +import org.openhab.binding.kermi.internal.api.KermiHttpUtil; +import org.openhab.binding.kermi.internal.model.KermiSiteInfo; +import org.openhab.core.thing.Bridge; +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.types.Command; +import org.openhab.core.types.RefreshType; +import org.openhab.core.types.State; +import org.openhab.core.types.UnDefType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; + +public abstract class KermiBaseThingHandler extends BaseThingHandler { + + private static final int API_TIMEOUT = 5000; + private final Logger logger = LoggerFactory.getLogger(KermiBaseThingHandler.class); + private final String serviceDescription; + private KermiBaseThingHandler bridgeHandler; + private final Gson gson; + private final KermiHttpUtil httpUtil; + private final KermiSiteInfo kermiSiteInfo; + + public KermiBaseThingHandler(Thing thing, KermiHttpUtil httpUtil, KermiSiteInfo kermiSiteInfo) { + super(thing); + this.httpUtil = httpUtil; + this.kermiSiteInfo = kermiSiteInfo; + gson = new Gson(); + serviceDescription = getDescription(); + } + + public KermiHttpUtil getHttpUtil() { + return httpUtil; + } + + public KermiSiteInfo getKermiSiteInfo() { + return kermiSiteInfo; + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof RefreshType) { + updateChannel(channelUID.getId()); + } + } + + @Override + public void initialize() { + logger.debug("Initializing {} Service", serviceDescription); + // this is important so FroniusBridgeHandler::childHandlerInitialized gets called + Bridge bridge = getBridge(); + if (bridge == null || bridge.getHandler() == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED); + } else if (bridge.getStatus() == ThingStatus.ONLINE) { + updateStatus(ThingStatus.UNKNOWN); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); + } + } + + /** + * Update all Channels + */ + protected void updateChannels() { + for (Channel channel : getThing().getChannels()) { + updateChannel(channel.getUID().getId()); + } + } + + /** + * Update the channel from the last data + * + * @param channelId the id identifying the channel to be updated + */ + protected void updateChannel(String channelId) { + System.out.println(channelId); + if (!isLinked(channelId)) { + return; + } + + State state = getValue(channelId); + if (state == null) { + state = UnDefType.NULL; + } + + if (logger.isTraceEnabled()) { + logger.trace("Update channel {} with state {} ({})", channelId, state.toString(), + state.getClass().getSimpleName()); + } + updateState(channelId, state); + } + + /** + * return an internal description for logging + * + * @return the description of the thing + */ + protected abstract String getDescription(); + + /** + * get the "new" associated value for a channelId + * + * @param channelId the id identifying the channel + * @return the "new" associated value + */ + protected abstract State getValue(String channelId); + + /** + * Called by the bridge to fetch data and update channels + * + * @param bridgeConfiguration the connected bridge configuration + */ + public void refresh(KermiBridgeConfiguration bridgeConfiguration) { + try { + handleRefresh(bridgeConfiguration); + if (getThing().getStatus() != ThingStatus.ONLINE) { + updateStatus(ThingStatus.ONLINE); + } + } catch (KermiCommunicationException | RuntimeException e) { + logger.debug("Exception caught in refresh() for {}", getThing().getUID().getId(), e); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); + } + } + + /** + * This method should be overridden to do whatever a thing must do to update its channels + * this function is called from the bridge in a given interval + * + * @param bridgeConfiguration the connected bridge configuration + */ + protected abstract void handleRefresh(KermiBridgeConfiguration bridgeConfiguration) + throws KermiCommunicationException; + + /** + * + * @param type response class type + * @param url to request + * @return the object representation of the json response + */ + protected @NonNull T collectDataFromUrl(Class type, String url) + throws KermiCommunicationException { + try { + int attempts = 1; + while (true) { + logger.trace("Fetching URL = {}", url); + // String response = KermiHttpUtil.executeUrl(url, API_TIMEOUT); + // logger.trace("aqiResponse = {}", response); + + // T result = gson.fromJson(response, type); + // if (result == null) { + // throw new KermiCommunicationException("Empty json result"); + // } + + // HeadStatus status = result.getHead().getStatus(); + // if (status.getCode() == 0) { + // return result; + // } + + // Sometimes Fronius would return a HTTP status 200 with a proper JSON data + // with Reason: Transfer timeout. + // + // "Status" : { + // "Code" : 8, + // "Reason" : "Transfer timeout.", + // "UserMessage" : "" + // }, + // logger.debug("Error from Fronius attempt #{}: {} - {}", attempts, status.getCode(), + // status.getReason()); + // if (attempts >= 3) { + // throw new KermiCommunicationException(status.getReason()); + // } + Thread.sleep(500 * attempts); + attempts++; + } + + } catch (JsonSyntaxException | NumberFormatException e) { + logger.debug("Received Invalid JSON Data", e); + throw new KermiCommunicationException("Invalid JSON data received", e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new KermiCommunicationException("Data collection interrupted", e); + } + } +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java new file mode 100644 index 0000000000000..e3fda10c777ef --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java @@ -0,0 +1,133 @@ +package org.openhab.binding.kermi.internal.handler; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.kermi.internal.KermiBridgeConfiguration; +import org.openhab.binding.kermi.internal.KermiCommunicationException; +import org.openhab.binding.kermi.internal.api.KermiHttpUtil; +import org.openhab.binding.kermi.internal.model.KermiSiteInfo; +import org.openhab.core.thing.Bridge; +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.BaseBridgeHandler; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.types.Command; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@NonNullByDefault +public class KermiBridgeHandler extends BaseBridgeHandler { + + private final Logger logger = LoggerFactory.getLogger(KermiBridgeHandler.class); + private static final int DEFAULT_REFRESH_PERIOD = 10; + private final Set services = new HashSet<>(); + private @Nullable ScheduledFuture refreshJob; + + private KermiHttpUtil httpUtil; + private KermiSiteInfo kermiSiteInfo; + + public KermiBridgeHandler(Bridge bridge, KermiHttpUtil httpUtil, KermiSiteInfo kermiSiteInfo) { + super(bridge); + this.httpUtil = httpUtil; + this.kermiSiteInfo = kermiSiteInfo; + } + + @Override + public void initialize() { + final KermiBridgeConfiguration config = getConfigAs(KermiBridgeConfiguration.class); + + boolean validConfig = true; + String errorMsg = null; + + String hostname = config.hostname; + if (hostname == null || hostname.isBlank()) { + errorMsg = "Parameter 'hostname' is mandatory and must be configured"; + validConfig = false; + } + String password = config.password; + if (password == null || password.isBlank()) { + errorMsg = "Parameter 'password' is mandatory and must be configured"; + validConfig = false; + } + + if (config.refreshInterval != null && config.refreshInterval <= 0) { + errorMsg = "Parameter 'refresh' must be at least 1 second"; + validConfig = false; + } + + if (validConfig) { + httpUtil.setHostname(config.hostname); + httpUtil.setPassword(config.password); + startAutomaticRefresh(); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, errorMsg); + } + } + + @Override + public void dispose() { + if (refreshJob != null) { + refreshJob.cancel(true); + refreshJob = null; + } + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + System.out.println("hallo"); + } + + @Override + public void childHandlerInitialized(ThingHandler childHandler, Thing childThing) { + if (childHandler instanceof KermiBaseThingHandler) { + this.services.add((KermiBaseThingHandler) childHandler); + restartAutomaticRefresh(); + } else { + logger.debug("Child handler {} not added because it is not an instance of KermiBaseThingHandler", + childThing.getUID().getId()); + } + } + + private void restartAutomaticRefresh() { + if (refreshJob != null) { // refreshJob should be null if the config isn't valid + refreshJob.cancel(false); + startAutomaticRefresh(); + } + } + + private void startAutomaticRefresh() { + if (refreshJob == null || refreshJob.isCancelled()) { + final KermiBridgeConfiguration config = getConfigAs(KermiBridgeConfiguration.class); + Runnable runnable = () -> { + try { + checkBridgeOnline(config); + if (getThing().getStatus() != ThingStatus.ONLINE) { + updateStatus(ThingStatus.ONLINE); + } + for (KermiBaseThingHandler service : services) { + service.refresh(config); + } + } catch (KermiCommunicationException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage()); + kermiSiteInfo.clearSiteInfo(); + } + }; + + int delay = (config.refreshInterval != null) ? config.refreshInterval.intValue() : DEFAULT_REFRESH_PERIOD; + refreshJob = scheduler.scheduleWithFixedDelay(runnable, 1, delay, TimeUnit.SECONDS); + } + + } + + private void checkBridgeOnline(KermiBridgeConfiguration config) throws KermiCommunicationException { + httpUtil.executeCheckBridgeOnline(); + } + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiDrinkingWaterHeatingThingHandler.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiDrinkingWaterHeatingThingHandler.java new file mode 100644 index 0000000000000..15786ea045166 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiDrinkingWaterHeatingThingHandler.java @@ -0,0 +1,100 @@ +package org.openhab.binding.kermi.internal.handler; + +import java.util.Map; +import java.util.Optional; + +import org.openhab.binding.kermi.internal.KermiBaseDeviceConfiguration; +import org.openhab.binding.kermi.internal.KermiBindingConstants; +import org.openhab.binding.kermi.internal.KermiBridgeConfiguration; +import org.openhab.binding.kermi.internal.KermiCommunicationException; +import org.openhab.binding.kermi.internal.api.DatapointValue; +import org.openhab.binding.kermi.internal.api.DeviceInfo; +import org.openhab.binding.kermi.internal.api.GetDeviceResponse; +import org.openhab.binding.kermi.internal.api.KermiHttpUtil; +import org.openhab.binding.kermi.internal.model.KermiSiteInfo; +import org.openhab.binding.kermi.internal.model.KermiSiteInfoUtil; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.thing.Thing; +import org.openhab.core.types.State; + +import tech.units.indriya.unit.Units; + +public class KermiDrinkingWaterHeatingThingHandler extends KermiBaseThingHandler { + + private KermiBaseDeviceConfiguration config; + + public KermiDrinkingWaterHeatingThingHandler(Thing thing, KermiHttpUtil httpUtil, KermiSiteInfo kermiSiteInfo) { + super(thing, httpUtil, kermiSiteInfo); + } + + @Override + protected String getDescription() { + return "DrinkingWaterHeating"; + // DeviceInfo deviceInfo = getKermiSiteInfo().getDeviceInfoByAddress(config.address.toString()); + // return deviceInfo != null ? deviceInfo.getName() : "No device on address " + config.address; + } + + @Override + protected State getValue(String channelId) { + DeviceInfo deviceInfo = getKermiSiteInfo().getDeviceInfoByAddress(config.address.toString()); + if (deviceInfo == null) { + return null; + } + + final String[] fields = channelId.split("#"); + if (fields.length < 1) { + return null; + } + + final String fieldName = fields[0]; + Optional dpv = KermiSiteInfoUtil.getVisualizationDatapointValueByWellKnownName(fieldName, + deviceInfo); + if (!dpv.isPresent()) { + return null; + } + Object value = dpv.get().getValue(); + State result = null; + switch (fieldName) { + case KermiBindingConstants.WELL_KNOWN_NAME_BS_TWE_TEMP_ACT: { + result = new QuantityType<>((float) value, Units.CELSIUS); + } + break; + default: + break; + } + + return result; + } + + @Override + protected void handleRefresh(KermiBridgeConfiguration bridgeConfiguration) throws KermiCommunicationException { + updateData(); + updateChannels(); + updateProperties(); + } + + private void updateProperties() { + DeviceInfo deviceInfo = getKermiSiteInfo().getDeviceInfoByAddress(config.address.toString()); + if (deviceInfo == null) { + return; + } + + Map properties = editProperties(); + properties.put(Thing.PROPERTY_SERIAL_NUMBER, deviceInfo.getSerial()); + properties.put(Thing.PROPERTY_FIRMWARE_VERSION, deviceInfo.getSoftwareVersion()); + updateProperties(properties); + } + + private void updateData() throws KermiCommunicationException { + String deviceId = getKermiSiteInfo().getDeviceIdByAddress(config.address.toString()); + GetDeviceResponse deviceResponse = getHttpUtil().getDeviceInfoByDeviceId(deviceId); + getKermiSiteInfo().updateDeviceInfo(deviceId, deviceResponse.getResponseData()); + } + + @Override + public void initialize() { + config = getConfigAs(KermiBaseDeviceConfiguration.class); + super.initialize(); + } + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiHeatpumpManagerThingHandler.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiHeatpumpManagerThingHandler.java new file mode 100644 index 0000000000000..9bc0a0b5b8f1f --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiHeatpumpManagerThingHandler.java @@ -0,0 +1,68 @@ +package org.openhab.binding.kermi.internal.handler; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.kermi.internal.KermiBridgeConfiguration; +import org.openhab.binding.kermi.internal.KermiCommunicationException; +import org.openhab.binding.kermi.internal.api.DeviceInfo; +import org.openhab.binding.kermi.internal.api.GetDevicesResponse; +import org.openhab.binding.kermi.internal.api.KermiHttpUtil; +import org.openhab.binding.kermi.internal.model.KermiSiteInfo; +import org.openhab.core.thing.Thing; +import org.openhab.core.types.State; + +public class KermiHeatpumpManagerThingHandler extends KermiBaseThingHandler { + + public KermiHeatpumpManagerThingHandler(Thing thing, @NonNull KermiHttpUtil httpUtil, KermiSiteInfo kermiSiteInfo) { + super(thing, httpUtil, kermiSiteInfo); + } + + @Override + protected String getDescription() { + return "HeatpumpManager"; + // DeviceInfo deviceInfo = getKermiSiteInfo().getHeatpumpManagerDeviceInfo(); + // return deviceInfo != null ? deviceInfo.getName() : "Unknown"; + } + + @Override + protected State getValue(String channelId) { + return null; + } + + @Override + protected void handleRefresh(KermiBridgeConfiguration bridgeConfiguration) throws KermiCommunicationException { + if (getKermiSiteInfo().getHeatpumpManagerDeviceInfo() == null) { + // no need to do this regularly + updateData(); + updateChannels(); + updateProperties(); + } + } + + private void updateData() throws KermiCommunicationException { + GetDevicesResponse getDevicesResponse = getHttpUtil().getDevicesByFilter(); + List deviceInfo = getDevicesResponse.getResponseData(); + Map _deviceInfo = deviceInfo.stream() + .collect(Collectors.toMap(DeviceInfo::getDeviceId, Function.identity())); + getKermiSiteInfo().updateSiteInfo(_deviceInfo); + } + + private void updateProperties() { + DeviceInfo deviceInfo = getKermiSiteInfo().getHeatpumpManagerDeviceInfo(); + if (deviceInfo == null) { + return; + } + + Map properties = editProperties(); + + properties.put(Thing.PROPERTY_FIRMWARE_VERSION, deviceInfo.getSoftwareVersion()); + properties.put(Thing.PROPERTY_SERIAL_NUMBER, deviceInfo.getSerial()); + + updateProperties(properties); + } + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiHeatpumpThingHandler.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiHeatpumpThingHandler.java new file mode 100644 index 0000000000000..e105c04e59d7c --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiHeatpumpThingHandler.java @@ -0,0 +1,97 @@ +package org.openhab.binding.kermi.internal.handler; + +import java.util.Map; +import java.util.Optional; + +import org.openhab.binding.kermi.internal.KermiBaseDeviceConfiguration; +import org.openhab.binding.kermi.internal.KermiBindingConstants; +import org.openhab.binding.kermi.internal.KermiBridgeConfiguration; +import org.openhab.binding.kermi.internal.KermiCommunicationException; +import org.openhab.binding.kermi.internal.api.DatapointValue; +import org.openhab.binding.kermi.internal.api.DeviceInfo; +import org.openhab.binding.kermi.internal.api.GetDeviceResponse; +import org.openhab.binding.kermi.internal.api.KermiHttpUtil; +import org.openhab.binding.kermi.internal.model.KermiSiteInfo; +import org.openhab.binding.kermi.internal.model.KermiSiteInfoUtil; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.thing.Thing; +import org.openhab.core.types.State; + +public class KermiHeatpumpThingHandler extends KermiBaseThingHandler { + + private KermiBaseDeviceConfiguration config; + + public KermiHeatpumpThingHandler(Thing thing, KermiHttpUtil httpUtil, KermiSiteInfo kermiSiteInfo) { + super(thing, httpUtil, kermiSiteInfo); + } + + @Override + protected String getDescription() { + return "Heatpump"; + // DeviceInfo deviceInfo = getKermiSiteInfo().getDeviceInfoByAddress(config.address.toString()); + // return deviceInfo != null ? deviceInfo.getName() : "No device on address " + config.address; + } + + @Override + protected State getValue(String channelId) { + DeviceInfo deviceInfo = getKermiSiteInfo().getDeviceInfoByAddress(config.address.toString()); + if (deviceInfo == null) { + return null; + } + + final String[] fields = channelId.split("#"); + if (fields.length < 1) { + return null; + } + + final String fieldName = fields[0]; + Optional dpv = KermiSiteInfoUtil.getVisualizationDatapointValueByWellKnownName(fieldName, + deviceInfo); + if (!dpv.isPresent()) { + return null; + } + Object value = dpv.get().getValue(); + + switch (fieldName) { + case KermiBindingConstants.WELL_KNOWN_NAME_COMB_HEATPUMP_STATE: { + return new DecimalType((String) value); + } + default: + break; + } + + return null; + } + + @Override + protected void handleRefresh(KermiBridgeConfiguration bridgeConfiguration) throws KermiCommunicationException { + updateData(); + updateChannels(); + updateProperties(); + } + + private void updateProperties() { + DeviceInfo deviceInfo = getKermiSiteInfo().getDeviceInfoByAddress(config.address.toString()); + if (deviceInfo == null) { + return; + } + + Map properties = editProperties(); + properties.put(Thing.PROPERTY_SERIAL_NUMBER, deviceInfo.getSerial()); + properties.put(Thing.PROPERTY_FIRMWARE_VERSION, deviceInfo.getSoftwareVersion()); + updateProperties(properties); + } + + private void updateData() throws KermiCommunicationException { + String deviceId = getKermiSiteInfo().getDeviceIdByAddress(config.address.toString()); + GetDeviceResponse deviceResponse = getHttpUtil().getDeviceInfoByDeviceId(deviceId); + getKermiSiteInfo().updateDeviceInfo(deviceId, deviceResponse.getResponseData()); + } + + @Override + public void initialize() { + config = getConfigAs(KermiBaseDeviceConfiguration.class); + super.initialize(); + } + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java new file mode 100644 index 0000000000000..2a7dc7270ca66 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java @@ -0,0 +1,52 @@ +package org.openhab.binding.kermi.internal.model; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.kermi.internal.KermiBindingConstants; +import org.openhab.binding.kermi.internal.api.DeviceInfo; + +import io.micrometer.core.instrument.util.StringUtils; + +public class KermiSiteInfo { + + private Map deviceIdToDeviceInfo; + private Map addressToDeviceId; + + public KermiSiteInfo() { + deviceIdToDeviceInfo = Collections.synchronizedMap(new HashMap()); + addressToDeviceId = new HashMap(); + } + + public DeviceInfo getHeatpumpManagerDeviceInfo() { + return deviceIdToDeviceInfo.get(KermiBindingConstants.DEVICE_ID_HEATPUMP_MANAGER); + } + + public DeviceInfo getDeviceInfoByAddress(String address) { + String deviceId = addressToDeviceId.get(address); + return deviceId != null ? deviceIdToDeviceInfo.get(deviceId) : null; + } + + public void updateSiteInfo(Map deviceInfo) { + clearSiteInfo(); + deviceIdToDeviceInfo.putAll(deviceInfo); + deviceInfo.values().stream().filter(di -> StringUtils.isNotEmpty(di.getAddress())) + .forEach(di -> addressToDeviceId.put(di.getAddress(), di.getDeviceId())); + } + + public void clearSiteInfo() { + deviceIdToDeviceInfo.clear(); + addressToDeviceId.clear(); + } + + public String getDeviceIdByAddress(@NonNull String address) { + return addressToDeviceId.get(address); + } + + public void updateDeviceInfo(String deviceId, DeviceInfo deviceInfo) { + deviceIdToDeviceInfo.put(deviceId, deviceInfo); + } + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtil.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtil.java new file mode 100644 index 0000000000000..1b637e0a79b09 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtil.java @@ -0,0 +1,26 @@ +package org.openhab.binding.kermi.internal.model; + +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; +import org.openhab.binding.kermi.internal.api.DatapointValue; +import org.openhab.binding.kermi.internal.api.DeviceInfo; +import org.openhab.binding.kermi.internal.api.VisualizationDatapoint; + +public class KermiSiteInfoUtil { + + public static Optional getVisualizationDatapointByWellKnownName(String wellKnownName, + DeviceInfo deviceInfo) { + return deviceInfo.getVisualizationDatapoints().stream().filter(vdp -> vdp.getConfig() != null) + .filter(vdp -> StringUtils.equalsIgnoreCase(wellKnownName, vdp.getConfig().getWellKnownName())) + .findFirst(); + } + + public static Optional getVisualizationDatapointValueByWellKnownName(String wellKnownName, + DeviceInfo deviceInfo) { + Optional visualizationDatapointByWellKnownName = getVisualizationDatapointByWellKnownName( + wellKnownName, deviceInfo); + return visualizationDatapointByWellKnownName.map(VisualizationDatapoint::getDatapointValue); + } + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/addon/addon.xml new file mode 100644 index 0000000000000..6498c42e435d6 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/addon/addon.xml @@ -0,0 +1,10 @@ + + + + binding + Kermi Binding + This is the binding for Kermi. + + diff --git a/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/i18n/kermi.properties b/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/i18n/kermi.properties new file mode 100644 index 0000000000000..be7f6ca7ba82e --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/i18n/kermi.properties @@ -0,0 +1,3 @@ +# FIXME: please add all English translations to this file so the texts can be translated using Crowdin +# FIXME: to generate the content of this file run: mvn i18n:generate-default-translations +# FIXME: see also: https://www.openhab.org/docs/developer/utils/i18n.html \ No newline at end of file diff --git a/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/bridge.xml b/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/bridge.xml new file mode 100644 index 0000000000000..ebe641ad27c96 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/bridge.xml @@ -0,0 +1,27 @@ + + + + + + A bridge to connect Kermi devices + + + network-address + + The hostname or IP address of the gateway/device + + + + The password to access the bridge api + + + + Specifies the refresh interval in seconds. + 60 + + + + diff --git a/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/thing-types.xml new file mode 100644 index 0000000000000..c9747a6b54dbd --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/thing-types.xml @@ -0,0 +1,64 @@ + + + + + + + + + + Kermi Heatpump Manager + + + + + + + + + + + + + + + + + Specific device identifier + 51 + + + + + + + + + + + + + + + + + + + + Specific device identifier + 40 + + + + + + Number:Temperature + + Current temperature + + + + diff --git a/bundles/org.openhab.binding.kermi/src/test/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtilTest.java b/bundles/org.openhab.binding.kermi/src/test/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtilTest.java new file mode 100644 index 0000000000000..314108a0b4f1b --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/test/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtilTest.java @@ -0,0 +1,32 @@ +package org.openhab.binding.kermi.internal.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import org.junit.jupiter.api.Test; +import org.openhab.binding.kermi.internal.KermiBindingConstants; +import org.openhab.binding.kermi.internal.api.GetDeviceResponse; +import org.openhab.binding.kermi.internal.api.VisualizationDatapoint; + +import com.google.gson.Gson; + +public class KermiSiteInfoUtilTest { + + @Test + public void getVisualizationDatapointByWellKnownName() throws IOException { + + GetDeviceResponse getDeviceResponse = null; + try (InputStream resourceAsStream = getClass() + .getResourceAsStream("/getDeviceInfoByDeviceIdDrinkingWaterHeating.json")) { + getDeviceResponse = new Gson().fromJson(new InputStreamReader(resourceAsStream), GetDeviceResponse.class); + } + + VisualizationDatapoint visualizationDatapoint = KermiSiteInfoUtil.getVisualizationDatapointByWellKnownName( + KermiBindingConstants.WELL_KNOWN_NAME_BS_TWE_TEMP_ACT, getDeviceResponse.getResponseData()).get(); + assertEquals("06e61673-abc2-4671-9e5a-960809d1f326", visualizationDatapoint.getConfig().getDatapointConfigId()); + } + +} diff --git a/bundles/org.openhab.binding.kermi/src/test/resources/getDeviceInfoByDeviceIdDrinkingWaterHeating.json b/bundles/org.openhab.binding.kermi/src/test/resources/getDeviceInfoByDeviceIdDrinkingWaterHeating.json new file mode 100644 index 0000000000000..f3f3eec98121a --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/test/resources/getDeviceInfoByDeviceIdDrinkingWaterHeating.json @@ -0,0 +1,148 @@ +{ + "ResponseData": { + "DeviceId": "2f82aa31-e57c-4212-872e-097fad62c30a", + "Protocol": 2, + "PortAddress": "/dev/ttymxc1", + "SoftwareVersion": "1.5.3", + "Address": "51", + "HomeServerSenderAddress": null, + "Name": "Trinkwassererwärmung (51)", + "Description": null, + "Serial": "29-41-00-75-a8-d5", + "ParentMenuEntryId": "00000000-0000-0000-0000-000000000000", + "ParentDeviceId": "00000000-0000-0000-0000-000000000000", + "DeviceType": 95, + "DeviceOptions": [ + { + "OptionId": "bdcd2755-6bf3-4d77-b742-f21be7801172", + "Name": "CoolingBufferEnabled", + "IsActivated": false + }, + { + "OptionId": "b7abc9eb-4d20-46a1-b3f9-e1a063da05ec", + "Name": "HeatingBufferEnabled", + "IsActivated": false + }, + { + "OptionId": "590dbd50-c0c4-4b6a-88e5-1a3d46628601", + "Name": "HeaterHeatingEnabled", + "IsActivated": false + }, + { + "OptionId": "cf83b44c-ca6e-4ea5-829c-eadff29ae7b2", + "Name": "HasOutsideTemperature", + "IsActivated": false + }, + { + "OptionId": "841e1039-fe57-4a46-b4ee-f9945447e2d7", + "Name": "Sensor2", + "IsActivated": false + }, + { + "OptionId": "e1340c32-cea9-41f1-a945-980c008b4246", + "Name": "Sensor4", + "IsActivated": false + }, + { + "OptionId": "9991809e-9fd8-42b8-bdad-492177554200", + "Name": "Sensor1", + "IsActivated": true + }, + { + "OptionId": "edf72fa5-c358-40cd-9f43-0b4fa4f4843a", + "Name": "edf72fa5-c358-40cd-9f43-0b4fa4f4843a", + "IsActivated": true + }, + { + "OptionId": "a8e2ee46-4f54-4f87-8f1a-b6f1e0e23c9d", + "Name": "a8e2ee46-4f54-4f87-8f1a-b6f1e0e23c9d", + "IsActivated": true + }, + { + "OptionId": "84922872-2d9b-46b2-aae8-30afd172fc31", + "Name": "84922872-2d9b-46b2-aae8-30afd172fc31", + "IsActivated": true + }, + { + "OptionId": "46ba01b3-284a-46ca-9d49-efa3ad453752", + "Name": "46ba01b3-284a-46ca-9d49-efa3ad453752", + "IsActivated": false + }, + { + "OptionId": "fbd7728b-792a-46ac-ab75-bb0226ec8b5a", + "Name": "Sensor3", + "IsActivated": false + }, + { + "OptionId": "955557bd-4596-485c-99c7-ed113607502f", + "Name": "955557bd-4596-485c-99c7-ed113607502f", + "IsActivated": false + } + ], + "VisualizationDatapoints": [ + { + "Config": { + "$type": "BMS.Shared.DatapointCore.DatapointConfig`1[[System.Single, mscorlib]], BMS.Shared", + "MaxValue": 0.0, + "MinValue": 0.0, + "DefaultValue": 0.0, + "Scale": 1.0, + "Offset": 0.0, + "PossibleValues": null, + "Colors": null, + "DatapointConfigId": "06e61673-abc2-4671-9e5a-960809d1f326", + "Category": 0, + "DisplayName": "Isttemperatur TWE", + "Description": "Anzeige der aktuellen Temperatur im Trinkwasserspeicher", + "UserLevelRead": 10, + "UserLevelWrite": 10, + "MenuEntryId": "fc642de6-9056-43c7-8469-5e2568db5e23", + "AllowedInAction": false, + "AllowedInCondition": true, + "Hidden": false, + "UIHint": "{ReadValueRound:0.1}", + "WriteOrder": 1, + "BundleId": "e786d99e-b08d-459e-a44e-43d5ea1e05fe", + "Unit": "°C", + "Sort": 2, + "ConfigVersion": 1, + "DatapointTarget": 1, + "DeviceType": 95, + "Address": null, + "DatapointType": 1, + "IsDeviceVisualizationDatapoint": true, + "VisualizationDatapointSort": 1, + "ColorKey": null, + "PossibleValueKey": null, + "WellKnownName": "BufferSystem_TweTemperatureActual", + "ImageName": "temp-ist_70x52", + "Ignore": false, + "DeviceOptions": { + "edf72fa5-c358-40cd-9f43-0b4fa4f4843a": "TweBufferEnabled" + } + }, + "DatapointValue": { + "$type": "BMS.Shared.DatapointCore.DatapointValue`1[[System.Single, mscorlib]], BMS.Shared", + "Value": 48.100002, + "DatapointConfigId": "06e61673-abc2-4671-9e5a-960809d1f326", + "DeviceId": "2f82aa31-e57c-4212-872e-097fad62c30a", + "Flags": 0 + } + } + ], + "IsSynced": true, + "IsOffline": false, + "LastChange": null, + "IsDisabled": null, + "ConfigVersion": 1, + "AccessControlList": null, + "CustomProperties": { + "WizardAnswer": "{\"$type\":\"BMS.Shared.Wizard.WizardAnswerBufferSystem, BMS.Shared\",\"BufferSystemType\":2,\"PowermoduleFunctionType\":2}" + }, + "Flags": 58 + }, + "StatusCode": 0, + "ExceptionData": null, + "DisplayText": "", + "DetailedText": "" +} From b2934578815e01d8233499f25a6b5f96a012eec7 Mon Sep 17 00:00:00 2001 From: Marco Descher Date: Sun, 1 Oct 2023 21:07:07 +0200 Subject: [PATCH 02/12] org.openhab.binding.kermi initial contribution --- bundles/org.openhab.binding.kermi/README.md | 111 ++++--------- bundles/org.openhab.binding.kermi/pom.xml | 2 +- .../kermi/internal/KermiBindingConstants.java | 22 ++- .../kermi/internal/KermiHandlerFactory.java | 16 +- .../binding/kermi/internal/api/Bundle.java | 34 ++++ .../binding/kermi/internal/api/Config.java | 11 ++ .../binding/kermi/internal/api/Datapoint.java | 29 ++++ .../api/DatapointReadValuesResponse.java | 7 + .../binding/kermi/internal/api/Device.java | 5 + .../kermi/internal/api/KermiHttpUtil.java | 79 +++++---- .../binding/kermi/internal/api/MenuEntry.java | 29 ++++ .../kermi/internal/api/MenuEntryResponse.java | 64 ++++++++ .../api/MenuGetChildEntriesResponse.java | 5 + .../handler/KermiBaseThingHandler.java | 155 ++++++++---------- .../internal/handler/KermiBridgeHandler.java | 32 +++- ...KermiDrinkingWaterHeatingThingHandler.java | 100 ----------- .../KermiHeatpumpManagerThingHandler.java | 68 -------- .../handler/KermiHeatpumpThingHandler.java | 97 ----------- .../kermi/internal/model/KermiSiteInfo.java | 124 ++++++++++++-- .../internal/model/KermiSiteInfoUtil.java | 118 +++++++++++-- .../model/ListDatapointCacheFile.java | 44 +++++ .../resources/OH-INF/thing/thing-types.xml | 24 ++- .../internal/model/KermiSiteInfoUtilTest.java | 11 +- ...iceInfoByDeviceIdDrinkingWaterHeating.json | 148 ----------------- 24 files changed, 670 insertions(+), 665 deletions(-) create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Bundle.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Datapoint.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointReadValuesResponse.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Device.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntry.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntryResponse.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuGetChildEntriesResponse.java delete mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiDrinkingWaterHeatingThingHandler.java delete mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiHeatpumpManagerThingHandler.java delete mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiHeatpumpThingHandler.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/ListDatapointCacheFile.java delete mode 100644 bundles/org.openhab.binding.kermi/src/test/resources/getDeviceInfoByDeviceIdDrinkingWaterHeating.json diff --git a/bundles/org.openhab.binding.kermi/README.md b/bundles/org.openhab.binding.kermi/README.md index e9fe549f00ab1..7304abdad4361 100644 --- a/bundles/org.openhab.binding.kermi/README.md +++ b/bundles/org.openhab.binding.kermi/README.md @@ -1,103 +1,62 @@ -# Notes +# Kermi Binding -https://www.kermi.com/en/de/indoor-climate/products/heat-pumps-and-storage/x-center-controller/ +This binding connects to [Kermi x-center controller](https://www.kermi.com/en/de/indoor-climate/products/heat-pumps-and-storage/x-center-controller/ "x-center-controller") for heat pumps. # Kermi Binding -_Give some details about what this binding is meant for - a protocol, system, specific device._ +Current support is developed and tested on -_If possible, provide some resources like pictures (only PNG is supported currently), a video, etc. to give an impression of what can be done with this binding._ -_You can place such resources into a `doc` folder next to this README.md._ +* a franchised version of the Kermi heatpump, namely the +[Heizbösch MOZART13AC-RW60](https://www.boesch.at/produkte/heizen/waermepumpe/luft/modulierende-luft-wasser-waermepumpe-mozart-aussenaufstellung~495589) heatpump manager version _1.6.0.118_ . -_Put each sentence in a separate line to improve readability of diffs._ +No official documentation could be found or gathered. This plug-in is based +on reverse engineering the protocol. ## Supported Things -_Please describe the different supported things / devices including their ThingTypeUID within this section._ -_Which different types are supported, which models were tested etc.?_ -_Note that it is planned to generate some part of this based on the XML files within ```src/main/resources/OH-INF/thing``` of your binding._ +The x-center consists of a heat-pump manager that provides the communication bridge. -- `bridge`: Short description of the Bridge, if any -- `sample`: Short description of the Thing with the ThingTypeUID `sample` +- `bridge`: The communication bridge, provided by the heat pump manager +- `drinkingwater-heating`: The storage module responsible for heating the drinking water (Default bus address = 51) +- `room-heating`: The storage module responsible for heating rooms (Default bus address = 50) +- `heatpump-manager`: As thing +- `heatpump`: The heatpump element itself (Default bus address = 40) ## Discovery -_Describe the available auto-discovery features here._ -_Mention for what it works and what needs to be kept in mind when using it._ - -## Binding Configuration - -_If your binding requires or supports general configuration settings, please create a folder ```cfg``` and place the configuration file ```.cfg``` inside it._ -_In this section, you should link to this file and provide some information about the options._ -_The file could e.g. look like:_ - -``` -# Configuration for the Kermi Binding -# -# Default secret key for the pairing of the Kermi Thing. -# It has to be between 10-40 (alphanumeric) characters. -# This may be changed by the user for security reasons. -secret=openHABSecret -``` - -_Note that it is planned to generate some part of this based on the information that is available within ```src/main/resources/OH-INF/binding``` of your binding._ - -_If your binding does not offer any generic configurations, you can remove this section completely._ - -## Thing Configuration +There is no obvious way to get a listing of all Datapoints for a given Device. Due to this, on first +connection to a site, the API for the UI User Interface is used, to iterate over all menu entries to +collect the datapoints - which may take a while. -_Describe what is needed to manually configure a thing, either through the UI or via a thing-file._ -_This should be mainly about its mandatory and optional configuration parameters._ +The gathered data is then stored in `OH_USERDATA/binding.kermi` and loaded on subsequent binding lifecycle +changes. The cache data is bound to the device-uuid and its serial number. If these values change, +the datapoints are automatically re-initialized. -_Note that it is planned to generate some part of this based on the XML files within ```src/main/resources/OH-INF/thing``` of your binding._ -### `sample` Thing Configuration -| Name | Type | Description | Default | Required | Advanced | -|-----------------|---------|---------------------------------------|---------|----------|----------| -| hostname | text | Hostname or IP address of the device | N/A | yes | no | -| password | text | Password to access the device | N/A | yes | no | -| refreshInterval | integer | Interval the device is polled in sec. | 600 | no | yes | - -## Channels - -_Here you should provide information about available channel types, what their meaning is and how they can be used._ - -_Note that it is planned to generate some part of this based on the XML files within ```src/main/resources/OH-INF/thing``` of your binding._ - -| Channel | Type | Read/Write | Description | -|---------|--------|------------|-----------------------------| -| control | Switch | RW | This is the control channel | - -## Full Example - -_Provide a full usage example based on textual configuration files._ -_*.things, *.items examples are mandatory as textual configuration is well used by many users._ -_*.sitemap examples are optional._ +## Binding Configuration -### Thing Configuration +The following samples are provided, representing the current state of my usage. -```java -Example thing configuration goes here. ``` -### Item Configuration +// kermi.things +Bridge kermi:bridge:heatpumpbridge [hostname="xcenter-url",password="password",refreshInterval=60] { + Thing drinkingwater-heating dwheating [ address=51 ] + Thing room-heating rheating [ address=50 ] + Thing heatpump heatpump [ address=40 ] +} -```java -Example item configuration goes here. ``` -### Sitemap Configuration +The plugin is configured to collect all `WellKnownName` datapoints, so generally +all of them should be supported. At the moment I only test the following items in read-only mode. -```perl -Optional Sitemap configuration goes here. -Remove this section, if not needed. ``` +// kermi.items +Number:Temperature Drinking_water_temperature {channel="kermi:drinkingwater-heating:heatpumpbridge:dwheating:BufferSystem_TweTemperatureActual"} -## Any custom content here! - -_Feel free to add additional sections for whatever you think should also be mentioned about your binding!_ - -# NOTES +Number:Temperature Heating_current_temperature_buffer {channel="kermi:room-heating:heatpumpbridge:rheating:BufferSystem_HeatingTemperatureActual"} +Number:Temperature Cooling_current_temperature_buffer {channel="kermi:room-heating:heatpumpbridge:rheating:BufferSystem_CoolingTemperatureActual"} -1. Bridge init - check socket connection to host -2. heatpump-manager - get devices +String Combined_Heatpump_State {channel="kermi:room-heating:heatpumpbridge:hpmanager:Rubin_CombinedHeatpumpState"} +``` \ No newline at end of file diff --git a/bundles/org.openhab.binding.kermi/pom.xml b/bundles/org.openhab.binding.kermi/pom.xml index 3ba506666823d..f2b8ca5dfc3fa 100644 --- a/bundles/org.openhab.binding.kermi/pom.xml +++ b/bundles/org.openhab.binding.kermi/pom.xml @@ -7,7 +7,7 @@ org.openhab.addons.bundles org.openhab.addons.reactor.bundles - 4.0.3-SNAPSHOT + 4.1.0-SNAPSHOT org.openhab.binding.kermi diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java index fda4729721574..3524ccfd25119 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java @@ -12,14 +12,17 @@ */ package org.openhab.binding.kermi.internal; +import java.io.File; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.OpenHAB; import org.openhab.core.thing.ThingTypeUID; /** * The {@link KermiBindingConstants} class defines common constants, which are * used across the whole binding. * - * @author marco@descher.at - Initial contribution + * @author Marco Descher - Initial contribution */ @NonNullByDefault public class KermiBindingConstants { @@ -45,13 +48,24 @@ public class KermiBindingConstants { public static final String WELL_KNOWN_NAME_FS_HEAT_TEMP_ACT = "BufferSystem_HeatingTemperatureActual"; public static final String WELL_KNOWN_NAME_COMB_HEATPUMP_STATE = "Rubin_CombinedHeatpumpState"; public static final String WELL_KNOWN_NAME_COMB_HEATPUMP_CURR_COP = "Rubin_CurrentCOP"; + public static final String WELL_KNOWN_NAME_CURR_OUT_CAP = "Rubin_CurrentOutputCapacity"; + public static final String WELL_KNOWN_NAME_HEAT_AIR_TEMPERATORE = "LuftTemperatur"; // All Urls - public static final String HPM_GETDEVICESBYFILTER_URL = "http://%IP%/api/Device/GetDevicesByFilter/00000000-0000-0000-0000-000000000000"; - public static final String HPM_GETDEVICE_URL = "http://%IP%/api/Device/GetDevice/00000000-0000-0000-0000-000000000000"; + public static final String HPM_DEVICE_GETDEVICESBYFILTER_URL = "http://%IP%/api/Device/GetDevicesByFilter/00000000-0000-0000-0000-000000000000"; + public static final String HPM_DEVICE_GETDEVICE_URL = "http://%IP%/api/Device/GetDevice/00000000-0000-0000-0000-000000000000"; + public static final String HPM_DEVICE_GETALLDEVICES_URL = "http://%IP%/api/Device/GetAllDevices/00000000-0000-0000-0000-000000000000"; + public static final String HPM_MENU_GETCHILDENTRIES_URL = "http://%IP%/api/Menu/GetChildEntries/00000000-0000-0000-0000-000000000000"; + public static final String HPM_DATAPOINT_READVALUES_URL = "http://%IP%/api/Datapoint/ReadValues/00000000-0000-0000-0000-000000000000"; public static String parseUrl(String url, String ip) { - return url.replace("%IP%", ip == null ? "" : ip.trim()); + return url.replace("%IP%", ip.trim()); + } + + public static File getKermiUserDataFolder() { + File kermiUserDataFolder = new File(OpenHAB.getUserDataFolder(), "binding.kermi"); + kermiUserDataFolder.mkdir(); + return kermiUserDataFolder; } } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiHandlerFactory.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiHandlerFactory.java index 4dab33e8d8aa4..21f54a8c4a0f8 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiHandlerFactory.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiHandlerFactory.java @@ -20,10 +20,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.kermi.internal.api.KermiHttpUtil; +import org.openhab.binding.kermi.internal.handler.KermiBaseThingHandler; import org.openhab.binding.kermi.internal.handler.KermiBridgeHandler; -import org.openhab.binding.kermi.internal.handler.KermiDrinkingWaterHeatingThingHandler; -import org.openhab.binding.kermi.internal.handler.KermiHeatpumpManagerThingHandler; -import org.openhab.binding.kermi.internal.handler.KermiHeatpumpThingHandler; import org.openhab.binding.kermi.internal.model.KermiSiteInfo; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Thing; @@ -37,7 +35,7 @@ * The {@link KermiHandlerFactory} is responsible for creating things and thing * handlers. * - * @author marco@descher.at - Initial contribution + * @author Marco Descher - Initial contribution */ @NonNullByDefault @Component(configurationPid = "binding.kermi", service = ThingHandlerFactory.class) @@ -75,11 +73,15 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { if (thingTypeUID.equals(THING_TYPE_BRIDGE)) { return new KermiBridgeHandler((Bridge) thing, httpUtil, kermiSiteInfo); } else if (thingTypeUID.equals(THING_TYPE_HEATPUMP_MANAGER)) { - return new KermiHeatpumpManagerThingHandler(thing, httpUtil, kermiSiteInfo); + KermiBaseThingHandler heatpumpManagerHandler = new KermiBaseThingHandler(thing, httpUtil, kermiSiteInfo); + heatpumpManagerHandler.setDeviceId(DEVICE_ID_HEATPUMP_MANAGER); + return heatpumpManagerHandler; } else if (thingTypeUID.equals(THING_TYPE_DRINKINGWATER_HEATING)) { - return new KermiDrinkingWaterHeatingThingHandler(thing, httpUtil, kermiSiteInfo); + return new KermiBaseThingHandler(thing, httpUtil, kermiSiteInfo); } else if (thingTypeUID.equals(THING_TYPE_HEATPUMP)) { - return new KermiHeatpumpThingHandler(thing, httpUtil, kermiSiteInfo); + return new KermiBaseThingHandler(thing, httpUtil, kermiSiteInfo); + } else if (thingTypeUID.equals(THING_TYPE_ROOM_HEATING)) { + return new KermiBaseThingHandler(thing, httpUtil, kermiSiteInfo); } return null; diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Bundle.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Bundle.java new file mode 100644 index 0000000000000..d020e02e1aff6 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Bundle.java @@ -0,0 +1,34 @@ +package org.openhab.binding.kermi.internal.api; + +import java.util.List; + +import com.google.gson.annotations.SerializedName; + +public class Bundle { + + @SerializedName("DatapointBundleId") + private String datapointBundleId; + + @SerializedName("Datapoints") + private List datapoints; + + @SerializedName("DisplayName") + private String displayName; + + public String getDatapointBundleId() { + return datapointBundleId; + } + + public void setDatapointBundleId(String datapointBundleId) { + this.datapointBundleId = datapointBundleId; + } + + public List getDatapoints() { + return datapoints; + } + + public void setDatapoints(List datapoints) { + this.datapoints = datapoints; + } + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Config.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Config.java index cbe3a0a0b8513..d17d3f3f32c71 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Config.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Config.java @@ -21,6 +21,9 @@ public class Config { @SerializedName("Unit") private String unit; + @SerializedName("DatapointType") + private int datapointType; + @SerializedName("PossibleValues") private Map possibleValues; @@ -64,6 +67,14 @@ public void setUnit(String unit) { this.unit = unit; } + public int getDatapointType() { + return datapointType; + } + + public void setDatapointType(int datapointType) { + this.datapointType = datapointType; + } + public Map getPossibleValues() { return possibleValues; } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Datapoint.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Datapoint.java new file mode 100644 index 0000000000000..0ead6bbf34937 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Datapoint.java @@ -0,0 +1,29 @@ +package org.openhab.binding.kermi.internal.api; + +import com.google.gson.annotations.SerializedName; + +public class Datapoint { + + @SerializedName("Config") + private Config config; + + @SerializedName("DatapointValue") + private DatapointValue datapointValue; + + public Config getConfig() { + return config; + } + + public void setConfig(Config config) { + this.config = config; + } + + public DatapointValue getDatapointValue() { + return datapointValue; + } + + public void setDatapointValue(DatapointValue datapointValue) { + this.datapointValue = datapointValue; + } + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointReadValuesResponse.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointReadValuesResponse.java new file mode 100644 index 0000000000000..133edcdd75fb0 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointReadValuesResponse.java @@ -0,0 +1,7 @@ +package org.openhab.binding.kermi.internal.api; + +import java.util.List; + +public class DatapointReadValuesResponse extends BaseResponse> { + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Device.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Device.java new file mode 100644 index 0000000000000..9983016daaa7a --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Device.java @@ -0,0 +1,5 @@ +package org.openhab.binding.kermi.internal.api; + +public class Device { + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/KermiHttpUtil.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/KermiHttpUtil.java index 76d4afa0e78d9..5c10bb27c1e63 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/KermiHttpUtil.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/KermiHttpUtil.java @@ -18,10 +18,11 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.util.Map; import java.util.Properties; +import java.util.Set; import java.util.concurrent.ExecutionException; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jetty.client.HttpResponseException; import org.openhab.binding.kermi.internal.KermiBindingConstants; import org.openhab.binding.kermi.internal.KermiCommunicationException; @@ -43,42 +44,24 @@ public class KermiHttpUtil { private String hostname = ""; private String password = ""; - private HttpUtil httpUtil; private Properties httpHeaders; private Gson gson; - private Map deviceInfo; - public KermiHttpUtil() { httpHeaders = new Properties(); gson = new Gson(); - httpUtil = new HttpUtil(); } - public void executeCheckBridgeOnline() throws KermiCommunicationException { - executeUrl("GET", "http://" + hostname, null, null); + private String getBaseApiUrl() { + return "http://" + hostname + "/api/"; } - public GetDevicesResponse getDevicesByFilter() throws KermiCommunicationException { - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("WithDetails", Boolean.FALSE); - jsonObject.addProperty("WithChildDevices", Boolean.FALSE); - jsonObject.addProperty("Recursive", Boolean.FALSE); - jsonObject.add("DeviceTypes", new JsonArray()); - jsonObject.add("MenuEntries", new JsonArray()); - String executeUrl = executeUrl("POST", parseUrl(KermiBindingConstants.HPM_GETDEVICESBYFILTER_URL, hostname), - jsonObject.toString(), CONTENT_TYPE); - return gson.fromJson(executeUrl, GetDevicesResponse.class); + public void setHostname(String hostname) { + this.hostname = hostname; } - public GetDeviceResponse getDeviceInfoByDeviceId(String deviceId) throws KermiCommunicationException { - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("DeviceId", deviceId); - jsonObject.addProperty("WithDetails", Boolean.TRUE); - jsonObject.addProperty("Recursive", Boolean.TRUE); - String executeUrl = executeUrl("POST", parseUrl(KermiBindingConstants.HPM_GETDEVICE_URL, hostname), - jsonObject.toString(), CONTENT_TYPE); - return gson.fromJson(executeUrl, GetDeviceResponse.class); + public void setPassword(String password) { + this.password = password; } /** @@ -89,6 +72,7 @@ public GetDeviceResponse getDeviceInfoByDeviceId(String deviceId) throws KermiCo * @return the response body * @throws KermiCommunicationException when the request execution failed or interrupted */ + @SuppressWarnings("null") public synchronized String executeUrl(String httpMethod, String url, String content, String contentType) throws KermiCommunicationException { @@ -156,20 +140,47 @@ private void performLogin() throws KermiCommunicationException { executeUrl("POST", getBaseApiUrl() + "Security/Login", jsonObject.toString(), CONTENT_TYPE); } - private String getBaseApiUrl() { - return "http://" + hostname + "/api/"; + public GetDevicesResponse getAllDevices() throws KermiCommunicationException { + String response = executeUrl("GET", parseUrl(KermiBindingConstants.HPM_DEVICE_GETALLDEVICES_URL, hostname), + null, null); + return gson.fromJson(response, GetDevicesResponse.class); } - public void setHostname(String hostname) { - this.hostname = hostname; - } + public MenuGetChildEntriesResponse getMenuChildEntries(String deviceId, @NonNull String parentMenuEntryId) + throws KermiCommunicationException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("DeviceId", deviceId); + jsonObject.addProperty("ParentMenuEntryId", parentMenuEntryId); + jsonObject.addProperty("WithDetails", Boolean.TRUE); - public void setPassword(String password) { - this.password = password; + String response = executeUrl("POST", parseUrl(KermiBindingConstants.HPM_MENU_GETCHILDENTRIES_URL, hostname), + jsonObject.toString(), CONTENT_TYPE); + return gson.fromJson(response, MenuGetChildEntriesResponse.class); } - public void setDeviceInfo(Map deviceInfo) { - this.deviceInfo = deviceInfo; + /** + * Fetch update datapoint values for the given idTuples + * + * @param idTuples with [0] being the DeviceId, and [1] being the DatapointConfigId + * @return + * @throws KermiCommunicationException + */ + public DatapointReadValuesResponse getDatapointReadValues(Set idTuples) + throws KermiCommunicationException { + + JsonObject jsonObject = new JsonObject(); + JsonArray datapointValues = new JsonArray(idTuples.size()); + jsonObject.add("DatapointValues", datapointValues); + idTuples.forEach(idt -> { + JsonObject _entryObject = new JsonObject(); + _entryObject.addProperty("DeviceId", idt[0]); + _entryObject.addProperty("DatapointConfigId", idt[1]); + datapointValues.add(_entryObject); + }); + + String response = executeUrl("POST", parseUrl(KermiBindingConstants.HPM_DATAPOINT_READVALUES_URL, hostname), + jsonObject.toString(), CONTENT_TYPE); + return gson.fromJson(response, DatapointReadValuesResponse.class); } } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntry.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntry.java new file mode 100644 index 0000000000000..00570abd84b1f --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntry.java @@ -0,0 +1,29 @@ +package org.openhab.binding.kermi.internal.api; + +import com.google.gson.annotations.SerializedName; + +public class MenuEntry { + + @SerializedName("MenuEntryId") + private String menuEntryId; + + @SerializedName("ParentMenuEntryId") + private String parentMenuEntryId; + + public String getMenuEntryId() { + return menuEntryId; + } + + public void setMenuEntryId(String menuEntryId) { + this.menuEntryId = menuEntryId; + } + + public String getParentMenuEntryId() { + return parentMenuEntryId; + } + + public void setParentMenuEntryId(String parentMenuEntryId) { + this.parentMenuEntryId = parentMenuEntryId; + } + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntryResponse.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntryResponse.java new file mode 100644 index 0000000000000..2f2e7e7655afe --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntryResponse.java @@ -0,0 +1,64 @@ +package org.openhab.binding.kermi.internal.api; + +import java.util.List; + +import com.google.gson.annotations.SerializedName; + +public class MenuEntryResponse { + + @SerializedName("DeviceId") + private String deviceId; + + @SerializedName("ParentMenuEntryId") + private String parentMenuEntryId; + + @SerializedName("MenuEntries") + private List menuEntries; + + @SerializedName("Bundles") + private List bundles; + + @SerializedName("Devices") + private List devices; + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getParentMenuEntryId() { + return parentMenuEntryId; + } + + public void setParentMenuEntryId(String parentMenuEntryId) { + this.parentMenuEntryId = parentMenuEntryId; + } + + public List getMenuEntries() { + return menuEntries; + } + + public void setMenuEntries(List menuEntries) { + this.menuEntries = menuEntries; + } + + public List getBundles() { + return bundles; + } + + public void setBundles(List bundles) { + this.bundles = bundles; + } + + public List getDevices() { + return devices; + } + + public void setDevices(List devices) { + this.devices = devices; + } + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuGetChildEntriesResponse.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuGetChildEntriesResponse.java new file mode 100644 index 0000000000000..133928a723c0a --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuGetChildEntriesResponse.java @@ -0,0 +1,5 @@ +package org.openhab.binding.kermi.internal.api; + +public class MenuGetChildEntriesResponse extends BaseResponse { + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java index b6590f320f5e4..c2787375169ad 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java @@ -12,10 +12,11 @@ */ package org.openhab.binding.kermi.internal.handler; -import org.eclipse.jdt.annotation.NonNull; -import org.openhab.binding.kermi.internal.KermiBridgeConfiguration; +import java.util.Map; + +import org.openhab.binding.kermi.internal.KermiBaseDeviceConfiguration; import org.openhab.binding.kermi.internal.KermiCommunicationException; -import org.openhab.binding.kermi.internal.api.BaseResponse; +import org.openhab.binding.kermi.internal.api.DeviceInfo; import org.openhab.binding.kermi.internal.api.KermiHttpUtil; import org.openhab.binding.kermi.internal.model.KermiSiteInfo; import org.openhab.core.thing.Bridge; @@ -32,25 +33,18 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.gson.Gson; -import com.google.gson.JsonSyntaxException; - -public abstract class KermiBaseThingHandler extends BaseThingHandler { +public class KermiBaseThingHandler extends BaseThingHandler { - private static final int API_TIMEOUT = 5000; private final Logger logger = LoggerFactory.getLogger(KermiBaseThingHandler.class); - private final String serviceDescription; - private KermiBaseThingHandler bridgeHandler; - private final Gson gson; private final KermiHttpUtil httpUtil; private final KermiSiteInfo kermiSiteInfo; + private String busAddress; + private String deviceId; public KermiBaseThingHandler(Thing thing, KermiHttpUtil httpUtil, KermiSiteInfo kermiSiteInfo) { super(thing); this.httpUtil = httpUtil; this.kermiSiteInfo = kermiSiteInfo; - gson = new Gson(); - serviceDescription = getDescription(); } public KermiHttpUtil getHttpUtil() { @@ -61,6 +55,12 @@ public KermiSiteInfo getKermiSiteInfo() { return kermiSiteInfo; } + @Override + public void channelLinked(ChannelUID channelUID) { + logger.info("channelLinked " + channelUID); + super.channelLinked(channelUID); + } + @Override public void handleCommand(ChannelUID channelUID, Command command) { if (command instanceof RefreshType) { @@ -70,7 +70,12 @@ public void handleCommand(ChannelUID channelUID, Command command) { @Override public void initialize() { - logger.debug("Initializing {} Service", serviceDescription); + KermiBaseDeviceConfiguration config = getConfigAs(KermiBaseDeviceConfiguration.class); + if (config.address != null) { + // null for heatpump-manager + busAddress = config.address.toString(); + } + logger.debug("Initializing busAddress {}", busAddress); // this is important so FroniusBridgeHandler::childHandlerInitialized gets called Bridge bridge = getBridge(); if (bridge == null || bridge.getHandler() == null) { @@ -82,6 +87,10 @@ public void initialize() { } } + public String getBusAddress() { + return busAddress; + } + /** * Update all Channels */ @@ -91,13 +100,23 @@ protected void updateChannels() { } } + public void updateProperties(DeviceInfo deviceInfo) { + if (deviceInfo == null) { + return; + } + + Map properties = editProperties(); + properties.put(Thing.PROPERTY_SERIAL_NUMBER, deviceInfo.getSerial()); + properties.put(Thing.PROPERTY_FIRMWARE_VERSION, deviceInfo.getSoftwareVersion()); + updateProperties(properties); + } + /** * Update the channel from the last data * * @param channelId the id identifying the channel to be updated */ protected void updateChannel(String channelId) { - System.out.println(channelId); if (!isLinked(channelId)) { return; } @@ -114,29 +133,36 @@ protected void updateChannel(String channelId) { updateState(channelId, state); } - /** - * return an internal description for logging - * - * @return the description of the thing - */ - protected abstract String getDescription(); + public String getDeviceId() { + return deviceId; + } - /** - * get the "new" associated value for a channelId - * - * @param channelId the id identifying the channel - * @return the "new" associated value - */ - protected abstract State getValue(String channelId); + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + protected State getValue(String channelId) { + if (getDeviceId() == null) { + return null; + } + + final String[] fields = channelId.split("#"); + if (fields.length < 1) { + return null; + } + + final String fieldName = fields[0]; + return getKermiSiteInfo().getStateByWellKnownName(fieldName, getDeviceId()); + } /** * Called by the bridge to fetch data and update channels * * @param bridgeConfiguration the connected bridge configuration */ - public void refresh(KermiBridgeConfiguration bridgeConfiguration) { + public void refresh() { try { - handleRefresh(bridgeConfiguration); + handleRefresh(); if (getThing().getStatus() != ThingStatus.ONLINE) { updateStatus(ThingStatus.ONLINE); } @@ -146,63 +172,18 @@ public void refresh(KermiBridgeConfiguration bridgeConfiguration) { } } - /** - * This method should be overridden to do whatever a thing must do to update its channels - * this function is called from the bridge in a given interval - * - * @param bridgeConfiguration the connected bridge configuration - */ - protected abstract void handleRefresh(KermiBridgeConfiguration bridgeConfiguration) - throws KermiCommunicationException; - - /** - * - * @param type response class type - * @param url to request - * @return the object representation of the json response - */ - protected @NonNull T collectDataFromUrl(Class type, String url) - throws KermiCommunicationException { - try { - int attempts = 1; - while (true) { - logger.trace("Fetching URL = {}", url); - // String response = KermiHttpUtil.executeUrl(url, API_TIMEOUT); - // logger.trace("aqiResponse = {}", response); - - // T result = gson.fromJson(response, type); - // if (result == null) { - // throw new KermiCommunicationException("Empty json result"); - // } - - // HeadStatus status = result.getHead().getStatus(); - // if (status.getCode() == 0) { - // return result; - // } - - // Sometimes Fronius would return a HTTP status 200 with a proper JSON data - // with Reason: Transfer timeout. - // - // "Status" : { - // "Code" : 8, - // "Reason" : "Transfer timeout.", - // "UserMessage" : "" - // }, - // logger.debug("Error from Fronius attempt #{}: {} - {}", attempts, status.getCode(), - // status.getReason()); - // if (attempts >= 3) { - // throw new KermiCommunicationException(status.getReason()); - // } - Thread.sleep(500 * attempts); - attempts++; + protected void handleRefresh() throws KermiCommunicationException { + if (getDeviceId() == null) { + DeviceInfo deviceInfo = getKermiSiteInfo().getDeviceInfoByAddress(getBusAddress()); + if (deviceInfo != null) { + setDeviceId(deviceInfo.getDeviceId()); + updateProperties(deviceInfo); + } else { + throw new KermiCommunicationException("Not yet initialized"); } - - } catch (JsonSyntaxException | NumberFormatException e) { - logger.debug("Received Invalid JSON Data", e); - throw new KermiCommunicationException("Invalid JSON data received", e); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new KermiCommunicationException("Data collection interrupted", e); } - } + + updateChannels(); + }; + } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java index e3fda10c777ef..2da4eea80b658 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java @@ -1,14 +1,20 @@ package org.openhab.binding.kermi.internal.handler; import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.kermi.internal.KermiBridgeConfiguration; import org.openhab.binding.kermi.internal.KermiCommunicationException; +import org.openhab.binding.kermi.internal.api.DeviceInfo; +import org.openhab.binding.kermi.internal.api.GetDevicesResponse; import org.openhab.binding.kermi.internal.api.KermiHttpUtil; import org.openhab.binding.kermi.internal.model.KermiSiteInfo; import org.openhab.core.thing.Bridge; @@ -65,6 +71,7 @@ public void initialize() { if (validConfig) { httpUtil.setHostname(config.hostname); httpUtil.setPassword(config.password); + startAutomaticRefresh(); } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, errorMsg); @@ -81,7 +88,6 @@ public void dispose() { @Override public void handleCommand(ChannelUID channelUID, Command command) { - System.out.println("hallo"); } @Override @@ -102,21 +108,30 @@ private void restartAutomaticRefresh() { } } + @SuppressWarnings("null") private void startAutomaticRefresh() { if (refreshJob == null || refreshJob.isCancelled()) { final KermiBridgeConfiguration config = getConfigAs(KermiBridgeConfiguration.class); Runnable runnable = () -> { try { - checkBridgeOnline(config); + for (KermiBaseThingHandler service : services) { + String deviceId = service.getDeviceId(); + if (deviceId != null) { + service.getThing().getChannels().stream().map(channel -> channel.getUID().getId()) + .forEach(channelId -> kermiSiteInfo.putRefreshBinding(channelId, deviceId)); + } + } + updateData(); if (getThing().getStatus() != ThingStatus.ONLINE) { updateStatus(ThingStatus.ONLINE); } for (KermiBaseThingHandler service : services) { - service.refresh(config); + service.refresh(); } } catch (KermiCommunicationException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage()); kermiSiteInfo.clearSiteInfo(); + logger.error("Communication error", e); } }; @@ -126,8 +141,15 @@ private void startAutomaticRefresh() { } - private void checkBridgeOnline(KermiBridgeConfiguration config) throws KermiCommunicationException { - httpUtil.executeCheckBridgeOnline(); + private void updateData() throws KermiCommunicationException { + if (!kermiSiteInfo.isInitialized()) { + GetDevicesResponse getDevicesResponse = httpUtil.getAllDevices(); + List deviceInfo = getDevicesResponse.getResponseData(); + Map _deviceInfo = deviceInfo.stream() + .collect(Collectors.toMap(DeviceInfo::getDeviceId, Function.identity())); + kermiSiteInfo.initializeSiteInfo(httpUtil, _deviceInfo); + } + kermiSiteInfo.updateStateValues(httpUtil); } } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiDrinkingWaterHeatingThingHandler.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiDrinkingWaterHeatingThingHandler.java deleted file mode 100644 index 15786ea045166..0000000000000 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiDrinkingWaterHeatingThingHandler.java +++ /dev/null @@ -1,100 +0,0 @@ -package org.openhab.binding.kermi.internal.handler; - -import java.util.Map; -import java.util.Optional; - -import org.openhab.binding.kermi.internal.KermiBaseDeviceConfiguration; -import org.openhab.binding.kermi.internal.KermiBindingConstants; -import org.openhab.binding.kermi.internal.KermiBridgeConfiguration; -import org.openhab.binding.kermi.internal.KermiCommunicationException; -import org.openhab.binding.kermi.internal.api.DatapointValue; -import org.openhab.binding.kermi.internal.api.DeviceInfo; -import org.openhab.binding.kermi.internal.api.GetDeviceResponse; -import org.openhab.binding.kermi.internal.api.KermiHttpUtil; -import org.openhab.binding.kermi.internal.model.KermiSiteInfo; -import org.openhab.binding.kermi.internal.model.KermiSiteInfoUtil; -import org.openhab.core.library.types.QuantityType; -import org.openhab.core.thing.Thing; -import org.openhab.core.types.State; - -import tech.units.indriya.unit.Units; - -public class KermiDrinkingWaterHeatingThingHandler extends KermiBaseThingHandler { - - private KermiBaseDeviceConfiguration config; - - public KermiDrinkingWaterHeatingThingHandler(Thing thing, KermiHttpUtil httpUtil, KermiSiteInfo kermiSiteInfo) { - super(thing, httpUtil, kermiSiteInfo); - } - - @Override - protected String getDescription() { - return "DrinkingWaterHeating"; - // DeviceInfo deviceInfo = getKermiSiteInfo().getDeviceInfoByAddress(config.address.toString()); - // return deviceInfo != null ? deviceInfo.getName() : "No device on address " + config.address; - } - - @Override - protected State getValue(String channelId) { - DeviceInfo deviceInfo = getKermiSiteInfo().getDeviceInfoByAddress(config.address.toString()); - if (deviceInfo == null) { - return null; - } - - final String[] fields = channelId.split("#"); - if (fields.length < 1) { - return null; - } - - final String fieldName = fields[0]; - Optional dpv = KermiSiteInfoUtil.getVisualizationDatapointValueByWellKnownName(fieldName, - deviceInfo); - if (!dpv.isPresent()) { - return null; - } - Object value = dpv.get().getValue(); - State result = null; - switch (fieldName) { - case KermiBindingConstants.WELL_KNOWN_NAME_BS_TWE_TEMP_ACT: { - result = new QuantityType<>((float) value, Units.CELSIUS); - } - break; - default: - break; - } - - return result; - } - - @Override - protected void handleRefresh(KermiBridgeConfiguration bridgeConfiguration) throws KermiCommunicationException { - updateData(); - updateChannels(); - updateProperties(); - } - - private void updateProperties() { - DeviceInfo deviceInfo = getKermiSiteInfo().getDeviceInfoByAddress(config.address.toString()); - if (deviceInfo == null) { - return; - } - - Map properties = editProperties(); - properties.put(Thing.PROPERTY_SERIAL_NUMBER, deviceInfo.getSerial()); - properties.put(Thing.PROPERTY_FIRMWARE_VERSION, deviceInfo.getSoftwareVersion()); - updateProperties(properties); - } - - private void updateData() throws KermiCommunicationException { - String deviceId = getKermiSiteInfo().getDeviceIdByAddress(config.address.toString()); - GetDeviceResponse deviceResponse = getHttpUtil().getDeviceInfoByDeviceId(deviceId); - getKermiSiteInfo().updateDeviceInfo(deviceId, deviceResponse.getResponseData()); - } - - @Override - public void initialize() { - config = getConfigAs(KermiBaseDeviceConfiguration.class); - super.initialize(); - } - -} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiHeatpumpManagerThingHandler.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiHeatpumpManagerThingHandler.java deleted file mode 100644 index 9bc0a0b5b8f1f..0000000000000 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiHeatpumpManagerThingHandler.java +++ /dev/null @@ -1,68 +0,0 @@ -package org.openhab.binding.kermi.internal.handler; - -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - -import org.eclipse.jdt.annotation.NonNull; -import org.openhab.binding.kermi.internal.KermiBridgeConfiguration; -import org.openhab.binding.kermi.internal.KermiCommunicationException; -import org.openhab.binding.kermi.internal.api.DeviceInfo; -import org.openhab.binding.kermi.internal.api.GetDevicesResponse; -import org.openhab.binding.kermi.internal.api.KermiHttpUtil; -import org.openhab.binding.kermi.internal.model.KermiSiteInfo; -import org.openhab.core.thing.Thing; -import org.openhab.core.types.State; - -public class KermiHeatpumpManagerThingHandler extends KermiBaseThingHandler { - - public KermiHeatpumpManagerThingHandler(Thing thing, @NonNull KermiHttpUtil httpUtil, KermiSiteInfo kermiSiteInfo) { - super(thing, httpUtil, kermiSiteInfo); - } - - @Override - protected String getDescription() { - return "HeatpumpManager"; - // DeviceInfo deviceInfo = getKermiSiteInfo().getHeatpumpManagerDeviceInfo(); - // return deviceInfo != null ? deviceInfo.getName() : "Unknown"; - } - - @Override - protected State getValue(String channelId) { - return null; - } - - @Override - protected void handleRefresh(KermiBridgeConfiguration bridgeConfiguration) throws KermiCommunicationException { - if (getKermiSiteInfo().getHeatpumpManagerDeviceInfo() == null) { - // no need to do this regularly - updateData(); - updateChannels(); - updateProperties(); - } - } - - private void updateData() throws KermiCommunicationException { - GetDevicesResponse getDevicesResponse = getHttpUtil().getDevicesByFilter(); - List deviceInfo = getDevicesResponse.getResponseData(); - Map _deviceInfo = deviceInfo.stream() - .collect(Collectors.toMap(DeviceInfo::getDeviceId, Function.identity())); - getKermiSiteInfo().updateSiteInfo(_deviceInfo); - } - - private void updateProperties() { - DeviceInfo deviceInfo = getKermiSiteInfo().getHeatpumpManagerDeviceInfo(); - if (deviceInfo == null) { - return; - } - - Map properties = editProperties(); - - properties.put(Thing.PROPERTY_FIRMWARE_VERSION, deviceInfo.getSoftwareVersion()); - properties.put(Thing.PROPERTY_SERIAL_NUMBER, deviceInfo.getSerial()); - - updateProperties(properties); - } - -} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiHeatpumpThingHandler.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiHeatpumpThingHandler.java deleted file mode 100644 index e105c04e59d7c..0000000000000 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiHeatpumpThingHandler.java +++ /dev/null @@ -1,97 +0,0 @@ -package org.openhab.binding.kermi.internal.handler; - -import java.util.Map; -import java.util.Optional; - -import org.openhab.binding.kermi.internal.KermiBaseDeviceConfiguration; -import org.openhab.binding.kermi.internal.KermiBindingConstants; -import org.openhab.binding.kermi.internal.KermiBridgeConfiguration; -import org.openhab.binding.kermi.internal.KermiCommunicationException; -import org.openhab.binding.kermi.internal.api.DatapointValue; -import org.openhab.binding.kermi.internal.api.DeviceInfo; -import org.openhab.binding.kermi.internal.api.GetDeviceResponse; -import org.openhab.binding.kermi.internal.api.KermiHttpUtil; -import org.openhab.binding.kermi.internal.model.KermiSiteInfo; -import org.openhab.binding.kermi.internal.model.KermiSiteInfoUtil; -import org.openhab.core.library.types.DecimalType; -import org.openhab.core.thing.Thing; -import org.openhab.core.types.State; - -public class KermiHeatpumpThingHandler extends KermiBaseThingHandler { - - private KermiBaseDeviceConfiguration config; - - public KermiHeatpumpThingHandler(Thing thing, KermiHttpUtil httpUtil, KermiSiteInfo kermiSiteInfo) { - super(thing, httpUtil, kermiSiteInfo); - } - - @Override - protected String getDescription() { - return "Heatpump"; - // DeviceInfo deviceInfo = getKermiSiteInfo().getDeviceInfoByAddress(config.address.toString()); - // return deviceInfo != null ? deviceInfo.getName() : "No device on address " + config.address; - } - - @Override - protected State getValue(String channelId) { - DeviceInfo deviceInfo = getKermiSiteInfo().getDeviceInfoByAddress(config.address.toString()); - if (deviceInfo == null) { - return null; - } - - final String[] fields = channelId.split("#"); - if (fields.length < 1) { - return null; - } - - final String fieldName = fields[0]; - Optional dpv = KermiSiteInfoUtil.getVisualizationDatapointValueByWellKnownName(fieldName, - deviceInfo); - if (!dpv.isPresent()) { - return null; - } - Object value = dpv.get().getValue(); - - switch (fieldName) { - case KermiBindingConstants.WELL_KNOWN_NAME_COMB_HEATPUMP_STATE: { - return new DecimalType((String) value); - } - default: - break; - } - - return null; - } - - @Override - protected void handleRefresh(KermiBridgeConfiguration bridgeConfiguration) throws KermiCommunicationException { - updateData(); - updateChannels(); - updateProperties(); - } - - private void updateProperties() { - DeviceInfo deviceInfo = getKermiSiteInfo().getDeviceInfoByAddress(config.address.toString()); - if (deviceInfo == null) { - return; - } - - Map properties = editProperties(); - properties.put(Thing.PROPERTY_SERIAL_NUMBER, deviceInfo.getSerial()); - properties.put(Thing.PROPERTY_FIRMWARE_VERSION, deviceInfo.getSoftwareVersion()); - updateProperties(properties); - } - - private void updateData() throws KermiCommunicationException { - String deviceId = getKermiSiteInfo().getDeviceIdByAddress(config.address.toString()); - GetDeviceResponse deviceResponse = getHttpUtil().getDeviceInfoByDeviceId(deviceId); - getKermiSiteInfo().updateDeviceInfo(deviceId, deviceResponse.getResponseData()); - } - - @Override - public void initialize() { - config = getConfigAs(KermiBaseDeviceConfiguration.class); - super.initialize(); - } - -} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java index 2a7dc7270ca66..defa2889c2af8 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java @@ -1,52 +1,144 @@ package org.openhab.binding.kermi.internal.model; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Set; +import org.apache.commons.collections4.BidiMap; +import org.apache.commons.collections4.bidimap.DualHashBidiMap; import org.eclipse.jdt.annotation.NonNull; import org.openhab.binding.kermi.internal.KermiBindingConstants; +import org.openhab.binding.kermi.internal.KermiCommunicationException; +import org.openhab.binding.kermi.internal.api.Datapoint; +import org.openhab.binding.kermi.internal.api.DatapointReadValuesResponse; +import org.openhab.binding.kermi.internal.api.DatapointValue; import org.openhab.binding.kermi.internal.api.DeviceInfo; +import org.openhab.binding.kermi.internal.api.KermiHttpUtil; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.types.State; +import org.slf4j.LoggerFactory; -import io.micrometer.core.instrument.util.StringUtils; +import tech.units.indriya.unit.Units; public class KermiSiteInfo { private Map deviceIdToDeviceInfo; - private Map addressToDeviceId; + + private Map dataPointConfigIdToDatapoint; + private BidiMap wellKnownNameToDatapointConfigId; + + // regularly updated + private Map wellKnownNameToCurrentState; + + // The WellKnownName datapoint values that are + // bound to a channel, and thus included in the + // refresh call + private Set wellKnownNameRefreshBinding; public KermiSiteInfo() { - deviceIdToDeviceInfo = Collections.synchronizedMap(new HashMap()); - addressToDeviceId = new HashMap(); + deviceIdToDeviceInfo = Collections.synchronizedMap(new HashMap<>()); + wellKnownNameToDatapointConfigId = new DualHashBidiMap<>(); + wellKnownNameRefreshBinding = Collections.synchronizedSet(new HashSet<>()); + wellKnownNameToCurrentState = Collections.synchronizedMap(new HashMap<>()); + dataPointConfigIdToDatapoint = Collections.synchronizedMap(new HashMap<>()); } public DeviceInfo getHeatpumpManagerDeviceInfo() { return deviceIdToDeviceInfo.get(KermiBindingConstants.DEVICE_ID_HEATPUMP_MANAGER); } - public DeviceInfo getDeviceInfoByAddress(String address) { - String deviceId = addressToDeviceId.get(address); - return deviceId != null ? deviceIdToDeviceInfo.get(deviceId) : null; - } - public void updateSiteInfo(Map deviceInfo) { clearSiteInfo(); deviceIdToDeviceInfo.putAll(deviceInfo); - deviceInfo.values().stream().filter(di -> StringUtils.isNotEmpty(di.getAddress())) - .forEach(di -> addressToDeviceId.put(di.getAddress(), di.getDeviceId())); } public void clearSiteInfo() { deviceIdToDeviceInfo.clear(); - addressToDeviceId.clear(); } - public String getDeviceIdByAddress(@NonNull String address) { - return addressToDeviceId.get(address); + public boolean isInitialized() { + return !deviceIdToDeviceInfo.isEmpty(); + } + + /** + * Initialize the information about this site. Iterate through all DeviceInfo elements to collect all + * VisualizationDataPoints + * + * @param _deviceInfo + * @throws KermiCommunicationException + */ + public void initializeSiteInfo(KermiHttpUtil httpUtil, Map<@NonNull String, @NonNull DeviceInfo> deviceInfo) + throws KermiCommunicationException { + clearSiteInfo(); + + deviceIdToDeviceInfo.putAll(deviceInfo); + + Collection devices = deviceIdToDeviceInfo.values(); + for (DeviceInfo device : devices) { + List deviceDatapoints = KermiSiteInfoUtil.collectDeviceDatapoints(httpUtil, device); + for (Datapoint datapoint : deviceDatapoints) { + String wellKnownName = datapoint.getConfig().getWellKnownName(); + if (wellKnownName != null) { + String datapointConfigId = datapoint.getConfig().getDatapointConfigId(); + wellKnownNameToDatapointConfigId.put(wellKnownName, datapointConfigId); + dataPointConfigIdToDatapoint.put(datapointConfigId, datapoint); + } + } + } + + } + + public void putRefreshBinding(@NonNull String wellKnownId, @NonNull String deviceId) { + String datapointConfigId = wellKnownNameToDatapointConfigId.get(wellKnownId); + wellKnownNameRefreshBinding.add(new String[] { deviceId, datapointConfigId }); + } + + public void updateStateValues(@NonNull KermiHttpUtil httpUtil) throws KermiCommunicationException { + if (wellKnownNameRefreshBinding.isEmpty()) { + return; + } + + DatapointReadValuesResponse datapointReadValues = httpUtil.getDatapointReadValues(wellKnownNameRefreshBinding); + List datapointValues = datapointReadValues.getResponseData(); + datapointValues.forEach(dpv -> { + // parallel stream? + String wellKnownName = wellKnownNameToDatapointConfigId.getKey(dpv.getDatapointConfigId()); + State currentState = convertDatapointValueToState(dpv); + wellKnownNameToCurrentState.put(wellKnownName, currentState); + + }); + } + + public State convertDatapointValueToState(DatapointValue datapointValue) { + // getDatapoint as resolved in #initializeSiteInfo + Datapoint datapoint = dataPointConfigIdToDatapoint.get(datapointValue.getDatapointConfigId()); + if (datapoint == null || datapoint.getConfig() == null) { + // TODO log? + return null; + } + + int datapointType = datapoint.getConfig().getDatapointType(); + if (1 == datapointType) { + return new QuantityType<>((double) datapointValue.getValue(), Units.CELSIUS); + } else if (2 == datapointType) { + // boolean + } + LoggerFactory.getLogger(getClass()).warn("Unknown datapointType {} in {}", datapointType, + datapoint.getConfig().getWellKnownName()); + return null; + } + + public State getStateByWellKnownName(String wellKnownName, String deviceId) { + return wellKnownNameToCurrentState.get(wellKnownName); } - public void updateDeviceInfo(String deviceId, DeviceInfo deviceInfo) { - deviceIdToDeviceInfo.put(deviceId, deviceInfo); + public DeviceInfo getDeviceInfoByAddress(String busAddress) { + return deviceIdToDeviceInfo.values().stream().filter(deviceInfo -> busAddress.equals(deviceInfo.getAddress())) + .findFirst().orElse(null); } } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtil.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtil.java index 1b637e0a79b09..d9fa8bfb8a92d 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtil.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtil.java @@ -1,26 +1,116 @@ package org.openhab.binding.kermi.internal.model; -import java.util.Optional; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; -import org.apache.commons.lang3.StringUtils; -import org.openhab.binding.kermi.internal.api.DatapointValue; +import org.openhab.binding.kermi.internal.KermiBindingConstants; +import org.openhab.binding.kermi.internal.KermiCommunicationException; +import org.openhab.binding.kermi.internal.api.Datapoint; import org.openhab.binding.kermi.internal.api.DeviceInfo; -import org.openhab.binding.kermi.internal.api.VisualizationDatapoint; +import org.openhab.binding.kermi.internal.api.KermiHttpUtil; +import org.openhab.binding.kermi.internal.api.MenuEntry; +import org.openhab.binding.kermi.internal.api.MenuEntryResponse; +import org.openhab.binding.kermi.internal.api.MenuGetChildEntriesResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.JsonIOException; +import com.google.gson.stream.JsonReader; public class KermiSiteInfoUtil { - public static Optional getVisualizationDatapointByWellKnownName(String wellKnownName, - DeviceInfo deviceInfo) { - return deviceInfo.getVisualizationDatapoints().stream().filter(vdp -> vdp.getConfig() != null) - .filter(vdp -> StringUtils.equalsIgnoreCase(wellKnownName, vdp.getConfig().getWellKnownName())) - .findFirst(); + private static Logger logger = LoggerFactory.getLogger(KermiSiteInfoUtil.class); + + /** + * Collects the datapoints for a given device, if a cache file is available, the cache is loaded + * + * @param httpUtil + * @param deviceInfo + * @return + * @throws KermiCommunicationException + * @throws IOException + * @throws JsonIOException + */ + public static List collectDeviceDatapoints(KermiHttpUtil httpUtil, DeviceInfo deviceInfo) + throws KermiCommunicationException { + + List dataPoints = loadDeviceDatapointsCache(deviceInfo); + if (dataPoints == null) { + logger.info("Collecting Datapoints for Device " + deviceInfo.getDeviceId()); + MenuGetChildEntriesResponse rootResponse = httpUtil.getMenuChildEntries(deviceInfo.getDeviceId(), + KermiBindingConstants.DEVICE_ID_HEATPUMP_MANAGER); + MenuEntryResponse rootChildEntry = rootResponse.getResponseData(); + + dataPoints = new ArrayList(); + collectAndTraverse(httpUtil, deviceInfo.getDeviceId(), dataPoints, rootChildEntry); + storeDeviceDatapointsCache(deviceInfo, dataPoints); + } + + return dataPoints; } - public static Optional getVisualizationDatapointValueByWellKnownName(String wellKnownName, - DeviceInfo deviceInfo) { - Optional visualizationDatapointByWellKnownName = getVisualizationDatapointByWellKnownName( - wellKnownName, deviceInfo); - return visualizationDatapointByWellKnownName.map(VisualizationDatapoint::getDatapointValue); + private static void storeDeviceDatapointsCache(DeviceInfo deviceInfo, List dataPoints) + throws KermiCommunicationException { + File file = new File(KermiBindingConstants.getKermiUserDataFolder(), + deviceInfo.getDeviceId() + "-" + deviceInfo.getSerial().trim() + ".json"); + + ListDatapointCacheFile listDatapointCacheFile = new ListDatapointCacheFile(); + listDatapointCacheFile.setDeviceId(deviceInfo.getDeviceId()); + listDatapointCacheFile.setSerial(deviceInfo.getSerial()); + + // clean the values, we don't need them + List datapointsWithoutValues = dataPoints.stream().map(dp -> { + dp.setDatapointValue(null); + return dp; + }).collect(Collectors.toList()); + listDatapointCacheFile.setDatapoints(datapointsWithoutValues); + try (FileWriter filewriter = new FileWriter(file, StandardCharsets.UTF_8)) { + new Gson().toJson(listDatapointCacheFile, filewriter); + } catch (JsonIOException | IOException e) { + throw new KermiCommunicationException(e); + } + } + + private static List loadDeviceDatapointsCache(DeviceInfo deviceInfo) { + File file = new File(KermiBindingConstants.getKermiUserDataFolder(), + deviceInfo.getDeviceId() + "-" + deviceInfo.getSerial().trim() + ".json"); + if (file.exists()) { + try (JsonReader reader = new JsonReader(new FileReader(file, StandardCharsets.UTF_8))) { + logger.debug("Loading cached datapoints for device " + deviceInfo.getDeviceId()); + ListDatapointCacheFile cacheFile = new Gson().fromJson(reader, ListDatapointCacheFile.class); + return cacheFile.getDatapoints(); + } catch (IOException e) { + logger.warn("Error loading device datapoint cache file", e); + } + } + + return null; + } + + private static void collectAndTraverse(KermiHttpUtil httpUtil, String deviceId, List dataPoints, + MenuEntryResponse menuEntry) throws KermiCommunicationException { + if (!menuEntry.getBundles().isEmpty()) { + menuEntry.getBundles().forEach(bundle -> dataPoints.addAll(bundle.getDatapoints())); + } + + List menuEntries = menuEntry.getMenuEntries(); + for (MenuEntry _menuEntry : menuEntries) { + try { + Thread.sleep(25); + } catch (InterruptedException e) { + // just some throttling + } + MenuGetChildEntriesResponse menuChildEntry = httpUtil.getMenuChildEntries(deviceId, + _menuEntry.getMenuEntryId()); + collectAndTraverse(httpUtil, deviceId, dataPoints, menuChildEntry.getResponseData()); + } } } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/ListDatapointCacheFile.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/ListDatapointCacheFile.java new file mode 100644 index 0000000000000..970c2acfacf26 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/ListDatapointCacheFile.java @@ -0,0 +1,44 @@ +package org.openhab.binding.kermi.internal.model; + +import java.util.List; + +import org.openhab.binding.kermi.internal.api.Datapoint; + +import com.google.gson.annotations.SerializedName; + +public class ListDatapointCacheFile { + + @SerializedName("DeviceId") + private String deviceId; + + @SerializedName("Serial") + private String serial; + + @SerializedName("Datapoints") + private List datapoints; + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getSerial() { + return serial; + } + + public void setSerial(String serial) { + this.serial = serial; + } + + public List getDatapoints() { + return datapoints; + } + + public void setDatapoints(List datapoints) { + this.datapoints = datapoints; + } + +} diff --git a/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/thing-types.xml index c9747a6b54dbd..c61a8c3baddba 100644 --- a/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/thing-types.xml @@ -31,7 +31,6 @@ 51 - @@ -54,6 +53,29 @@ + + + + + + + + + + + + + + + + + + Specific device identifier + 50 + + + + Number:Temperature diff --git a/bundles/org.openhab.binding.kermi/src/test/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtilTest.java b/bundles/org.openhab.binding.kermi/src/test/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtilTest.java index 314108a0b4f1b..a0ee5fa7c4e6b 100644 --- a/bundles/org.openhab.binding.kermi/src/test/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtilTest.java +++ b/bundles/org.openhab.binding.kermi/src/test/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtilTest.java @@ -1,15 +1,11 @@ package org.openhab.binding.kermi.internal.model; -import static org.junit.jupiter.api.Assertions.assertEquals; - import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import org.junit.jupiter.api.Test; -import org.openhab.binding.kermi.internal.KermiBindingConstants; import org.openhab.binding.kermi.internal.api.GetDeviceResponse; -import org.openhab.binding.kermi.internal.api.VisualizationDatapoint; import com.google.gson.Gson; @@ -24,9 +20,10 @@ public void getVisualizationDatapointByWellKnownName() throws IOException { getDeviceResponse = new Gson().fromJson(new InputStreamReader(resourceAsStream), GetDeviceResponse.class); } - VisualizationDatapoint visualizationDatapoint = KermiSiteInfoUtil.getVisualizationDatapointByWellKnownName( - KermiBindingConstants.WELL_KNOWN_NAME_BS_TWE_TEMP_ACT, getDeviceResponse.getResponseData()).get(); - assertEquals("06e61673-abc2-4671-9e5a-960809d1f326", visualizationDatapoint.getConfig().getDatapointConfigId()); + // VisualizationDatapoint visualizationDatapoint = KermiSiteInfoUtil.getVisualizationDatapointByWellKnownName( + // KermiBindingConstants.WELL_KNOWN_NAME_BS_TWE_TEMP_ACT, getDeviceResponse.getResponseData()).get(); + // assertEquals("06e61673-abc2-4671-9e5a-960809d1f326", + // visualizationDatapoint.getConfig().getDatapointConfigId()); } } diff --git a/bundles/org.openhab.binding.kermi/src/test/resources/getDeviceInfoByDeviceIdDrinkingWaterHeating.json b/bundles/org.openhab.binding.kermi/src/test/resources/getDeviceInfoByDeviceIdDrinkingWaterHeating.json deleted file mode 100644 index f3f3eec98121a..0000000000000 --- a/bundles/org.openhab.binding.kermi/src/test/resources/getDeviceInfoByDeviceIdDrinkingWaterHeating.json +++ /dev/null @@ -1,148 +0,0 @@ -{ - "ResponseData": { - "DeviceId": "2f82aa31-e57c-4212-872e-097fad62c30a", - "Protocol": 2, - "PortAddress": "/dev/ttymxc1", - "SoftwareVersion": "1.5.3", - "Address": "51", - "HomeServerSenderAddress": null, - "Name": "Trinkwassererwärmung (51)", - "Description": null, - "Serial": "29-41-00-75-a8-d5", - "ParentMenuEntryId": "00000000-0000-0000-0000-000000000000", - "ParentDeviceId": "00000000-0000-0000-0000-000000000000", - "DeviceType": 95, - "DeviceOptions": [ - { - "OptionId": "bdcd2755-6bf3-4d77-b742-f21be7801172", - "Name": "CoolingBufferEnabled", - "IsActivated": false - }, - { - "OptionId": "b7abc9eb-4d20-46a1-b3f9-e1a063da05ec", - "Name": "HeatingBufferEnabled", - "IsActivated": false - }, - { - "OptionId": "590dbd50-c0c4-4b6a-88e5-1a3d46628601", - "Name": "HeaterHeatingEnabled", - "IsActivated": false - }, - { - "OptionId": "cf83b44c-ca6e-4ea5-829c-eadff29ae7b2", - "Name": "HasOutsideTemperature", - "IsActivated": false - }, - { - "OptionId": "841e1039-fe57-4a46-b4ee-f9945447e2d7", - "Name": "Sensor2", - "IsActivated": false - }, - { - "OptionId": "e1340c32-cea9-41f1-a945-980c008b4246", - "Name": "Sensor4", - "IsActivated": false - }, - { - "OptionId": "9991809e-9fd8-42b8-bdad-492177554200", - "Name": "Sensor1", - "IsActivated": true - }, - { - "OptionId": "edf72fa5-c358-40cd-9f43-0b4fa4f4843a", - "Name": "edf72fa5-c358-40cd-9f43-0b4fa4f4843a", - "IsActivated": true - }, - { - "OptionId": "a8e2ee46-4f54-4f87-8f1a-b6f1e0e23c9d", - "Name": "a8e2ee46-4f54-4f87-8f1a-b6f1e0e23c9d", - "IsActivated": true - }, - { - "OptionId": "84922872-2d9b-46b2-aae8-30afd172fc31", - "Name": "84922872-2d9b-46b2-aae8-30afd172fc31", - "IsActivated": true - }, - { - "OptionId": "46ba01b3-284a-46ca-9d49-efa3ad453752", - "Name": "46ba01b3-284a-46ca-9d49-efa3ad453752", - "IsActivated": false - }, - { - "OptionId": "fbd7728b-792a-46ac-ab75-bb0226ec8b5a", - "Name": "Sensor3", - "IsActivated": false - }, - { - "OptionId": "955557bd-4596-485c-99c7-ed113607502f", - "Name": "955557bd-4596-485c-99c7-ed113607502f", - "IsActivated": false - } - ], - "VisualizationDatapoints": [ - { - "Config": { - "$type": "BMS.Shared.DatapointCore.DatapointConfig`1[[System.Single, mscorlib]], BMS.Shared", - "MaxValue": 0.0, - "MinValue": 0.0, - "DefaultValue": 0.0, - "Scale": 1.0, - "Offset": 0.0, - "PossibleValues": null, - "Colors": null, - "DatapointConfigId": "06e61673-abc2-4671-9e5a-960809d1f326", - "Category": 0, - "DisplayName": "Isttemperatur TWE", - "Description": "Anzeige der aktuellen Temperatur im Trinkwasserspeicher", - "UserLevelRead": 10, - "UserLevelWrite": 10, - "MenuEntryId": "fc642de6-9056-43c7-8469-5e2568db5e23", - "AllowedInAction": false, - "AllowedInCondition": true, - "Hidden": false, - "UIHint": "{ReadValueRound:0.1}", - "WriteOrder": 1, - "BundleId": "e786d99e-b08d-459e-a44e-43d5ea1e05fe", - "Unit": "°C", - "Sort": 2, - "ConfigVersion": 1, - "DatapointTarget": 1, - "DeviceType": 95, - "Address": null, - "DatapointType": 1, - "IsDeviceVisualizationDatapoint": true, - "VisualizationDatapointSort": 1, - "ColorKey": null, - "PossibleValueKey": null, - "WellKnownName": "BufferSystem_TweTemperatureActual", - "ImageName": "temp-ist_70x52", - "Ignore": false, - "DeviceOptions": { - "edf72fa5-c358-40cd-9f43-0b4fa4f4843a": "TweBufferEnabled" - } - }, - "DatapointValue": { - "$type": "BMS.Shared.DatapointCore.DatapointValue`1[[System.Single, mscorlib]], BMS.Shared", - "Value": 48.100002, - "DatapointConfigId": "06e61673-abc2-4671-9e5a-960809d1f326", - "DeviceId": "2f82aa31-e57c-4212-872e-097fad62c30a", - "Flags": 0 - } - } - ], - "IsSynced": true, - "IsOffline": false, - "LastChange": null, - "IsDisabled": null, - "ConfigVersion": 1, - "AccessControlList": null, - "CustomProperties": { - "WizardAnswer": "{\"$type\":\"BMS.Shared.Wizard.WizardAnswerBufferSystem, BMS.Shared\",\"BufferSystemType\":2,\"PowermoduleFunctionType\":2}" - }, - "Flags": 58 - }, - "StatusCode": 0, - "ExceptionData": null, - "DisplayText": "", - "DetailedText": "" -} From 5ca5a2b07cae346df91adfa85081e476c73e51b2 Mon Sep 17 00:00:00 2001 From: Marco Descher Date: Fri, 22 Sep 2023 20:09:42 +0200 Subject: [PATCH 03/12] 15680 org.openhab.binding.kermi initial contribution Signed-off-by: Marco Descher --- bundles/org.openhab.binding.kermi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.kermi/README.md b/bundles/org.openhab.binding.kermi/README.md index 7304abdad4361..53e412bf5d660 100644 --- a/bundles/org.openhab.binding.kermi/README.md +++ b/bundles/org.openhab.binding.kermi/README.md @@ -58,5 +58,5 @@ Number:Temperature Drinking_water_temperature {channel="kermi:drinkingwater-hea Number:Temperature Heating_current_temperature_buffer {channel="kermi:room-heating:heatpumpbridge:rheating:BufferSystem_HeatingTemperatureActual"} Number:Temperature Cooling_current_temperature_buffer {channel="kermi:room-heating:heatpumpbridge:rheating:BufferSystem_CoolingTemperatureActual"} -String Combined_Heatpump_State {channel="kermi:room-heating:heatpumpbridge:hpmanager:Rubin_CombinedHeatpumpState"} +Number:Temperature Outside_temperature {channel="kermi:room-heating:heatpumpbridge:rheating:LuftTemperatur"} ``` \ No newline at end of file From 1d0f98b9fdaf6047eff342044b5dfac4be2ec15d Mon Sep 17 00:00:00 2001 From: Marco Descher Date: Mon, 2 Oct 2023 14:00:30 +0200 Subject: [PATCH 04/12] 15680 org.openhab.binding.kermi pom.xml, build fixes Signed-off-by: Marco Descher --- bundles/org.openhab.binding.kermi/README.md | 2 +- .../KermiBaseDeviceConfiguration.java | 3 +++ .../kermi/internal/KermiBindingConstants.java | 1 - .../internal/KermiBridgeConfiguration.java | 15 ++++++++++++++ .../kermi/internal/api/BaseResponse.java | 16 ++++++++++++++- .../binding/kermi/internal/api/Bundle.java | 16 ++++++++++++++- .../binding/kermi/internal/api/Config.java | 16 ++++++++++++++- .../binding/kermi/internal/api/Datapoint.java | 16 ++++++++++++++- .../api/DatapointReadValuesResponse.java | 15 ++++++++++++++ .../kermi/internal/api/DatapointValue.java | 16 ++++++++++++++- .../binding/kermi/internal/api/Device.java | 15 ++++++++++++++ .../kermi/internal/api/DeviceInfo.java | 15 ++++++++++++++ .../kermi/internal/api/DeviceOption.java | 16 ++++++++++++++- .../kermi/internal/api/GetDeviceResponse.java | 15 ++++++++++++++ .../internal/api/GetDevicesResponse.java | 15 ++++++++++++++ .../kermi/internal/api/KermiHttpUtil.java | 4 +++- .../binding/kermi/internal/api/MenuEntry.java | 16 ++++++++++++++- .../kermi/internal/api/MenuEntryResponse.java | 16 ++++++++++++++- .../api/MenuGetChildEntriesResponse.java | 15 ++++++++++++++ .../internal/api/VisualizationDatapoint.java | 16 ++++++++++++++- .../handler/KermiBaseThingHandler.java | 6 ++++-- .../internal/handler/KermiBridgeHandler.java | 18 +++++++++++++++-- .../kermi/internal/model/KermiSiteInfo.java | 17 ++++++++++++++-- .../internal/model/KermiSiteInfoUtil.java | 20 ++++++++++++++++--- .../model/ListDatapointCacheFile.java | 16 ++++++++++++++- .../resources/OH-INF/i18n/kermi.properties | 2 +- bundles/pom.xml | 1 + 27 files changed, 316 insertions(+), 23 deletions(-) diff --git a/bundles/org.openhab.binding.kermi/README.md b/bundles/org.openhab.binding.kermi/README.md index 53e412bf5d660..342f0dbe489e7 100644 --- a/bundles/org.openhab.binding.kermi/README.md +++ b/bundles/org.openhab.binding.kermi/README.md @@ -59,4 +59,4 @@ Number:Temperature Heating_current_temperature_buffer {channel="kermi:room-heati Number:Temperature Cooling_current_temperature_buffer {channel="kermi:room-heating:heatpumpbridge:rheating:BufferSystem_CoolingTemperatureActual"} Number:Temperature Outside_temperature {channel="kermi:room-heating:heatpumpbridge:rheating:LuftTemperatur"} -``` \ No newline at end of file +``` diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBaseDeviceConfiguration.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBaseDeviceConfiguration.java index 030676bbf3243..e1e7a8e975237 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBaseDeviceConfiguration.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBaseDeviceConfiguration.java @@ -12,6 +12,9 @@ */ package org.openhab.binding.kermi.internal; +/** + * @author Marco Descher - intial implementation + */ public class KermiBaseDeviceConfiguration { public Integer address; } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java index 3524ccfd25119..aed410dd13526 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java @@ -67,5 +67,4 @@ public static File getKermiUserDataFolder() { kermiUserDataFolder.mkdir(); return kermiUserDataFolder; } - } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBridgeConfiguration.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBridgeConfiguration.java index b6f2d4370d2d1..df3ea9b0e1af4 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBridgeConfiguration.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBridgeConfiguration.java @@ -1,5 +1,20 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal; +/** + * @author Marco Descher - intial implementation + */ public class KermiBridgeConfiguration { public String hostname; public String password; diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/BaseResponse.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/BaseResponse.java index 6fb62df8a4060..108cad5fdb9b3 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/BaseResponse.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/BaseResponse.java @@ -1,7 +1,22 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal.api; import com.google.gson.annotations.SerializedName; +/** + * @author Marco Descher - intial implementation + */ public class BaseResponse { @SerializedName("ResponseData") @@ -58,5 +73,4 @@ public String getDetailedText() { public void setDetailedText(String detailedText) { this.detailedText = detailedText; } - } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Bundle.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Bundle.java index d020e02e1aff6..7e323c781c560 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Bundle.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Bundle.java @@ -1,9 +1,24 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal.api; import java.util.List; import com.google.gson.annotations.SerializedName; +/** + * @author Marco Descher - intial implementation + */ public class Bundle { @SerializedName("DatapointBundleId") @@ -30,5 +45,4 @@ public List getDatapoints() { public void setDatapoints(List datapoints) { this.datapoints = datapoints; } - } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Config.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Config.java index d17d3f3f32c71..91be8522dfcaf 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Config.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Config.java @@ -1,9 +1,24 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal.api; import java.util.Map; import com.google.gson.annotations.SerializedName; +/** + * @author Marco Descher - intial implementation + */ public class Config { @SerializedName("DatapointConfigId") @@ -82,5 +97,4 @@ public Map getPossibleValues() { public void setPossibleValues(Map possibleValues) { this.possibleValues = possibleValues; } - } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Datapoint.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Datapoint.java index 0ead6bbf34937..d698510ee881c 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Datapoint.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Datapoint.java @@ -1,7 +1,22 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal.api; import com.google.gson.annotations.SerializedName; +/** + * @author Marco Descher - intial implementation + */ public class Datapoint { @SerializedName("Config") @@ -25,5 +40,4 @@ public DatapointValue getDatapointValue() { public void setDatapointValue(DatapointValue datapointValue) { this.datapointValue = datapointValue; } - } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointReadValuesResponse.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointReadValuesResponse.java index 133edcdd75fb0..3e52c29704619 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointReadValuesResponse.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointReadValuesResponse.java @@ -1,7 +1,22 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal.api; import java.util.List; +/** + * @author Marco Descher - intial implementation + */ public class DatapointReadValuesResponse extends BaseResponse> { } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointValue.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointValue.java index cf93ae51105d8..95bffafaa7209 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointValue.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointValue.java @@ -1,7 +1,22 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal.api; import com.google.gson.annotations.SerializedName; +/** + * @author Marco Descher - intial implementation + */ public class DatapointValue { @SerializedName("Value") @@ -47,5 +62,4 @@ public int getFlags() { public void setFlags(int flags) { this.flags = flags; } - } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Device.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Device.java index 9983016daaa7a..7170d5cd6a837 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Device.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Device.java @@ -1,5 +1,20 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal.api; +/** + * @author Marco Descher - intial implementation + */ public class Device { } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceInfo.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceInfo.java index abb26e7cc707b..1aa1dd7e28e68 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceInfo.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceInfo.java @@ -1,9 +1,24 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal.api; import java.util.List; import com.google.gson.annotations.SerializedName; +/** + * @author Marco Descher - intial implementation + */ public class DeviceInfo { @SerializedName("DeviceId") diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceOption.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceOption.java index 1be2f844e9520..30e8274401cd4 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceOption.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceOption.java @@ -1,7 +1,22 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal.api; import com.google.gson.annotations.SerializedName; +/** + * @author Marco Descher - intial implementation + */ public class DeviceOption { @SerializedName("OptionId") @@ -36,5 +51,4 @@ public boolean isActivated() { public void setActivated(boolean isActivated) { this.isActivated = isActivated; } - } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDeviceResponse.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDeviceResponse.java index 10664d1f2209a..af2ff8c3f1ccc 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDeviceResponse.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDeviceResponse.java @@ -1,5 +1,20 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal.api; +/** + * @author Marco Descher - intial implementation + */ public class GetDeviceResponse extends BaseResponse { } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDevicesResponse.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDevicesResponse.java index 1f21517d64cf0..46095c6b4b20a 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDevicesResponse.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDevicesResponse.java @@ -1,7 +1,22 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal.api; import java.util.List; +/** + * @author Marco Descher - intial implementation + */ public class GetDevicesResponse extends BaseResponse> { } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/KermiHttpUtil.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/KermiHttpUtil.java index 5c10bb27c1e63..9b5e4c3b1d8df 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/KermiHttpUtil.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/KermiHttpUtil.java @@ -36,6 +36,9 @@ import io.micrometer.core.instrument.util.StringUtils; +/** + * @author Marco Descher - intial implementation + */ public class KermiHttpUtil { private static final String CONTENT_TYPE = "application/json; charset=utf-8"; @@ -182,5 +185,4 @@ public DatapointReadValuesResponse getDatapointReadValues(Set idTuples jsonObject.toString(), CONTENT_TYPE); return gson.fromJson(response, DatapointReadValuesResponse.class); } - } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntry.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntry.java index 00570abd84b1f..f81310767e395 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntry.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntry.java @@ -1,7 +1,22 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal.api; import com.google.gson.annotations.SerializedName; +/** + * @author Marco Descher - intial implementation + */ public class MenuEntry { @SerializedName("MenuEntryId") @@ -25,5 +40,4 @@ public String getParentMenuEntryId() { public void setParentMenuEntryId(String parentMenuEntryId) { this.parentMenuEntryId = parentMenuEntryId; } - } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntryResponse.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntryResponse.java index 2f2e7e7655afe..67e8c558a5b6f 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntryResponse.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntryResponse.java @@ -1,9 +1,24 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal.api; import java.util.List; import com.google.gson.annotations.SerializedName; +/** + * @author Marco Descher - intial implementation + */ public class MenuEntryResponse { @SerializedName("DeviceId") @@ -60,5 +75,4 @@ public List getDevices() { public void setDevices(List devices) { this.devices = devices; } - } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuGetChildEntriesResponse.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuGetChildEntriesResponse.java index 133928a723c0a..2875822c61344 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuGetChildEntriesResponse.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuGetChildEntriesResponse.java @@ -1,5 +1,20 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal.api; +/** + * @author Marco Descher - intial implementation + */ public class MenuGetChildEntriesResponse extends BaseResponse { } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/VisualizationDatapoint.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/VisualizationDatapoint.java index 29afb993546a5..148d0567cc025 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/VisualizationDatapoint.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/VisualizationDatapoint.java @@ -1,7 +1,22 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal.api; import com.google.gson.annotations.SerializedName; +/** + * @author Marco Descher - intial implementation + */ public class VisualizationDatapoint { @SerializedName("Config") @@ -25,5 +40,4 @@ public DatapointValue getDatapointValue() { public void setDatapointValue(DatapointValue datapointValue) { this.datapointValue = datapointValue; } - } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java index c2787375169ad..7046b2d964b06 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java @@ -33,6 +33,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * @author Marco Descher - intial implementation + */ public class KermiBaseThingHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(KermiBaseThingHandler.class); @@ -57,7 +60,7 @@ public KermiSiteInfo getKermiSiteInfo() { @Override public void channelLinked(ChannelUID channelUID) { - logger.info("channelLinked " + channelUID); + logger.info("channelLinked {}", channelUID); super.channelLinked(channelUID); } @@ -185,5 +188,4 @@ protected void handleRefresh() throws KermiCommunicationException { updateChannels(); }; - } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java index 2da4eea80b658..aff74a1044c44 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java @@ -1,3 +1,16 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal.handler; import java.util.HashSet; @@ -28,6 +41,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * @author Marco Descher - intial implementation + */ @NonNullByDefault public class KermiBridgeHandler extends BaseBridgeHandler { @@ -138,7 +154,6 @@ private void startAutomaticRefresh() { int delay = (config.refreshInterval != null) ? config.refreshInterval.intValue() : DEFAULT_REFRESH_PERIOD; refreshJob = scheduler.scheduleWithFixedDelay(runnable, 1, delay, TimeUnit.SECONDS); } - } private void updateData() throws KermiCommunicationException { @@ -151,5 +166,4 @@ private void updateData() throws KermiCommunicationException { } kermiSiteInfo.updateStateValues(httpUtil); } - } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java index defa2889c2af8..8d6b7f8a7fc2c 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java @@ -1,3 +1,15 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal.model; import java.util.Collection; @@ -24,6 +36,9 @@ import tech.units.indriya.unit.Units; +/** + * @author Marco Descher - intial implementation + */ public class KermiSiteInfo { private Map deviceIdToDeviceInfo; @@ -89,7 +104,6 @@ public void initializeSiteInfo(KermiHttpUtil httpUtil, Map<@NonNull String, @Non } } } - } public void putRefreshBinding(@NonNull String wellKnownId, @NonNull String deviceId) { @@ -140,5 +154,4 @@ public DeviceInfo getDeviceInfoByAddress(String busAddress) { return deviceIdToDeviceInfo.values().stream().filter(deviceInfo -> busAddress.equals(deviceInfo.getAddress())) .findFirst().orElse(null); } - } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtil.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtil.java index d9fa8bfb8a92d..5162893c606aa 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtil.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtil.java @@ -1,3 +1,15 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal.model; import java.io.File; @@ -24,6 +36,9 @@ import com.google.gson.JsonIOException; import com.google.gson.stream.JsonReader; +/** + * @author Marco Descher - intial implementation + */ public class KermiSiteInfoUtil { private static Logger logger = LoggerFactory.getLogger(KermiSiteInfoUtil.class); @@ -43,7 +58,7 @@ public static List collectDeviceDatapoints(KermiHttpUtil httpUtil, De List dataPoints = loadDeviceDatapointsCache(deviceInfo); if (dataPoints == null) { - logger.info("Collecting Datapoints for Device " + deviceInfo.getDeviceId()); + logger.info("Collecting Datapoints for Device {}", deviceInfo.getDeviceId()); MenuGetChildEntriesResponse rootResponse = httpUtil.getMenuChildEntries(deviceInfo.getDeviceId(), KermiBindingConstants.DEVICE_ID_HEATPUMP_MANAGER); MenuEntryResponse rootChildEntry = rootResponse.getResponseData(); @@ -83,7 +98,7 @@ private static List loadDeviceDatapointsCache(DeviceInfo deviceInfo) deviceInfo.getDeviceId() + "-" + deviceInfo.getSerial().trim() + ".json"); if (file.exists()) { try (JsonReader reader = new JsonReader(new FileReader(file, StandardCharsets.UTF_8))) { - logger.debug("Loading cached datapoints for device " + deviceInfo.getDeviceId()); + logger.debug("Loading cached datapoints for device {}", deviceInfo.getDeviceId()); ListDatapointCacheFile cacheFile = new Gson().fromJson(reader, ListDatapointCacheFile.class); return cacheFile.getDatapoints(); } catch (IOException e) { @@ -112,5 +127,4 @@ private static void collectAndTraverse(KermiHttpUtil httpUtil, String deviceId, collectAndTraverse(httpUtil, deviceId, dataPoints, menuChildEntry.getResponseData()); } } - } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/ListDatapointCacheFile.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/ListDatapointCacheFile.java index 970c2acfacf26..300e03c8b9add 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/ListDatapointCacheFile.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/ListDatapointCacheFile.java @@ -1,3 +1,15 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal.model; import java.util.List; @@ -6,6 +18,9 @@ import com.google.gson.annotations.SerializedName; +/** + * @author Marco Descher - intial implementation + */ public class ListDatapointCacheFile { @SerializedName("DeviceId") @@ -40,5 +55,4 @@ public List getDatapoints() { public void setDatapoints(List datapoints) { this.datapoints = datapoints; } - } diff --git a/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/i18n/kermi.properties b/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/i18n/kermi.properties index be7f6ca7ba82e..0c2f44015a92d 100644 --- a/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/i18n/kermi.properties +++ b/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/i18n/kermi.properties @@ -1,3 +1,3 @@ # FIXME: please add all English translations to this file so the texts can be translated using Crowdin # FIXME: to generate the content of this file run: mvn i18n:generate-default-translations -# FIXME: see also: https://www.openhab.org/docs/developer/utils/i18n.html \ No newline at end of file +# FIXME: see also: https://www.openhab.org/docs/developer/utils/i18n.html diff --git a/bundles/pom.xml b/bundles/pom.xml index b58688b52dbfa..0ec9797470b8d 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -197,6 +197,7 @@ org.openhab.binding.juicenet org.openhab.binding.kaleidescape org.openhab.binding.keba + org.openhab.binding.kermi org.openhab.binding.km200 org.openhab.binding.knx org.openhab.binding.kodi From 02899a49ebbf9f7d15cecdf7a28900f3c160df6a Mon Sep 17 00:00:00 2001 From: Marco Descher Date: Mon, 2 Oct 2023 14:33:47 +0200 Subject: [PATCH 05/12] 15680 org.openhab.binding.kermi pom.xml commons-collections4 dependency Signed-off-by: Marco Descher --- bom/openhab-addons/pom.xml | 5 +++++ bundles/org.openhab.binding.kermi/pom.xml | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index 95eba8ac828c2..4c1495f4e8252 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -816,6 +816,11 @@ org.openhab.binding.keba ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.kermi + ${project.version} + org.openhab.addons.bundles org.openhab.binding.km200 diff --git a/bundles/org.openhab.binding.kermi/pom.xml b/bundles/org.openhab.binding.kermi/pom.xml index f2b8ca5dfc3fa..08b2655fd0e22 100644 --- a/bundles/org.openhab.binding.kermi/pom.xml +++ b/bundles/org.openhab.binding.kermi/pom.xml @@ -14,4 +14,13 @@ openHAB Add-ons :: Bundles :: Kermi Binding + + + org.apache.commons + commons-collections4 + 4.1 + compile + + + From 69db59c0a152301de66b68bd32550a6b19acd413 Mon Sep 17 00:00:00 2001 From: Marco Descher Date: Mon, 2 Oct 2023 17:24:55 +0200 Subject: [PATCH 06/12] 15680 org.openhab.binding.kermi minor fixes Signed-off-by: Marco Descher --- .../openhab/binding/kermi/internal/KermiBindingConstants.java | 2 +- .../org/openhab/binding/kermi/internal/api/KermiHttpUtil.java | 1 - .../binding/kermi/internal/handler/KermiBaseThingHandler.java | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java index aed410dd13526..2e36a098adb81 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java @@ -49,7 +49,7 @@ public class KermiBindingConstants { public static final String WELL_KNOWN_NAME_COMB_HEATPUMP_STATE = "Rubin_CombinedHeatpumpState"; public static final String WELL_KNOWN_NAME_COMB_HEATPUMP_CURR_COP = "Rubin_CurrentCOP"; public static final String WELL_KNOWN_NAME_CURR_OUT_CAP = "Rubin_CurrentOutputCapacity"; - public static final String WELL_KNOWN_NAME_HEAT_AIR_TEMPERATORE = "LuftTemperatur"; + public static final String WELL_KNOWN_NAME_HEAT_AIR_TEMPERATURE = "LuftTemperatur"; // All Urls public static final String HPM_DEVICE_GETDEVICESBYFILTER_URL = "http://%IP%/api/Device/GetDevicesByFilter/00000000-0000-0000-0000-000000000000"; diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/KermiHttpUtil.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/KermiHttpUtil.java index 9b5e4c3b1d8df..5bdaaa4e04b03 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/KermiHttpUtil.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/KermiHttpUtil.java @@ -124,7 +124,6 @@ public synchronized String executeUrl(String httpMethod, String url, String cont if (attemptCount >= 3) { logger.debug("Failed connecting to {} after {} attempts.", url, attemptCount, lastException); - // throw new FroniusCommunicationException("Unable to connect", lastException); } logger.debug("HTTP error on attempt #{} {}", attemptCount, url); diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java index 7046b2d964b06..ce268eca3e4f7 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java @@ -79,7 +79,6 @@ public void initialize() { busAddress = config.address.toString(); } logger.debug("Initializing busAddress {}", busAddress); - // this is important so FroniusBridgeHandler::childHandlerInitialized gets called Bridge bridge = getBridge(); if (bridge == null || bridge.getHandler() == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED); From 731087bbe8901eba4ae85f25e6209578dfa4c3c2 Mon Sep 17 00:00:00 2001 From: Marco Descher Date: Fri, 13 Oct 2023 14:17:21 +0200 Subject: [PATCH 07/12] Dynamically resolve all channels, multiple updates --- bundles/org.openhab.binding.kermi/README.md | 8 +- .../kermi/internal/KermiBindingConstants.java | 6 + ...ory.java => KermiThingHandlerFactory.java} | 10 +- .../handler/KermiBaseThingHandler.java | 113 +++++++++++++----- .../handler/KermiBaseThingHandlerUtil.java | 46 +++++++ .../internal/handler/KermiBridgeHandler.java | 30 +++-- .../kermi/internal/model/KermiSiteInfo.java | 41 ++++++- .../internal/model/KermiSiteInfoUtil.java | 20 ++++ .../model/ListDatapointCacheFile.java | 22 ++++ .../src/main/resources/OH-INF/addon/addon.xml | 3 +- .../resources/OH-INF/config/deviceConfig.xml | 15 +++ .../resources/OH-INF/thing/thing-types.xml | 45 +++---- .../internal/model/KermiSiteInfoUtilTest.java | 29 ----- 13 files changed, 281 insertions(+), 107 deletions(-) rename bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/{KermiHandlerFactory.java => KermiThingHandlerFactory.java} (87%) create mode 100644 bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandlerUtil.java create mode 100644 bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/config/deviceConfig.xml delete mode 100644 bundles/org.openhab.binding.kermi/src/test/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtilTest.java diff --git a/bundles/org.openhab.binding.kermi/README.md b/bundles/org.openhab.binding.kermi/README.md index 342f0dbe489e7..47affc9d15021 100644 --- a/bundles/org.openhab.binding.kermi/README.md +++ b/bundles/org.openhab.binding.kermi/README.md @@ -32,8 +32,6 @@ The gathered data is then stored in `OH_USERDATA/binding.kermi` and loaded on su changes. The cache data is bound to the device-uuid and its serial number. If these values change, the datapoints are automatically re-initialized. - - ## Binding Configuration The following samples are provided, representing the current state of my usage. @@ -59,4 +57,10 @@ Number:Temperature Heating_current_temperature_buffer {channel="kermi:room-heati Number:Temperature Cooling_current_temperature_buffer {channel="kermi:room-heating:heatpumpbridge:rheating:BufferSystem_CoolingTemperatureActual"} Number:Temperature Outside_temperature {channel="kermi:room-heating:heatpumpbridge:rheating:LuftTemperatur"} + +Number:Power Current_Power_Inverter {channel="kermi:heatpump:heatpumpbridge:heatpump:Rubin_CurrentPowerInverter"} ``` + +# ToDo / Future Tasks + +* Support `DatapointType==0` and `DatapointType>2` diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java index 2e36a098adb81..ccf8008053cb1 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java @@ -17,6 +17,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.OpenHAB; import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.type.ChannelTypeUID; /** * The {@link KermiBindingConstants} class defines common constants, which are @@ -37,6 +38,11 @@ public class KermiBindingConstants { public static final ThingTypeUID THING_TYPE_HEATPUMP = new ThingTypeUID(BINDING_ID, "heatpump"); public static final ThingTypeUID THING_TYPE_HEATPUMP_MANAGER = new ThingTypeUID(BINDING_ID, "heatpump-manager"); + public static final ChannelTypeUID CHANNEL_TYPE_TEMPERATURE = new ChannelTypeUID(BINDING_ID, "temperature"); + public static final ChannelTypeUID CHANNEL_TYPE_POWER = new ChannelTypeUID(BINDING_ID, "power"); + public static final ChannelTypeUID CHANNEL_TYPE_NUMBER = new ChannelTypeUID(BINDING_ID, "number"); + public static final ChannelTypeUID CHANNEL_TYPE_ONOFF = new ChannelTypeUID(BINDING_ID, "onoff"); + // Device Constants public static final String DEVICE_ID_HEATPUMP_MANAGER = "00000000-0000-0000-0000-000000000000"; public static final int DEVICE_TYPE_HEATING_SYSTEM = 95; diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiHandlerFactory.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiThingHandlerFactory.java similarity index 87% rename from bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiHandlerFactory.java rename to bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiThingHandlerFactory.java index 21f54a8c4a0f8..32466ef348b4d 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiHandlerFactory.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiThingHandlerFactory.java @@ -32,19 +32,19 @@ import org.osgi.service.component.annotations.Component; /** - * The {@link KermiHandlerFactory} is responsible for creating things and thing + * The {@link KermiThingHandlerFactory} is responsible for creating things and thing * handlers. * * @author Marco Descher - Initial contribution */ @NonNullByDefault @Component(configurationPid = "binding.kermi", service = ThingHandlerFactory.class) -public class KermiHandlerFactory extends BaseThingHandlerFactory { +public class KermiThingHandlerFactory extends BaseThingHandlerFactory { private KermiHttpUtil httpUtil; private KermiSiteInfo kermiSiteInfo; - public KermiHandlerFactory() { + public KermiThingHandlerFactory() { httpUtil = new KermiHttpUtil(); kermiSiteInfo = new KermiSiteInfo(); } @@ -73,9 +73,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { if (thingTypeUID.equals(THING_TYPE_BRIDGE)) { return new KermiBridgeHandler((Bridge) thing, httpUtil, kermiSiteInfo); } else if (thingTypeUID.equals(THING_TYPE_HEATPUMP_MANAGER)) { - KermiBaseThingHandler heatpumpManagerHandler = new KermiBaseThingHandler(thing, httpUtil, kermiSiteInfo); - heatpumpManagerHandler.setDeviceId(DEVICE_ID_HEATPUMP_MANAGER); - return heatpumpManagerHandler; + return new KermiBaseThingHandler(thing, httpUtil, kermiSiteInfo); } else if (thingTypeUID.equals(THING_TYPE_DRINKINGWATER_HEATING)) { return new KermiBaseThingHandler(thing, httpUtil, kermiSiteInfo); } else if (thingTypeUID.equals(THING_TYPE_HEATPUMP)) { diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java index ce268eca3e4f7..1797e2c9c1945 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java @@ -12,20 +12,31 @@ */ package org.openhab.binding.kermi.internal.handler; +import java.util.List; import java.util.Map; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; import org.openhab.binding.kermi.internal.KermiBaseDeviceConfiguration; +import org.openhab.binding.kermi.internal.KermiBindingConstants; import org.openhab.binding.kermi.internal.KermiCommunicationException; +import org.openhab.binding.kermi.internal.api.Config; +import org.openhab.binding.kermi.internal.api.Datapoint; import org.openhab.binding.kermi.internal.api.DeviceInfo; import org.openhab.binding.kermi.internal.api.KermiHttpUtil; import org.openhab.binding.kermi.internal.model.KermiSiteInfo; +import org.openhab.binding.kermi.internal.model.KermiSiteInfoUtil; import org.openhab.core.thing.Bridge; 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.ThingUID; import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.thing.binding.builder.ThingBuilder; +import org.openhab.core.thing.type.ChannelTypeUID; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; import org.openhab.core.types.State; @@ -41,6 +52,7 @@ public class KermiBaseThingHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(KermiBaseThingHandler.class); private final KermiHttpUtil httpUtil; private final KermiSiteInfo kermiSiteInfo; + private final KermiBaseThingHandlerUtil kermiBaseThingHandlerUtil; private String busAddress; private String deviceId; @@ -48,6 +60,7 @@ public KermiBaseThingHandler(Thing thing, KermiHttpUtil httpUtil, KermiSiteInfo super(thing); this.httpUtil = httpUtil; this.kermiSiteInfo = kermiSiteInfo; + this.kermiBaseThingHandlerUtil = new KermiBaseThingHandlerUtil(); } public KermiHttpUtil getHttpUtil() { @@ -60,10 +73,18 @@ public KermiSiteInfo getKermiSiteInfo() { @Override public void channelLinked(ChannelUID channelUID) { - logger.info("channelLinked {}", channelUID); + kermiSiteInfo.putRefreshBinding(channelUID.getId(), deviceId); + logger.trace("Thing {} linked channel {}", getThing().getUID(), channelUID); super.channelLinked(channelUID); } + @Override + public void channelUnlinked(ChannelUID channelUID) { + kermiSiteInfo.removeRefreshBinding(channelUID.getId(), deviceId); + logger.trace("Thing {} unlinked channel {}", getThing().getUID(), channelUID); + super.channelUnlinked(channelUID); + } + @Override public void handleCommand(ChannelUID channelUID, Command command) { if (command instanceof RefreshType) { @@ -73,12 +94,17 @@ public void handleCommand(ChannelUID channelUID, Command command) { @Override public void initialize() { - KermiBaseDeviceConfiguration config = getConfigAs(KermiBaseDeviceConfiguration.class); - if (config.address != null) { - // null for heatpump-manager + + if (KermiBindingConstants.THING_TYPE_HEATPUMP_MANAGER.equals(getThing().getThingTypeUID())) { + deviceId = KermiBindingConstants.DEVICE_ID_HEATPUMP_MANAGER; + logger.debug("Initializing heatpump-manager with deviceId {}", deviceId); + } else { + KermiBaseDeviceConfiguration config = getConfigAs(KermiBaseDeviceConfiguration.class); busAddress = config.address.toString(); + deviceId = kermiSiteInfo.getDeviceInfoByAddress(busAddress).getDeviceId(); + logger.debug("Initializing busAddress {} with deviceId {}", busAddress, deviceId); } - logger.debug("Initializing busAddress {}", busAddress); + Bridge bridge = getBridge(); if (bridge == null || bridge.getHandler() == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED); @@ -87,6 +113,46 @@ public void initialize() { } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); } + + // determine channels for thing + // get device info by address + try { + ThingBuilder thingBuilder = editThing(); + DeviceInfo deviceInfo = kermiSiteInfo.getDeviceInfoByAddress(busAddress); + thingBuilder.withLabel(deviceInfo.getName()); + + List deviceDatapoints = KermiSiteInfoUtil.collectDeviceDatapoints(httpUtil, deviceInfo); + deviceDatapoints.forEach(datapoint -> addDatapointAsChannel(getThing().getUID(), datapoint, thingBuilder)); + updateThing(thingBuilder.build()); + } catch (KermiCommunicationException e) { + // updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); + logger.warn("Communication exception", e); + } + + getThing().getChannels().forEach(channel -> { + if (isLinked(channel.getUID())) { + channelLinked(channel.getUID()); + } + }); + } + + private void addDatapointAsChannel(@NonNull ThingUID thingUID, Datapoint datapoint, ThingBuilder thingBuilder) { + Config datapointConfig = datapoint.getConfig(); + ChannelTypeUID channelTypeUID = kermiBaseThingHandlerUtil.determineChannelTypeUID(datapointConfig); + if (channelTypeUID != null) { + if (StringUtils.isNotBlank(datapointConfig.getWellKnownName())) { + ChannelUID channelUID = new ChannelUID(getThing().getUID(), datapointConfig.getWellKnownName()); + + Channel channel = ChannelBuilder.create(channelUID).withType(channelTypeUID).withLabel(busAddress) + .withLabel(datapointConfig.getDisplayName()).withDescription(datapointConfig.getDescription()) + .build(); + thingBuilder.withChannel(channel); + logger.debug("{} added channel {}", thingUID, datapointConfig.getWellKnownName()); + } + } else { + logger.info("{} unsupported channel-type for datapointConfigId {}", thingUID, + datapointConfig.getDatapointConfigId()); + } } public String getBusAddress() { @@ -94,11 +160,13 @@ public String getBusAddress() { } /** - * Update all Channels + * Update linked channels */ protected void updateChannels() { for (Channel channel : getThing().getChannels()) { - updateChannel(channel.getUID().getId()); + if (isLinked(channel.getUID())) { + updateChannel(channel.getUID().getId()); + } } } @@ -107,9 +175,11 @@ public void updateProperties(DeviceInfo deviceInfo) { return; } - Map properties = editProperties(); + Map<@NonNull String, @NonNull String> properties = editProperties(); properties.put(Thing.PROPERTY_SERIAL_NUMBER, deviceInfo.getSerial()); properties.put(Thing.PROPERTY_FIRMWARE_VERSION, deviceInfo.getSoftwareVersion()); + properties.put("DeviceType", deviceInfo.getDeviceType()); + properties.put("DeviceId", deviceInfo.getDeviceId()); updateProperties(properties); } @@ -135,26 +205,14 @@ protected void updateChannel(String channelId) { updateState(channelId, state); } - public String getDeviceId() { - return deviceId; - } - - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } - protected State getValue(String channelId) { - if (getDeviceId() == null) { - return null; - } - final String[] fields = channelId.split("#"); if (fields.length < 1) { return null; } final String fieldName = fields[0]; - return getKermiSiteInfo().getStateByWellKnownName(fieldName, getDeviceId()); + return getKermiSiteInfo().getStateByWellKnownName(fieldName, deviceId); } /** @@ -175,14 +233,11 @@ public void refresh() { } protected void handleRefresh() throws KermiCommunicationException { - if (getDeviceId() == null) { - DeviceInfo deviceInfo = getKermiSiteInfo().getDeviceInfoByAddress(getBusAddress()); - if (deviceInfo != null) { - setDeviceId(deviceInfo.getDeviceId()); - updateProperties(deviceInfo); - } else { - throw new KermiCommunicationException("Not yet initialized"); - } + DeviceInfo deviceInfo = getKermiSiteInfo().getDeviceInfoByAddress(getBusAddress()); + if (deviceInfo != null) { + updateProperties(deviceInfo); + } else { + throw new KermiCommunicationException("Not yet initialized"); } updateChannels(); diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandlerUtil.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandlerUtil.java new file mode 100644 index 0000000000000..f7df148e3cb9b --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandlerUtil.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2010-2023 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.kermi.internal.handler; + +import javax.measure.Unit; + +import org.openhab.binding.kermi.internal.KermiBindingConstants; +import org.openhab.binding.kermi.internal.api.Config; +import org.openhab.binding.kermi.internal.model.KermiSiteInfoUtil; +import org.openhab.core.thing.type.ChannelTypeUID; + +import tech.units.indriya.unit.Units; + +/** + * @author Marco Descher - intial implementation + */ +public class KermiBaseThingHandlerUtil { + + public ChannelTypeUID determineChannelTypeUID(Config datapointConfig) { + switch (datapointConfig.getDatapointType()) { + case 1: + Unit unit = KermiSiteInfoUtil.determineUnitByString(datapointConfig.getUnit()); + if (Units.WATT.equals(unit)) { + return KermiBindingConstants.CHANNEL_TYPE_POWER; + } else if (Units.CELSIUS.equals(unit)) { + return KermiBindingConstants.CHANNEL_TYPE_TEMPERATURE; + } + return KermiBindingConstants.CHANNEL_TYPE_NUMBER; + case 2: + return KermiBindingConstants.CHANNEL_TYPE_ONOFF; + default: + break; + } + return null; + } +} diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java index aff74a1044c44..f7103fde97e43 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java @@ -88,7 +88,14 @@ public void initialize() { httpUtil.setHostname(config.hostname); httpUtil.setPassword(config.password); - startAutomaticRefresh(); + try { + initializeKermiSiteInfo(); + + startAutomaticRefresh(); + } catch (KermiCommunicationException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage()); + logger.error("Communication error", e); + } } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, errorMsg); } @@ -130,13 +137,6 @@ private void startAutomaticRefresh() { final KermiBridgeConfiguration config = getConfigAs(KermiBridgeConfiguration.class); Runnable runnable = () -> { try { - for (KermiBaseThingHandler service : services) { - String deviceId = service.getDeviceId(); - if (deviceId != null) { - service.getThing().getChannels().stream().map(channel -> channel.getUID().getId()) - .forEach(channelId -> kermiSiteInfo.putRefreshBinding(channelId, deviceId)); - } - } updateData(); if (getThing().getStatus() != ThingStatus.ONLINE) { updateStatus(ThingStatus.ONLINE); @@ -156,13 +156,17 @@ private void startAutomaticRefresh() { } } + private void initializeKermiSiteInfo() throws KermiCommunicationException { + GetDevicesResponse getDevicesResponse = httpUtil.getAllDevices(); + List deviceInfo = getDevicesResponse.getResponseData(); + Map _deviceInfo = deviceInfo.stream() + .collect(Collectors.toMap(DeviceInfo::getDeviceId, Function.identity())); + kermiSiteInfo.initializeSiteInfo(httpUtil, _deviceInfo); + } + private void updateData() throws KermiCommunicationException { if (!kermiSiteInfo.isInitialized()) { - GetDevicesResponse getDevicesResponse = httpUtil.getAllDevices(); - List deviceInfo = getDevicesResponse.getResponseData(); - Map _deviceInfo = deviceInfo.stream() - .collect(Collectors.toMap(DeviceInfo::getDeviceId, Function.identity())); - kermiSiteInfo.initializeSiteInfo(httpUtil, _deviceInfo); + initializeKermiSiteInfo(); } kermiSiteInfo.updateStateValues(httpUtil); } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java index 8d6b7f8a7fc2c..c14154556ed35 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java @@ -18,8 +18,11 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; +import javax.measure.Unit; + import org.apache.commons.collections4.BidiMap; import org.apache.commons.collections4.bidimap.DualHashBidiMap; import org.eclipse.jdt.annotation.NonNull; @@ -30,17 +33,21 @@ import org.openhab.binding.kermi.internal.api.DatapointValue; import org.openhab.binding.kermi.internal.api.DeviceInfo; import org.openhab.binding.kermi.internal.api.KermiHttpUtil; +import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.types.State; +import org.osgi.service.component.annotations.Component; +import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import tech.units.indriya.unit.Units; - /** * @author Marco Descher - intial implementation */ +@Component public class KermiSiteInfo { + private Logger logger = LoggerFactory.getLogger(KermiSiteInfo.class); + private Map deviceIdToDeviceInfo; private Map dataPointConfigIdToDatapoint; @@ -111,6 +118,11 @@ public void putRefreshBinding(@NonNull String wellKnownId, @NonNull String devic wellKnownNameRefreshBinding.add(new String[] { deviceId, datapointConfigId }); } + public void removeRefreshBinding(@NonNull String wellKnownId, @NonNull String deviceId) { + String datapointConfigId = wellKnownNameToDatapointConfigId.get(wellKnownId); + wellKnownNameRefreshBinding.remove(new String[] { deviceId, datapointConfigId }); + } + public void updateStateValues(@NonNull KermiHttpUtil httpUtil) throws KermiCommunicationException { if (wellKnownNameRefreshBinding.isEmpty()) { return; @@ -131,18 +143,35 @@ public State convertDatapointValueToState(DatapointValue datapointValue) { // getDatapoint as resolved in #initializeSiteInfo Datapoint datapoint = dataPointConfigIdToDatapoint.get(datapointValue.getDatapointConfigId()); if (datapoint == null || datapoint.getConfig() == null) { - // TODO log? + logger.warn("Could not determine datapoint for datapointConfigId {}", + datapointValue.getDatapointConfigId()); return null; } int datapointType = datapoint.getConfig().getDatapointType(); if (1 == datapointType) { - return new QuantityType<>((double) datapointValue.getValue(), Units.CELSIUS); + // Numeric value or "NaN" + Object value = datapointValue.getValue(); + if (Objects.equals("NaN", value)) { + return new QuantityType<>(); + } + Unit unit = KermiSiteInfoUtil.determineUnitByString(datapoint.getConfig().getUnit()); + if (value instanceof Double) { + // TODO if kw > multiply with 1000? + return new QuantityType<>((double) datapointValue.getValue(), unit); + } } else if (2 == datapointType) { - // boolean + // OnOff Type + Object value = datapointValue.getValue(); + if (value instanceof Boolean) { + return ((Boolean) value) ? OnOffType.ON : OnOffType.OFF; + } } - LoggerFactory.getLogger(getClass()).warn("Unknown datapointType {} in {}", datapointType, + + logger.warn("Unknown datapointType {} or datapointValue {} ({}) in {}", datapointType, + datapointValue.getValue(), datapointValue.getValue().getClass().getName(), datapoint.getConfig().getWellKnownName()); + return null; } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtil.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtil.java index 5162893c606aa..961e93119396f 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtil.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtil.java @@ -21,6 +21,8 @@ import java.util.List; import java.util.stream.Collectors; +import javax.measure.Unit; + import org.openhab.binding.kermi.internal.KermiBindingConstants; import org.openhab.binding.kermi.internal.KermiCommunicationException; import org.openhab.binding.kermi.internal.api.Datapoint; @@ -36,6 +38,8 @@ import com.google.gson.JsonIOException; import com.google.gson.stream.JsonReader; +import tech.units.indriya.unit.Units; + /** * @author Marco Descher - intial implementation */ @@ -79,6 +83,8 @@ private static void storeDeviceDatapointsCache(DeviceInfo deviceInfo, List datapointsWithoutValues = dataPoints.stream().map(dp -> { @@ -127,4 +133,18 @@ private static void collectAndTraverse(KermiHttpUtil httpUtil, String deviceId, collectAndTraverse(httpUtil, deviceId, dataPoints, menuChildEntry.getResponseData()); } } + + /** + * + * @param unitString + * @return null if unit could not be determined + */ + public static Unit determineUnitByString(String unitString) { + if ("kW".equals(unitString)) { + return Units.WATT; + } else if ("°C".equals(unitString)) { + return Units.CELSIUS; + } + return null; + } } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/ListDatapointCacheFile.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/ListDatapointCacheFile.java index 300e03c8b9add..6597061be2d76 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/ListDatapointCacheFile.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/ListDatapointCacheFile.java @@ -29,6 +29,12 @@ public class ListDatapointCacheFile { @SerializedName("Serial") private String serial; + @SerializedName("Address") + private String address; + + @SerializedName("Name") + private String name; + @SerializedName("Datapoints") private List datapoints; @@ -48,6 +54,22 @@ public void setSerial(String serial) { this.serial = serial; } + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + public List getDatapoints() { return datapoints; } diff --git a/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/addon/addon.xml index 6498c42e435d6..ab717408124c6 100644 --- a/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/addon/addon.xml +++ b/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/addon/addon.xml @@ -5,6 +5,7 @@ binding Kermi Binding - This is the binding for Kermi. + Binding for Kermi x-center Heatpump Manager + local diff --git a/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/config/deviceConfig.xml b/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/config/deviceConfig.xml new file mode 100644 index 0000000000000..1386fc0840269 --- /dev/null +++ b/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/config/deviceConfig.xml @@ -0,0 +1,15 @@ + + + + + + + Specific device identifier + + + + diff --git a/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/thing-types.xml index c61a8c3baddba..dad572e8f3de5 100644 --- a/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/thing-types.xml @@ -4,10 +4,12 @@ xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0" xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd"> + + - + Kermi Heatpump Manager @@ -16,14 +18,10 @@ - + - - - - @@ -36,14 +34,10 @@ - + - - - - @@ -56,16 +50,9 @@ - + - - - - - - - @@ -79,8 +66,24 @@ Number:Temperature - Current temperature - + + + + + Number:Power + + + + + + Number + + + + + + Switch + diff --git a/bundles/org.openhab.binding.kermi/src/test/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtilTest.java b/bundles/org.openhab.binding.kermi/src/test/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtilTest.java deleted file mode 100644 index a0ee5fa7c4e6b..0000000000000 --- a/bundles/org.openhab.binding.kermi/src/test/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtilTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.openhab.binding.kermi.internal.model; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; - -import org.junit.jupiter.api.Test; -import org.openhab.binding.kermi.internal.api.GetDeviceResponse; - -import com.google.gson.Gson; - -public class KermiSiteInfoUtilTest { - - @Test - public void getVisualizationDatapointByWellKnownName() throws IOException { - - GetDeviceResponse getDeviceResponse = null; - try (InputStream resourceAsStream = getClass() - .getResourceAsStream("/getDeviceInfoByDeviceIdDrinkingWaterHeating.json")) { - getDeviceResponse = new Gson().fromJson(new InputStreamReader(resourceAsStream), GetDeviceResponse.class); - } - - // VisualizationDatapoint visualizationDatapoint = KermiSiteInfoUtil.getVisualizationDatapointByWellKnownName( - // KermiBindingConstants.WELL_KNOWN_NAME_BS_TWE_TEMP_ACT, getDeviceResponse.getResponseData()).get(); - // assertEquals("06e61673-abc2-4671-9e5a-960809d1f326", - // visualizationDatapoint.getConfig().getDatapointConfigId()); - } - -} From ebf5838e2f34baf415653b3a6c2b864ec2844a80 Mon Sep 17 00:00:00 2001 From: Marco Descher Date: Sun, 15 Oct 2023 17:44:22 +0200 Subject: [PATCH 08/12] correct kwh to watt conversion --- .../binding/kermi/internal/model/KermiSiteInfo.java | 9 +++++++-- .../src/main/resources/OH-INF/thing/thing-types.xml | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java index c14154556ed35..e444e98e3a950 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java @@ -40,6 +40,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tech.units.indriya.unit.Units; + /** * @author Marco Descher - intial implementation */ @@ -157,8 +159,11 @@ public State convertDatapointValueToState(DatapointValue datapointValue) { } Unit unit = KermiSiteInfoUtil.determineUnitByString(datapoint.getConfig().getUnit()); if (value instanceof Double) { - // TODO if kw > multiply with 1000? - return new QuantityType<>((double) datapointValue.getValue(), unit); + double _value = (double) datapointValue.getValue(); + if (Units.WATT.equals(unit)) { + _value *= 1000; + } + return new QuantityType<>(_value, unit); } } else if (2 == datapointType) { // OnOff Type diff --git a/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/thing-types.xml index dad572e8f3de5..45834cf990138 100644 --- a/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/thing-types.xml @@ -72,7 +72,7 @@ Number:Power - + From e0ece61f41bba8b214e9f8b7ac50f04acf9810b7 Mon Sep 17 00:00:00 2001 From: Marco Descher Date: Fri, 20 Oct 2023 13:10:48 +0200 Subject: [PATCH 09/12] support datapointType 0,3,4 --- bundles/org.openhab.binding.kermi/README.md | 18 +++++++++++++++--- .../kermi/internal/KermiBindingConstants.java | 1 + .../handler/KermiBaseThingHandlerUtil.java | 11 ++++++++++- .../internal/handler/KermiBridgeHandler.java | 3 +++ .../resources/OH-INF/thing/thing-types.xml | 5 +++++ 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/bundles/org.openhab.binding.kermi/README.md b/bundles/org.openhab.binding.kermi/README.md index 47affc9d15021..f90a80e34fd80 100644 --- a/bundles/org.openhab.binding.kermi/README.md +++ b/bundles/org.openhab.binding.kermi/README.md @@ -6,8 +6,7 @@ This binding connects to [Kermi x-center controller](https://www.kermi.com/en/de Current support is developed and tested on -* a franchised version of the Kermi heatpump, namely the -[Heizbösch MOZART13AC-RW60](https://www.boesch.at/produkte/heizen/waermepumpe/luft/modulierende-luft-wasser-waermepumpe-mozart-aussenaufstellung~495589) heatpump manager version _1.6.0.118_ . +* a franchised version of the Kermi heatpump, namely the [Heizbösch MOZART13AC-RW60](https://www.boesch.at/produkte/heizen/waermepumpe/luft/modulierende-luft-wasser-waermepumpe-mozart-aussenaufstellung~495589) heatpump manager version _1.6.0.118_ . No official documentation could be found or gathered. This plug-in is based on reverse engineering the protocol. @@ -61,6 +60,19 @@ Number:Temperature Outside_temperature {channel="kermi:room-heating:heatpumpbrid Number:Power Current_Power_Inverter {channel="kermi:heatpump:heatpumpbridge:heatpump:Rubin_CurrentPowerInverter"} ``` +# Changelog + +20.10.23 +* Support numeric values for datapointType = 0 +* Support string values for datapointType = 3 +* Support string values for datapointType = 4 + # ToDo / Future Tasks -* Support `DatapointType==0` and `DatapointType>2` +* Change default query time, resemble webinterface behaviour (every 10 seconds to GetFavorites) +* Support channels for bridge +* Somehow add DatapointConfigId to channel, seems not supported +* Collection of statistics providing virtual channels + * 24/h power consumption (all, heating, drinking-water) + * number of cycles (all, heating, drinking-water) + * time between cycles diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java index ccf8008053cb1..6869e33762acc 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBindingConstants.java @@ -42,6 +42,7 @@ public class KermiBindingConstants { public static final ChannelTypeUID CHANNEL_TYPE_POWER = new ChannelTypeUID(BINDING_ID, "power"); public static final ChannelTypeUID CHANNEL_TYPE_NUMBER = new ChannelTypeUID(BINDING_ID, "number"); public static final ChannelTypeUID CHANNEL_TYPE_ONOFF = new ChannelTypeUID(BINDING_ID, "onoff"); + public static final ChannelTypeUID CHANNEL_TYPE_STRING = new ChannelTypeUID(BINDING_ID, "string"); // Device Constants public static final String DEVICE_ID_HEATPUMP_MANAGER = "00000000-0000-0000-0000-000000000000"; diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandlerUtil.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandlerUtil.java index f7df148e3cb9b..cffb1381a6f64 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandlerUtil.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandlerUtil.java @@ -22,12 +22,16 @@ import tech.units.indriya.unit.Units; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ public class KermiBaseThingHandlerUtil { public ChannelTypeUID determineChannelTypeUID(Config datapointConfig) { switch (datapointConfig.getDatapointType()) { + case 0: + // enumeration values, there should be a specific item-type for each + // of these channels, we simply return the numeric value by now + return KermiBindingConstants.CHANNEL_TYPE_NUMBER; case 1: Unit unit = KermiSiteInfoUtil.determineUnitByString(datapointConfig.getUnit()); if (Units.WATT.equals(unit)) { @@ -38,6 +42,11 @@ public ChannelTypeUID determineChannelTypeUID(Config datapointConfig) { return KermiBindingConstants.CHANNEL_TYPE_NUMBER; case 2: return KermiBindingConstants.CHANNEL_TYPE_ONOFF; + case 3: + return KermiBindingConstants.CHANNEL_TYPE_STRING; + case 4: + // time value? + return KermiBindingConstants.CHANNEL_TYPE_STRING; default: break; } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java index f7103fde97e43..802b093ea68f8 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java @@ -96,6 +96,9 @@ public void initialize() { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage()); logger.error("Communication error", e); } + + // TODO add channels + } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, errorMsg); } diff --git a/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/thing-types.xml index 45834cf990138..31e23270ba994 100644 --- a/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.kermi/src/main/resources/OH-INF/thing/thing-types.xml @@ -86,4 +86,9 @@ + + String + + + From 9211798d2c1151c6cfca8a5d19d777dbfb513423 Mon Sep 17 00:00:00 2001 From: Marco Descher Date: Sat, 11 Nov 2023 08:55:19 +0100 Subject: [PATCH 10/12] satisfy checkstyle --- .../internal/KermiBaseDeviceConfiguration.java | 2 +- .../kermi/internal/KermiBridgeConfiguration.java | 2 +- .../binding/kermi/internal/api/BaseResponse.java | 2 +- .../binding/kermi/internal/api/Bundle.java | 2 +- .../binding/kermi/internal/api/Config.java | 2 +- .../binding/kermi/internal/api/Datapoint.java | 2 +- .../api/DatapointReadValuesResponse.java | 2 +- .../kermi/internal/api/DatapointValue.java | 2 +- .../binding/kermi/internal/api/Device.java | 2 +- .../binding/kermi/internal/api/DeviceInfo.java | 2 +- .../binding/kermi/internal/api/DeviceOption.java | 2 +- .../kermi/internal/api/GetDeviceResponse.java | 2 +- .../kermi/internal/api/GetDevicesResponse.java | 2 +- .../kermi/internal/api/KermiHttpUtil.java | 16 +++++++--------- .../binding/kermi/internal/api/MenuEntry.java | 2 +- .../kermi/internal/api/MenuEntryResponse.java | 2 +- .../api/MenuGetChildEntriesResponse.java | 5 ++++- .../internal/api/VisualizationDatapoint.java | 2 +- .../internal/handler/KermiBaseThingHandler.java | 5 ++--- .../internal/handler/KermiBridgeHandler.java | 7 +++---- .../kermi/internal/model/KermiSiteInfo.java | 2 +- .../kermi/internal/model/KermiSiteInfoUtil.java | 8 +++----- .../internal/model/ListDatapointCacheFile.java | 2 +- 23 files changed, 37 insertions(+), 40 deletions(-) diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBaseDeviceConfiguration.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBaseDeviceConfiguration.java index e1e7a8e975237..9e38d387f18a2 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBaseDeviceConfiguration.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBaseDeviceConfiguration.java @@ -13,7 +13,7 @@ package org.openhab.binding.kermi.internal; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ public class KermiBaseDeviceConfiguration { public Integer address; diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBridgeConfiguration.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBridgeConfiguration.java index df3ea9b0e1af4..9cdfe2921aac2 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBridgeConfiguration.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/KermiBridgeConfiguration.java @@ -13,7 +13,7 @@ package org.openhab.binding.kermi.internal; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ public class KermiBridgeConfiguration { public String hostname; diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/BaseResponse.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/BaseResponse.java index 108cad5fdb9b3..ebe91f3daabf5 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/BaseResponse.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/BaseResponse.java @@ -15,7 +15,7 @@ import com.google.gson.annotations.SerializedName; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ public class BaseResponse { diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Bundle.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Bundle.java index 7e323c781c560..ee727d55c5246 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Bundle.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Bundle.java @@ -17,7 +17,7 @@ import com.google.gson.annotations.SerializedName; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ public class Bundle { diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Config.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Config.java index 91be8522dfcaf..c58efd1f4cdf0 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Config.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Config.java @@ -17,7 +17,7 @@ import com.google.gson.annotations.SerializedName; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ public class Config { diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Datapoint.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Datapoint.java index d698510ee881c..c265f486eb10c 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Datapoint.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Datapoint.java @@ -15,7 +15,7 @@ import com.google.gson.annotations.SerializedName; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ public class Datapoint { diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointReadValuesResponse.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointReadValuesResponse.java index 3e52c29704619..412306d99c38b 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointReadValuesResponse.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointReadValuesResponse.java @@ -15,7 +15,7 @@ import java.util.List; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ public class DatapointReadValuesResponse extends BaseResponse> { diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointValue.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointValue.java index 95bffafaa7209..3ebb2a473db78 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointValue.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DatapointValue.java @@ -15,7 +15,7 @@ import com.google.gson.annotations.SerializedName; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ public class DatapointValue { diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Device.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Device.java index 7170d5cd6a837..8cdf6a0259289 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Device.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/Device.java @@ -13,7 +13,7 @@ package org.openhab.binding.kermi.internal.api; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ public class Device { diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceInfo.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceInfo.java index 1aa1dd7e28e68..d0e971fd1302d 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceInfo.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceInfo.java @@ -17,7 +17,7 @@ import com.google.gson.annotations.SerializedName; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ public class DeviceInfo { diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceOption.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceOption.java index 30e8274401cd4..11161bde971da 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceOption.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/DeviceOption.java @@ -15,7 +15,7 @@ import com.google.gson.annotations.SerializedName; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ public class DeviceOption { diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDeviceResponse.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDeviceResponse.java index af2ff8c3f1ccc..22821d9d59795 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDeviceResponse.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDeviceResponse.java @@ -13,7 +13,7 @@ package org.openhab.binding.kermi.internal.api; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ public class GetDeviceResponse extends BaseResponse { diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDevicesResponse.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDevicesResponse.java index 46095c6b4b20a..deb070dfe6cb3 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDevicesResponse.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/GetDevicesResponse.java @@ -15,7 +15,7 @@ import java.util.List; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ public class GetDevicesResponse extends BaseResponse> { diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/KermiHttpUtil.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/KermiHttpUtil.java index 5bdaaa4e04b03..85de779690a8e 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/KermiHttpUtil.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/KermiHttpUtil.java @@ -37,7 +37,7 @@ import io.micrometer.core.instrument.util.StringUtils; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ public class KermiHttpUtil { @@ -78,7 +78,6 @@ public void setPassword(String password) { @SuppressWarnings("null") public synchronized String executeUrl(String httpMethod, String url, String content, String contentType) throws KermiCommunicationException { - if (StringUtils.isBlank(hostname)) { return "Not connected"; } @@ -89,10 +88,10 @@ public synchronized String executeUrl(String httpMethod, String url, String cont Throwable lastException = null; String result = null; try { - InputStream _content = (content != null) + InputStream inputStream = (content != null) ? new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)) : null; - result = HttpUtil.executeUrl(httpMethod, url, httpHeaders, _content, contentType, 5000); + result = HttpUtil.executeUrl(httpMethod, url, httpHeaders, inputStream, contentType, 5000); logger.debug("[{} {}] {}", httpMethod, url, result); } catch (IOException e) { // HttpUtil::executeUrl wraps InterruptedException into IOException. @@ -169,15 +168,14 @@ public MenuGetChildEntriesResponse getMenuChildEntries(String deviceId, @NonNull */ public DatapointReadValuesResponse getDatapointReadValues(Set idTuples) throws KermiCommunicationException { - JsonObject jsonObject = new JsonObject(); JsonArray datapointValues = new JsonArray(idTuples.size()); jsonObject.add("DatapointValues", datapointValues); idTuples.forEach(idt -> { - JsonObject _entryObject = new JsonObject(); - _entryObject.addProperty("DeviceId", idt[0]); - _entryObject.addProperty("DatapointConfigId", idt[1]); - datapointValues.add(_entryObject); + JsonObject entryObject = new JsonObject(); + entryObject.addProperty("DeviceId", idt[0]); + entryObject.addProperty("DatapointConfigId", idt[1]); + datapointValues.add(entryObject); }); String response = executeUrl("POST", parseUrl(KermiBindingConstants.HPM_DATAPOINT_READVALUES_URL, hostname), diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntry.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntry.java index f81310767e395..8d9f7bf6c2ab0 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntry.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntry.java @@ -15,7 +15,7 @@ import com.google.gson.annotations.SerializedName; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ public class MenuEntry { diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntryResponse.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntryResponse.java index 67e8c558a5b6f..853d48f8bf212 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntryResponse.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuEntryResponse.java @@ -17,7 +17,7 @@ import com.google.gson.annotations.SerializedName; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ public class MenuEntryResponse { diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuGetChildEntriesResponse.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuGetChildEntriesResponse.java index 2875822c61344..3dc0c82def19f 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuGetChildEntriesResponse.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/MenuGetChildEntriesResponse.java @@ -12,9 +12,12 @@ */ package org.openhab.binding.kermi.internal.api; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ +@NonNullByDefault public class MenuGetChildEntriesResponse extends BaseResponse { } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/VisualizationDatapoint.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/VisualizationDatapoint.java index 148d0567cc025..ea660e8a2abb5 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/VisualizationDatapoint.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/api/VisualizationDatapoint.java @@ -15,7 +15,7 @@ import com.google.gson.annotations.SerializedName; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ public class VisualizationDatapoint { diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java index 1797e2c9c1945..d5da6c13187cd 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBaseThingHandler.java @@ -45,7 +45,7 @@ import org.slf4j.LoggerFactory; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ public class KermiBaseThingHandler extends BaseThingHandler { @@ -94,7 +94,6 @@ public void handleCommand(ChannelUID channelUID, Command command) { @Override public void initialize() { - if (KermiBindingConstants.THING_TYPE_HEATPUMP_MANAGER.equals(getThing().getThingTypeUID())) { deviceId = KermiBindingConstants.DEVICE_ID_HEATPUMP_MANAGER; logger.debug("Initializing heatpump-manager with deviceId {}", deviceId); @@ -136,7 +135,7 @@ public void initialize() { }); } - private void addDatapointAsChannel(@NonNull ThingUID thingUID, Datapoint datapoint, ThingBuilder thingBuilder) { + private void addDatapointAsChannel(ThingUID thingUID, Datapoint datapoint, ThingBuilder thingBuilder) { Config datapointConfig = datapoint.getConfig(); ChannelTypeUID channelTypeUID = kermiBaseThingHandlerUtil.determineChannelTypeUID(datapointConfig); if (channelTypeUID != null) { diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java index 802b093ea68f8..ff06ddbcd0c4b 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/handler/KermiBridgeHandler.java @@ -42,7 +42,7 @@ import org.slf4j.LoggerFactory; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ @NonNullByDefault public class KermiBridgeHandler extends BaseBridgeHandler { @@ -98,7 +98,6 @@ public void initialize() { } // TODO add channels - } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, errorMsg); } @@ -162,9 +161,9 @@ private void startAutomaticRefresh() { private void initializeKermiSiteInfo() throws KermiCommunicationException { GetDevicesResponse getDevicesResponse = httpUtil.getAllDevices(); List deviceInfo = getDevicesResponse.getResponseData(); - Map _deviceInfo = deviceInfo.stream() + Map deviceInfoMap = deviceInfo.stream() .collect(Collectors.toMap(DeviceInfo::getDeviceId, Function.identity())); - kermiSiteInfo.initializeSiteInfo(httpUtil, _deviceInfo); + kermiSiteInfo.initializeSiteInfo(httpUtil, deviceInfoMap); } private void updateData() throws KermiCommunicationException { diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java index e444e98e3a950..6f6ff2c1525b5 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java @@ -43,7 +43,7 @@ import tech.units.indriya.unit.Units; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ @Component public class KermiSiteInfo { diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtil.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtil.java index 961e93119396f..1f0e3829492fa 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtil.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfoUtil.java @@ -41,7 +41,7 @@ import tech.units.indriya.unit.Units; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ public class KermiSiteInfoUtil { @@ -120,16 +120,14 @@ private static void collectAndTraverse(KermiHttpUtil httpUtil, String deviceId, if (!menuEntry.getBundles().isEmpty()) { menuEntry.getBundles().forEach(bundle -> dataPoints.addAll(bundle.getDatapoints())); } - List menuEntries = menuEntry.getMenuEntries(); - for (MenuEntry _menuEntry : menuEntries) { + for (MenuEntry me : menuEntries) { try { Thread.sleep(25); } catch (InterruptedException e) { // just some throttling } - MenuGetChildEntriesResponse menuChildEntry = httpUtil.getMenuChildEntries(deviceId, - _menuEntry.getMenuEntryId()); + MenuGetChildEntriesResponse menuChildEntry = httpUtil.getMenuChildEntries(deviceId, me.getMenuEntryId()); collectAndTraverse(httpUtil, deviceId, dataPoints, menuChildEntry.getResponseData()); } } diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/ListDatapointCacheFile.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/ListDatapointCacheFile.java index 6597061be2d76..82ddbcf802cd0 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/ListDatapointCacheFile.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/ListDatapointCacheFile.java @@ -19,7 +19,7 @@ import com.google.gson.annotations.SerializedName; /** - * @author Marco Descher - intial implementation + * @author Marco Descher - Initial contribution */ public class ListDatapointCacheFile { From 15b651b46748d2834894e9a9a62f5af7d06b4634 Mon Sep 17 00:00:00 2001 From: Marco Descher Date: Sat, 11 Nov 2023 09:58:26 +0100 Subject: [PATCH 11/12] Fix value presentation for datapointType '0', refactor instanceof to J17 --- .../kermi/internal/model/KermiSiteInfo.java | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java index 6f6ff2c1525b5..197b3c3c8e38a 100644 --- a/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java +++ b/bundles/org.openhab.binding.kermi/src/main/java/org/openhab/binding/kermi/internal/model/KermiSiteInfo.java @@ -35,6 +35,7 @@ import org.openhab.binding.kermi.internal.api.KermiHttpUtil; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.types.StringType; import org.openhab.core.types.State; import org.osgi.service.component.annotations.Component; import org.slf4j.Logger; @@ -151,25 +152,35 @@ public State convertDatapointValueToState(DatapointValue datapointValue) { } int datapointType = datapoint.getConfig().getDatapointType(); - if (1 == datapointType) { + Object value = datapointValue.getValue(); + if (0 == datapointType) { + if (value instanceof Double dValue) { + value = dValue.intValue(); + } + // enumeration, needs to translate via "PossibleValues" + String valueString = null; + Map possibleValues = datapoint.getConfig().getPossibleValues(); + if (possibleValues != null) { + valueString = possibleValues.get(value.toString()); + } + String finalValue = (valueString != null) ? "(" + value + ") " + valueString : "(" + value + ")"; + return new StringType(finalValue); + } else if (1 == datapointType) { // Numeric value or "NaN" - Object value = datapointValue.getValue(); if (Objects.equals("NaN", value)) { return new QuantityType<>(); } Unit unit = KermiSiteInfoUtil.determineUnitByString(datapoint.getConfig().getUnit()); - if (value instanceof Double) { - double _value = (double) datapointValue.getValue(); + if (value instanceof Double dValue) { if (Units.WATT.equals(unit)) { - _value *= 1000; + dValue *= 1000; } - return new QuantityType<>(_value, unit); + return new QuantityType<>(dValue, unit); } } else if (2 == datapointType) { // OnOff Type - Object value = datapointValue.getValue(); - if (value instanceof Boolean) { - return ((Boolean) value) ? OnOffType.ON : OnOffType.OFF; + if (value instanceof Boolean bool) { + return bool ? OnOffType.ON : OnOffType.OFF; } } From 6ba94a92172c296d544547c6a40e3cfe03b2e6df Mon Sep 17 00:00:00 2001 From: Marco Descher Date: Sat, 23 Dec 2023 10:41:42 +0100 Subject: [PATCH 12/12] Update bundles/org.openhab.binding.kermi/pom.xml Co-authored-by: Wouter Born Signed-off-by: Marco Descher --- bundles/org.openhab.binding.kermi/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.kermi/pom.xml b/bundles/org.openhab.binding.kermi/pom.xml index 08b2655fd0e22..88aa9ad841e04 100644 --- a/bundles/org.openhab.binding.kermi/pom.xml +++ b/bundles/org.openhab.binding.kermi/pom.xml @@ -7,7 +7,7 @@ org.openhab.addons.bundles org.openhab.addons.reactor.bundles - 4.1.0-SNAPSHOT + 4.2.0-SNAPSHOT org.openhab.binding.kermi