From f60dd6723aa0dc6081010e82f932ecfd59f1eecc Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 8 Jan 2023 11:52:25 +0100 Subject: [PATCH 001/214] intial version Signed-off-by: Laurent ARNAL --- .../ESH-INF/binding/binding.xml | 11 + .../ESH-INF/i18n/siemenshvac_xx_XX.properties | 11 + .../ESH-INF/thing/thing-types.xml | 53 + .../META-INF/MANIFEST.MF | 31 + .../OSGI-INF/SiemensHvacDiscovery.xml | 17 + .../OSGI-INF/SiemensHvacHandlerFactory.xml | 19 + .../build.properties | 7 + .../lib/json-simple-1.1.1.jar | Bin 0 -> 23737 bytes .../org.openhab.binding.siemenshvac/pom.xml | 19 + .../SiemensHvacBindingConstants.java | 51 + .../handler/SiemensHvacHandler.java | 77 ++ .../internal/JSONResponseHandler.java | 63 ++ .../internal/SiemensHvacHandlerFactory.java | 79 ++ .../constants/JSONApiResponseKeysEnum.java | 198 ++++ .../constants/JSONRequestConstants.java | 973 ++++++++++++++++++ .../SiemensHvacDiscoveryParticipant.java | 137 +++ .../internal/siemensHvacConnector.java | 481 +++++++++ .../internal/siemensHvacDiscoveryService.java | 132 +++ .../siemensHvacGenericBindingProvider.java | 109 ++ 19 files changed, 2468 insertions(+) create mode 100644 bundles/org.openhab.binding.siemenshvac/ESH-INF/binding/binding.xml create mode 100644 bundles/org.openhab.binding.siemenshvac/ESH-INF/i18n/siemenshvac_xx_XX.properties create mode 100644 bundles/org.openhab.binding.siemenshvac/ESH-INF/thing/thing-types.xml create mode 100644 bundles/org.openhab.binding.siemenshvac/META-INF/MANIFEST.MF create mode 100644 bundles/org.openhab.binding.siemenshvac/OSGI-INF/SiemensHvacDiscovery.xml create mode 100644 bundles/org.openhab.binding.siemenshvac/OSGI-INF/SiemensHvacHandlerFactory.xml create mode 100644 bundles/org.openhab.binding.siemenshvac/build.properties create mode 100644 bundles/org.openhab.binding.siemenshvac/lib/json-simple-1.1.1.jar create mode 100644 bundles/org.openhab.binding.siemenshvac/pom.xml create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/SiemensHvacBindingConstants.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/handler/SiemensHvacHandler.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/JSONResponseHandler.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/SiemensHvacHandlerFactory.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/JSONApiResponseKeysEnum.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/JSONRequestConstants.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDiscoveryParticipant.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacConnector.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacDiscoveryService.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacGenericBindingProvider.java diff --git a/bundles/org.openhab.binding.siemenshvac/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.siemenshvac/ESH-INF/binding/binding.xml new file mode 100644 index 0000000000000..b082c33750a15 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/ESH-INF/binding/binding.xml @@ -0,0 +1,11 @@ + + + + SiemensHvac Binding + This is the binding for SiemensHvac. + Laurent ARNAL + + diff --git a/bundles/org.openhab.binding.siemenshvac/ESH-INF/i18n/siemenshvac_xx_XX.properties b/bundles/org.openhab.binding.siemenshvac/ESH-INF/i18n/siemenshvac_xx_XX.properties new file mode 100644 index 0000000000000..32a0379fec109 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/ESH-INF/i18n/siemenshvac_xx_XX.properties @@ -0,0 +1,11 @@ +# binding +binding.siemenshvac.name = +binding.siemenshvac.description = + +# thing types +thing-type.siemenshvac.sample.label = +thing-type.siemenshvac.sample.description = + +# channel types +channel-type.siemenshvac.sample-channel.label = +channel-type.siemenshvac.sample-channel.description = \ No newline at end of file diff --git a/bundles/org.openhab.binding.siemenshvac/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.siemenshvac/ESH-INF/thing/thing-types.xml new file mode 100644 index 0000000000000..0215ec7128622 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/ESH-INF/thing/thing-types.xml @@ -0,0 +1,53 @@ + + + + + + + + Control a Siemens Hvac Bridge over IP + + + + + + + + + network-address + + The address of the Siemens Hvac bridge to control. + + + 80 + + The TCP port number used to connect to the bridge. + + + Administrator + + Nom de l'utilisateur. + + + Mnbo32tyu2! + + Password de l'utilisateur. + + + + + + + + + Dimmer + + Increase/Decrease the temperature (°c) + TemperatureControl + + + + diff --git a/bundles/org.openhab.binding.siemenshvac/META-INF/MANIFEST.MF b/bundles/org.openhab.binding.siemenshvac/META-INF/MANIFEST.MF new file mode 100644 index 0000000000000..edff17c2743ff --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/META-INF/MANIFEST.MF @@ -0,0 +1,31 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: SiemensHvac Binding +Bundle-SymbolicName: org.openhab.binding.siemenshvac;singleton:=true +Bundle-Vendor: openHAB +Bundle-Version: 2.0.0.qualifier +Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Bundle-ClassPath: ., + lib/json-simple-1.1.1.jar +Import-Package: com.google.common.base, + com.google.common.collect, + gnu.io;resolution:=optional, + org.apache.commons.lang, + org.openhab.binding.siemenshvac, + org.openhab.binding.siemenshvac.handler, + org.eclipse.smarthome.config.core, + org.eclipse.smarthome.core.library.types, + org.eclipse.smarthome.core.thing, + org.eclipse.smarthome.core.thing.binding, + org.eclipse.smarthome.core.thing.binding.builder, + org.eclipse.smarthome.core.thing.type, + org.eclipse.smarthome.core.types, + org.jupnp.model.meta, + org.jupnp.model.types, + org.osgi.service.cm, + org.osgi.service.component, + org.slf4j +Service-Component: OSGI-INF/*.xml +Export-Package: org.openhab.binding.siemenshvac, + org.openhab.binding.siemenshvac.handler +Require-Bundle: org.eclipse.smarthome.config.discovery diff --git a/bundles/org.openhab.binding.siemenshvac/OSGI-INF/SiemensHvacDiscovery.xml b/bundles/org.openhab.binding.siemenshvac/OSGI-INF/SiemensHvacDiscovery.xml new file mode 100644 index 0000000000000..ee57a46d6fb29 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/OSGI-INF/SiemensHvacDiscovery.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/bundles/org.openhab.binding.siemenshvac/OSGI-INF/SiemensHvacHandlerFactory.xml b/bundles/org.openhab.binding.siemenshvac/OSGI-INF/SiemensHvacHandlerFactory.xml new file mode 100644 index 0000000000000..3bb5022abccd6 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/OSGI-INF/SiemensHvacHandlerFactory.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.siemenshvac/build.properties b/bundles/org.openhab.binding.siemenshvac/build.properties new file mode 100644 index 0000000000000..cbd2b7cb6a4cd --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/build.properties @@ -0,0 +1,7 @@ +source.. = src/main/java/ +output.. = target/classes +bin.includes = META-INF/,\ + lib/json-simple-1.1.1.jar,\ + .,\ + OSGI-INF/,\ + ESH-INF/ \ No newline at end of file diff --git a/bundles/org.openhab.binding.siemenshvac/lib/json-simple-1.1.1.jar b/bundles/org.openhab.binding.siemenshvac/lib/json-simple-1.1.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..66347a6c86b7d6442358ca7643e4dc484fb01866 GIT binary patch literal 23737 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 A{Qv*} literal 0 HcmV?d00001 diff --git a/bundles/org.openhab.binding.siemenshvac/pom.xml b/bundles/org.openhab.binding.siemenshvac/pom.xml new file mode 100644 index 0000000000000..f53f6cd5f3202 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/pom.xml @@ -0,0 +1,19 @@ + + + + 4.0.0 + + + org.openhab.binding + pom + 2.0.0-SNAPSHOT + + + org.openhab.binding + org.openhab.binding.siemenshvac + 2.0.0-SNAPSHOT + + SiemensHvac Binding + eclipse-plugin + + diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/SiemensHvacBindingConstants.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/SiemensHvacBindingConstants.java new file mode 100644 index 0000000000000..e14ca9c942b09 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/SiemensHvacBindingConstants.java @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2014-2015 openHAB UG (haftungsbeschraenkt) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.binding.siemenshvac; + +import java.util.Set; + +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +import com.google.common.collect.ImmutableSet; + +/** + * The {@link SiemensHvacBinding} class defines common constants, which are + * used across the whole binding. + * + * @author Laurent ARNAL - Initial contribution + */ +public class SiemensHvacBindingConstants { + + public static final String BINDING_ID = "siemenshvac"; + + public final static String TEMPERATURE = "temperature"; + + public final static Set SUPPORTED_DEVICE_MODELS = ImmutableSet.of("Web Server OZW672.01"); + + // List of thing parameters names + public final static String PROTOCOL_PARAMETER = "protocol"; + public final static String HOST_PARAMETER = "address"; + public final static String TCP_PORT_PARAMETER = "tcpPort"; + public final static String USER_PARAMETER = "userName"; + public final static String PASSWORD_PARAMETER = "userPassword"; + + public final static String IP_PROTOCOL_NAME = "IP"; + + public final static ThingTypeUID HVAC_THING_TYPE = new ThingTypeUID(BINDING_ID, "hvacBridge"); + public final static ThingTypeUID TEST_THING_TYPE = new ThingTypeUID(BINDING_ID, "hvacBridge"); + public final static ThingTypeUID HVAC_UNSUPPORTED_THING_TYPE = new ThingTypeUID(BINDING_ID, + "hvacBridgeUnsupported"); + + // Used for Discovery service + public final static String MANUFACTURER = "Siemens Switzerland Ltd."; + public final static String UPNP_DEVICE_TYPE = "Basic"; + + public final static Set SUPPORTED_THING_TYPES_UIDS = ImmutableSet.of(HVAC_THING_TYPE, + HVAC_UNSUPPORTED_THING_TYPE); + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/handler/SiemensHvacHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/handler/SiemensHvacHandler.java new file mode 100644 index 0000000000000..a3b32778add58 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/handler/SiemensHvacHandler.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2014-2015 openHAB UG (haftungsbeschraenkt) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.binding.siemenshvac.handler; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; +import org.eclipse.smarthome.core.thing.binding.builder.ChannelBuilder; +import org.eclipse.smarthome.core.thing.binding.builder.ThingBuilder; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.siemenshvac.SiemensHvacBindingConstants; +import org.openhab.binding.siemenshvac.internal.siemensHvacConnector; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link SiemensHvacHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Laurent ARNAL - Initial contribution + */ +public class SiemensHvacHandler extends BaseThingHandler { + + private Logger logger = LoggerFactory.getLogger(SiemensHvacHandler.class); + private siemensHvacConnector hvacConnector; + + public SiemensHvacHandler(Thing thing) { + super(thing); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (channelUID.getId().equals(SiemensHvacBindingConstants.TEMPERATURE)) { + + } + } + + @Override + public void initialize() { + List channels = new ArrayList<>(1); + + ThingBuilder thingBuilder = ThingBuilder.create(thing.getThingTypeUID(), thing.getUID()) + .withBridge(thing.getBridgeUID()).withChannels(channels).withConfiguration(thing.getConfiguration()); + + ChannelUID channelUID = new ChannelUID(thing.getUID(), "0"); + Channel channel = ChannelBuilder.create(channelUID, "switch").build(); + thingBuilder.withChannel(channel); + + // ChannelUID channelUID2 = new ChannelUID(thing.getUID(), "1"); + // Channel channel2 = ChannelBuilder.create(channelUID2, "switch").build(); + // thingBuilder.withChannel(channel2); + + updateThing(thingBuilder.build()); + + updateStatus(ThingStatus.ONLINE); + hvacConnector = createConnection(); + } + + protected siemensHvacConnector createConnection() { + String host = (String) this.getConfig().get(SiemensHvacBindingConstants.HOST_PARAMETER); + String user = (String) this.getConfig().get(SiemensHvacBindingConstants.USER_PARAMETER); + String password = (String) this.getConfig().get(SiemensHvacBindingConstants.PASSWORD_PARAMETER); + + return new siemensHvacConnector(host, user, password); + } + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/JSONResponseHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/JSONResponseHandler.java new file mode 100644 index 0000000000000..7c5366302d9f1 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/JSONResponseHandler.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2010-2015, openHAB.org and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.binding.siemenshvac.internal; + + +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import org.openhab.binding.siemenshvac.internal.constants.JSONApiResponseKeysEnum; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Alexander Betker + * @author Alex Maier + * @since 1.3.0 + */ +public class JSONResponseHandler { + + private static final Logger logger = LoggerFactory.getLogger(JSONResponseHandler.class); + + public JSONResponseHandler() { + + } + + public boolean checkResponse(JSONObject jsonResponse) { + if(jsonResponse == null) + return false; + else if (jsonResponse.get(JSONApiResponseKeysEnum.RESPONSE_OK.getKey()) != null) { + return jsonResponse.get(JSONApiResponseKeysEnum.RESPONSE_OK.getKey()).toString().equals(JSONApiResponseKeysEnum.RESPONSE_SUCCESSFUL.getKey()); + } + else{ + logger.error("error in json request. Error message : "+jsonResponse.get(JSONApiResponseKeysEnum.RESPONSE_MESSAGE).toString()); + } + return false; + } + + public static JSONObject toJSONObject(String jsonResponse) { + if (jsonResponse != null && !jsonResponse.trim().equals("")) { + try { + return (JSONObject)new JSONParser().parse(jsonResponse); + } + catch (ParseException e) { + logger.error(e.getLocalizedMessage()); + } + } + return null; + } + + public JSONObject getResultJSONObject(JSONObject jsonObject) { + if (jsonObject != null) { + return (JSONObject) jsonObject.get(JSONApiResponseKeysEnum.RESULT.getKey()); + } + return null; + } + +} \ No newline at end of file diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/SiemensHvacHandlerFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/SiemensHvacHandlerFactory.java new file mode 100644 index 0000000000000..94ea5359c3e2a --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/SiemensHvacHandlerFactory.java @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2014 openHAB UG (haftungsbeschraenkt) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.binding.siemenshvac.internal; + +import static org.openhab.binding.siemenshvac.SiemensHvacBindingConstants.HVAC_THING_TYPE; + +import java.util.Collections; +import java.util.Set; + +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.openhab.binding.siemenshvac.handler.SiemensHvacHandler; + +/** + * The {@link SiemensHvacHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Laurent ARNAL - Initial contribution + */ +public class SiemensHvacHandlerFactory extends BaseThingHandlerFactory { + + private final static Set SUPPORTED_THING_TYPES_UIDS = Collections.singleton(HVAC_THING_TYPE); + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected ThingHandler createHandler(Thing thing) { + + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + SiemensHvacHandler handler; + + handler = new SiemensHvacHandler(thing); + + return handler; + + // return null; + } + + private synchronized void registerDiscoveryService( + SiemensHvacHandler bridgeHandler) {/* + * HueLightDiscoveryService discoveryService = new + * HueLightDiscoveryService(bridgeHandler); + * discoveryService.activate(); + * + * this.discoveryServiceRegs.put(bridgeHandler.getThing().getUID(), + * bundleContext + * .registerService(DiscoveryService.class.getName(), discoveryService, + * new Hashtable())); + */ + } + + @Override + protected synchronized void removeHandler(ThingHandler thingHandler) { + /* + * if (thingHandler instanceof HueBridgeHandler) { + * ServiceRegistration serviceReg = this.discoveryServiceRegs.get(thingHandler.getThing().getUID()); + * if (serviceReg != null) { + * // remove discovery service, if bridge handler is removed + * HueLightDiscoveryService service = (HueLightDiscoveryService) bundleContext + * .getService(serviceReg.getReference()); + * service.deactivate(); + * serviceReg.unregister(); + * discoveryServiceRegs.remove(thingHandler.getThing().getUID()); + * } + * } + */ + } + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/JSONApiResponseKeysEnum.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/JSONApiResponseKeysEnum.java new file mode 100644 index 0000000000000..d748900656487 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/JSONApiResponseKeysEnum.java @@ -0,0 +1,198 @@ +/** + * Copyright (c) 2010-2015, openHAB.org and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.binding.siemenshvac.internal.constants; + +/** + * @author Alexander Betker + * @since 1.3.0 + * @version digitalSTROM-API 1.14.5 + */ +public enum JSONApiResponseKeysEnum { + + RESPONSE_OK ("ok"), + RESPONSE_SUCCESSFUL ("true"), + RESPONSE_MESSAGE ("message"), + + RESULT ("result"), + + APARTMENT_GET_NAME ("name"), + APARTMENT_GET_CONSUMPTION ("consumption"), + APARTMENT_GET_STRUCTURE ("apartment"), + APARTMENT_GET_STRUCTURE_ZONES ("zones"), + APARTMENT_GET_STRUCTURE_ZONES_ID ("id"), + APARTMENT_GET_STRUCTURE_ZONES_NAME ("name"), + APARTMENT_GET_STRUCTURE_ZONES_ISPRESENT ("isPresent"), + APARTMENT_GET_STRUCTURE_ZONES_DEVICES ("devices"), + APARTMENT_GET_STRUCTURE_ZONES_GROUPS ("groups"), + + APARTMENT_GET_DEVICES ("result"), + APARTMENT_GET_CIRCUITS ("circuits"), + + ZONE_GET_NAME ("name"), + ZONE_GET_CONSUMPTION ("consumption"), + ZONE_SCENE_GET_NAME ("name"), + ZONE_GET_REACHABLE_SCENES ("reachableScenes"), + + DEVICE_GET_NAME ("name"), + DEVICE_GET_SPEC ("result"), + DEVICE_GET_GROUPS ("groups"), + DEVICE_GET_GROUPS_ID ("id"), + DEVICE_GET_GROUPS_NAME ("name"), + DEVICE_GET_STATE ("isOn"), + DEVICE_GET_CONSUMPTION ("consumption"), + DEVICE_HAS_TAG ("hasTag"), + DEVICE_GET_TAGS ("tags"), + DEVICE_GET_CONFIG ("result"), + DEVICE_GET_CONFIG_CLASS ("class"), + DEVICE_GET_CONFIG_INDEX ("index"), + DEVICE_GET_CONFIG_VALUE ("value"), + DEVICE_GET_CONFIG_WORD ("result"), + DEVICE_GET_OUTPUT_VALUE ("value"), + DEVICE_GET_SCENE_MODE ("result"), + DEVICE_GET_SCENE_MODE_SCENE_ID ("sceneID"), + DEVICE_GET_SCENE_MODE_DONT_CARE ("dontCare"), + DEVICE_GET_SCENE_MODE_LOCAL_PRIO ("localPrio"), + DEVICE_GET_SCENE_MODE_SPECIAL_MODE ("specialMode"), + DEVICE_GET_SCENE_MODE_FLASH_MODE ("flashMode"), + DEVICE_GET_SCENE_MODE_LEDCON_INDEX ("ledconIndex"), + DEVICE_GET_SCENE_MODE_DIMTIME_INDEX ("dimtimeIndex"), + DEVICE_GET_TRANSITION_TIME ("result"), + DEVICE_GET_TRANSITION_TIME_INDEX ("dimtimeIndex"), + DEVICE_GET_TRANSITION_TIME_UP ("up"), + DEVICE_GET_TRANSITION_TIME_DOWN ("down"), + + DEVICE_GET_LED_MODE ("result"), + DEVICE_GET_LED_MODE_INDEX ("ledconIndex"), + DEVICE_GET_LED_MODE_COLOR ("colorSelect"), + DEVICE_GET_LED_MODE_SELECT ("modeSelect"), + DEVICE_GET_LED_MODE_DIM_MODE ("dimMode"), + DEVICE_GET_LED_MODE_RGB ("rgbMode"), + DEVICE_GET_LED_MODE_GROUP_COLOR_MODE ("groupColorMode"), + + DEVICE_GET_SENSOR_VALUE ("result"), + DEVICE_GET_SENSOR_VALUE_SENSOR_VALUE ("sensorValue"), + DEVICE_GET_SENSOR_TYPE ("result"), + DEVICE_GET_SENSOR_TYPE_TYPE ("sensorType"), + + DEVICE_GET_SENSOR_EVENT_TABLE_ENTRY ("result"), + DEVICE_GET_SENSOR_EVENT_TABLE_ENTRY_EVENT_INDEX ("eventIndex"), + DEVICE_GET_SENSOR_EVENT_TABLE_ENTRY_EVENT_NAME ("eventName"), + DEVICE_GET_SENSOR_EVENT_TABLE_ENTRY_IS_SCENE_DEVICE ("isSceneDevice"), + DEVICE_GET_SENSOR_EVENT_TABLE_ENTRY_SENSOR_INDEX ("sensorIndex"), + DEVICE_GET_SENSOR_EVENT_TABLE_TEST ("test"), + DEVICE_GET_SENSOR_EVENT_TABLE_ACTION ("action"), + DEVICE_GET_SENSOR_EVENT_TABLE_VALUE ("value"), + DEVICE_GET_SENSOR_EVENT_TABLE_HYSTERSIS ("hysteresis"), + DEVICE_GET_SENSOR_EVENT_TABLE_VALIDITY ("validity"), + + // Device + DEVICE_NAME ("name"), + DEVICE_ID ("id"), + DEVICE_ID_QUERY ("dSID"), + DEVICE_FUNCTION_ID ("functionID"), + DEVICE_PRODUCT_REVISION ("productRevision"), + DEVICE_PRODUCT_ID ("productID"), + DEVICE_HW_INFO ("hwInfo"), + DEVICE_ON ("on"), + DEVICE_OUTPUT_MODE ("outputMode"), + DEVICE_BUTTON_ID ("buttonID"), + DEVICE_IS_PRESENT ("isPresent"), + DEVICE_IS_PRESENT_QUERY ("present"), + DEVICE_ZONE_ID ("zoneID"), + DEVICE_ZONE_ID_QUERY ("ZoneID"), + DEVICE_GROUPS ("groups"), + + // DeviceSpec + DEVICE_SPEC_FUNCTION_ID ("functionID"), + DEVICE_SPEC_PRODUCT_ID ("productID"), + DEVICE_SPEC_REVISION_ID ("revisionID"), + + EVENT_GET_EVENT ("events"), + EVENT_GET_EVENT_ERROR ("message"), + EVENT_NAME ("name"), + EVENT_PROPERTIES ("properties"), + + DS_METER_QUERY ("dSMeters"), + DS_METER_DSID ("dsid"), + DS_METER_DSID_QUERY ("dSID"), + DS_METER_IS_PRESENT ("isPresent"), + DS_METER_IS_PRESENT_QUERY ("present"), + + DS_METER_POWER_CONSUMPTION_QUERY ("powerConsumption"), + DS_METER_ENERGY_METER_VALUE_QUERY ("energyMeterValue"), + DS_METER_ENERGY_METER_VALUE_WS_QUERY ("energyMeterValueWs"), + + // Group + GROUP_ID ("id"), + GROUP_NAME ("name"), + GROUP_IS_PRESENT ("isPresent"), + GROUP_DEVICES ("devices"), + + CIRCUIT_GET_NAME ("name"), + CIRCUIT_GET_CONSUMPTION ("consumption"), + CIRCUIT_GET_METER_VALUE ("meterValue"), + + PROPERTY_GET_STRING ("value"), + PROPERTY_GET_INTEGER ("value"), + PROPERTY_GET_BOOLEAN ("value"), + PROPERTY_NAME ("name"), + PROPERTY_TYPE ("type"), + PROPERTY_QUERY ("result"), + PROPERTY_QUERY_ZONE_ID ("ZoneID"), + PROPERTY_QUERY_DEVICE_ID ("dSID"), + PROPERTY_GET_CHILDREN ("result"), + PROPERTY_GET_PROPERTY_TYPE ("type"), + PROPERTY_GET_FLAGS ("result"), + + SYSTEM_GET_VERSION ("version"), + SYSTEM_GET_TIME ("time"), + SYSTEM_LOGIN ("token"), + SYSTEM_LOGGED_IN_USER ("name"), + + SET_FROM_APARTMENT ("self"), + SET_BY_ZONE ("self"), + SET_BY_GROUP ("self"), + SET_BY_DSID ("self"), + SET_ADD ("self"), + SET_SUBTRACT ("self"), + SET_GET_CONSUMPTION ("consumption"), + + STRUCTURE_PERSIST ("groupID"), + + METERING_GET_RESOLUTIONS ("resolutions"), + METERING_GET_RESOLUTION ("resolution"), + METERING_GET_SERIES ("series"), + METERING_GET_SERIES_DSID ("dsid"), + METERING_GET_SERIES_TYPE ("type"), + METERING_GET_VALUES ("result"), + METERING_GET_VALUES_METER_ID ("meterID"), + METERING_GET_VALUES_TYPE ("type"), + METERING_GET_VALUES_UNIT ("unit"), + METERING_GET_VALUES_RESOLUTION ("resolution"), + METERING_GET_VALUES_VALUES ("values"), + + METERING_GET_LATEST ("values"), + METERING_GET_LATEST_DSID ("dsid"), + METERING_GET_LATEST_VALUE ("value"), + METERING_GET_LATEST_DATE ("date"), + + QUERY_ZONE_ID ("ZoneID"); + + + private final String key; + + private JSONApiResponseKeysEnum(String key) { + this.key = key; + } + + public String getKey() { + return key; + } + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/JSONRequestConstants.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/JSONRequestConstants.java new file mode 100644 index 0000000000000..3f87342c179be --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/JSONRequestConstants.java @@ -0,0 +1,973 @@ +/** + * Copyright (c) 2010-2015, openHAB.org and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.binding.siemenshvac.internal.constants; + +/** + * @author Alexander Betker + * @since 1.3.0 + * @version digitalSTROM-API 1.14.5 + */ +public interface JSONRequestConstants { + + // Symbols + public final static String SLASH_SYMBOL = "/"; + + public final static String FIRST_PARAMETER_CONCAT_SYMBOL = "?"; + + public final static String NEXT_PARAMETER_CONCAT_SYMBOL = "&"; + + public final static String EQUAL_SIGN_SYMBOL = "="; + + + // Classes + public final static String JSON_TO_STRING = "json"; + + public final static String SYSTEM_TO_STRING = "system"; + + public final static String DEVICE_TO_STRING = "device"; + + public final static String ZONE_TO_STRING = "zone"; + + public final static String APARTMENT_TO_STRING = "apartment"; + + public final static String SET_TO_STRING = "set"; + + public final static String CIRCUIT_TO_STRING = "circuit"; + + public final static String PROPERTY_TO_STRING = "property"; + + public final static String EVENT_TO_STRING = "event"; + + public final static String STRUCTURE_TO_STRING = "structure"; + + public final static String METERING_TO_STRING = "metering"; + + + // Methods + public final static String LOGIN_TO_STRING = "login"; + + public final static String LOGOUT_TO_STRING = "logout"; + + public final static String CALLSCENE_TO_STRING = "callScene"; + + public final static String SAVESCENE_TO_STRING = "saveScene"; + + public final static String UNDOSCENE_TO_STRING = "undoScene"; + + public final static String TURN_ON_TO_STRING = "turnOn"; + + public final static String TURN_OFF_TO_STRING = "turnOff"; + + public final static String INCREASE_VALUE_TO_STRING = "increaseValue"; + + public final static String DECREASE_VALUE_TO_STRING = "decreaseValue"; + + public final static String GET_STRUCTURE_TO_STRING = "getStructure"; + + public final static String GET_DEVICES_TO_STRING = "getDevices"; + + public final static String GET_CIRCUITS_TO_STRING = "getCircuits"; + + public final static String LOGIN_APPLICATION_TO_STRING = "loginApplication"; + + public final static String GET_NAME_TO_STRING = "getName"; + + public final static String SET_NAME_TO_STRING = "setName"; + + public final static String SUBSCRIBE_TO_STRING = "subscribe"; + + public final static String UNSUBSCRIBE_TO_STRING = "unsubscribe"; + + public final static String GET_TO_STRING = "get"; + + public final static String SET_VALUE_TO_STRING = "setValue"; + + public final static String GET_CONSUMPTION_TO_STRING = "getConsumption"; + + public final static String RESCAN_TO_STRING = "rescan"; + + public final static String SCENE_SET_NAME_TO_STRING = "sceneSetName"; + + public final static String SCENE_GET_NAME_TO_STRING = "sceneGetName"; + + public final static String PUSH_SENSOR_VALUES_TO_STRING = "pushSensorValues"; + + public final static String GET_REACHABLE_SCENES_TO_STRING = "getReachableScenes"; + + public final static String GET_STATE_TO_STRING = "getState"; + + public final static String GET_GROUPS_TO_STRING = "getGroups"; + + public final static String GET_ENERGY_METER_VALUE_TO_STRING = "getEnergyMeterValue"; + + public final static String GET_STRING_TO_STRING = "getString"; + + public final static String GET_INTEGER_TO_STRING = "getInteger"; + + public final static String GET_BOOLEAN_TO_STRING = "getBoolean"; + + public final static String SET_STRING_TO_STRING = "setString"; + + public final static String SET_INTEGER_TO_STRING = "setInteger"; + + public final static String SET_BOOLEAN_TO_STRING = "setBoolean"; + + public final static String GET_CHILDREN_TO_STRING = "getChildren"; + + public final static String SET_FLAG_TO_STRING = "setFlag"; + + public final static String GET_FLAGS_TO_STRING = "getFlags"; + + public final static String QUERY_TO_STRING = "query"; + + public final static String REMOVE_TO_STRING = "remove"; + + public final static String GET_TYPE_TO_STRING = "getType"; + + public final static String GET_SPEC_TO_STRING = "getSpec"; + + public final static String VERSION_TO_STRING = "version"; + + public final static String TIME_TO_STRING = "time"; + + public final static String FROM_APARTMENT_TO_STRING = "fromApartment"; + + public final static String BY_ZONE_TO_STRING = "byZone"; + + public final static String BY_GROUP_TO_STRING = "byGroup"; + + public final static String BY_DSID_TO_STRING = "byDSID"; + + public final static String ADD_TO_STRING = "add"; + + public final static String SUBTRACT_TO_STRING = "subtract"; + + public final static String LOGGED_IN_USER_TO_STRING = "loggedInUser"; + + public final static String ZONE_ADD_DEVICE_TO_STRING = "zoneAddDevice"; + + public final static String ADD_ZONE_TO_STRING = "addZone"; + + public final static String REMOVE_ZONE_TO_STRING = "removeZone"; + + public final static String REMOVE_DEVICE_TO_STRING = "removeDevice"; + + public final static String PERSIST_SET_TO_STRING = "persistSet"; + + public final static String UNPERSIST_SET_TO_STRING = "unpersistSet"; + + public final static String ADD_GROUP_TO_STRING = "addGroup"; + + public final static String GROUP_ADD_DEVICE_TO_STRING = "groupAddDevice"; + + public final static String GROUP_REMOVE_DEVICE_TO_STRING = "groupRemoveDevice"; + + public final static String GET_RESOLUTIONS_TO_STRING = "getResolutions"; + + public final static String GET_SERIES_TO_STRING = "getSeries"; + + public final static String GET_VALUES_TO_STRING = "getValues"; + + public final static String GET_LATEST_TO_STRING = "getLatest"; + + public final static String ADD_TAG_TO_STRING = "addTag"; + + public final static String REMOVE_TAG_TO_STRING = "removeTag"; + + public final static String HAS_TAG_TO_STRING = "hasTag"; + + public final static String GET_TAGS_TO_STRING = "getTags"; + + public final static String LOCK_TO_STRING = "lock"; + + public final static String UNLOCK_TO_STRING = "unlock"; + + public final static String GET_SENSOR_EVENT_TABLE_ENTRY_TO_STRING = "getSensorEventTableEntry"; + + public final static String SET_SENSOR_EVENT_TABLE_ENTRY_TO_STRING = "setSensorEventTableEntry"; + + public final static String ADD_TO_AREA_TO_STRING = "addToArea"; + + public final static String REMOVE_FROM_AREA_TO_STRING = "removeFromArea"; + + public final static String SET_CONFIG_TO_STRING = "setConfig"; + + public final static String GET_CONFIG_TO_STRING = "getConfig"; + + public final static String GET_CONFIG_WORD_TO_STRING = "getConfigWord"; + + public final static String SET_JOKER_GROUP_TO_STRING = "setJokerGroup"; + + public final static String SET_BUTTON_ID_TO_STRING = "setButtonID"; + + public final static String SET_BUTTON_INPUT_MODE_TO_STRING = "setButtonInputMode"; + + public final static String SET_OUTPUT_MODE_TO_STRING = "setOutputMode"; + + public final static String SET_PROG_MODE_TO_STRING = "setProgMode"; + + public final static String GET_OUTPUT_VALUE_TO_STRING = "getOutputValue"; + + public final static String SET_OUTPUT_VALUE_TO_STRING = "setOutputValue"; + + public final static String GET_SCENE_MODE_TO_STRING = "getSceneMode"; + + public final static String SET_SCENE_MODE_TO_STRING = "setSceneMode"; + + public final static String GET_TRANSITION_TIME_TO_STRING = "getTransitionTime"; + + public final static String SET_TRANSITION_TIME_TO_STRING = "setTransitionTime"; + + public final static String GET_LED_MODE_TO_STRING = "getLedMode"; + + public final static String SET_LED_MODE_TO_STRING = "setLedMode"; + + public final static String GET_SENSOR_VALUE_TO_STRING = "getSensorValue"; + + public final static String GET_SENSOR_TYPE_TO_STRING = "getSensorType"; + + + // Parameter-Names + public final static String TOKEN_TO_STRING = "token"; + + public final static String NAME_TO_STRING = "name"; + + public final static String NEW_NAME_TO_STRING = "newName"; + + public final static String DSID_TO_STRING = "dsid"; + + public final static String SCENENUMBER_TO_STRING = "sceneNumber"; + + public final static String LOGIN_TOKEN_TO_STRING = "loginToken"; + + public final static String USER_TO_STRING = "user"; + + public final static String PASSWORD_TO_STRING = "password"; + + public final static String SUBSCRIPTIONID_TO_STRING = "subscriptionID"; + + public final static String TIMEOUT_TO_STRING = "timeout"; + + public final static String GROUP_ID_TO_STRING = "groupID"; + + public final static String GROUP_NAME_TO_STRING = "groupName"; + + public final static String VALUE_TO_STRING = "value"; + + public final static String FORCE_TO_STRING = "force"; + + public final static String ID_TO_STRING = "id"; + + public final static String ENABLE_TO_STRING = "enable"; + + public final static String DISABLE_TO_STRING = "disable"; + + public final static String UNASSIGNED_TO_STRING = "unassigned"; + + public final static String SOURCE_DSID_TO_STRING = "sourceDSID"; + + public final static String SENSOR_TYPE_TO_STRING = "sensorType"; + + public final static String SENSOR_VALUE_TO_STRING = "sensorValue"; + + public final static String FLAG_TO_STRING = "flag"; + + public final static String PATH_TO_STRING = "path"; + + public final static String RAISE_TO_STRING = "raise"; + + public final static String CONTEXT_TO_STRING = "context"; + + public final static String LOCATION_TO_STRING = "location"; + + public final static String SELF_TO_STRING = "self"; + + public final static String ZONE_ID_TO_STRING = "zoneID"; + + public final static String ZONE_NAME_TO_STRING = "zoneName"; + + public final static String OTHER_TO_STRING = "other"; + + public final static String DEVICE_ID_TO_STRING = "deviceID"; + + public final static String UNIT_TO_STRING = "unit"; + + public final static String START_TIME_TO_STRING = "startTime"; + + public final static String END_TIME_TO_STRING = "endTime"; + + public final static String VALUE_COUNT_TO_STRING = "valueCount"; + + public final static String RESOLUTION_TO_STRING = "resolution"; + + public final static String TYPE_TO_STRING = "type"; + + public final static String FROM_TO_STRING = "from"; + + public final static String TAG_TO_STRING = "tag"; + + public final static String CLASS_TO_STRING = "class"; + + public final static String INDEX_TO_STRING = "index"; + + public final static String BUTTON_ID_TO_STRING = "buttonID"; + + public final static String MODE_ID_TO_STRING = "modeID"; + + public final static String MODE_TO_STRING = "mode"; + + public final static String OFFSET_TO_STRING = "offset"; + + public final static String SCENE_ID_TO_STRING = "sceneID"; + + public final static String DONT_CARE_TO_STRING = "dontCare"; + + public final static String LOCAL_PRIO_TO_STRING = "localPrio"; + + public final static String SPECIAL_MODE_TO_STRING = "specialMode"; + + public final static String FLASH_MODE_TO_STRING = "flashMode"; + + public final static String LED_CON_INDEX_TO_STRING = "ledconIndex"; + + public final static String DIM_TIME_INDEX_TO_STRING = "dimtimeIndex"; + + public final static String UP_TO_STRING = "up"; + + public final static String DOWN_TO_STRING = "down"; + + public final static String COLOR_SELECT_TO_STRING = "colorSelect"; + + public final static String MODE_SELECT_TO_STRING = "modeSelect"; + + public final static String DIM_MODE_TO_STRING = "dimMode"; + + public final static String RGB_MODE_TO_STRING = "rgbMode"; + + public final static String GROUP_COLOR_MODE_TO_STRING = "groupColorMode"; + + public final static String SENSOR_INDEX_TO_STRING = "sensorIndex"; + + public final static String EVENT_INDEX_TO_STRING = "eventIndex"; + + public final static String AREA_SCENE_TO_STRING = "areaScene"; + + public final static String EVENT_NAME_TO_STRING = "eventName"; + + public final static String TEST_TO_STRING = "test"; + + public final static String HYSTERSIS_TO_STRING = "hysteresis"; + + public final static String VALIDITY_TO_STRING = "validity"; + + public final static String ACTION_TO_STRING = "action"; + + public final static String BUTTON_NUMBER_TO_STRING = "buttonNumber"; + + public final static String CLICK_TYPE_TO_STRING = "clickType"; + + public final static String SCENE_DEVICE_MODE_TO_STRING = "sceneDeviceMode"; + + + // values + public final static String TRUE_TO_STRING = "true"; + + + // Prefixes-Classes + public final static String JSON_PREFIX = SLASH_SYMBOL+JSON_TO_STRING+SLASH_SYMBOL; + + public final static String JSON_SYSTEM_PREFIX = JSON_PREFIX+SYSTEM_TO_STRING+SLASH_SYMBOL; + + public final static String JSON_APARTMENT_PREFIX = JSON_PREFIX+APARTMENT_TO_STRING+SLASH_SYMBOL; + + public final static String JSON_DEVICE_PREFIX = JSON_PREFIX+DEVICE_TO_STRING+SLASH_SYMBOL; + + public final static String JSON_ZONE_PREFIX = JSON_PREFIX+ZONE_TO_STRING+SLASH_SYMBOL; + + public final static String JSON_SET_PREFIX = JSON_PREFIX+SET_TO_STRING+SLASH_SYMBOL; + + public final static String JSON_CIRCUIT_PREFIX = JSON_PREFIX+CIRCUIT_TO_STRING+SLASH_SYMBOL; + + public final static String JSON_EVENT_PREFIX = JSON_PREFIX+EVENT_TO_STRING+SLASH_SYMBOL; + + public final static String JSON_PROPERTY_PREFIX = JSON_PREFIX+PROPERTY_TO_STRING+SLASH_SYMBOL; + + public final static String JSON_STRUCTURE_PREFIX = JSON_PREFIX+STRUCTURE_TO_STRING+SLASH_SYMBOL; + + public final static String JSON_METERING_PREFIX = JSON_PREFIX+METERING_TO_STRING+SLASH_SYMBOL; + + + // Parameter + public final static String PARAMETER_NAME = NAME_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_NEW_NAME = NEW_NAME_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_USER = USER_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_PASSWORD = PASSWORD_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_DSID = DSID_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_SCENE_NUMBER = SCENENUMBER_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_TOKEN = TOKEN_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_SUBSCRIPTION_ID = SUBSCRIPTIONID_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_TIMEOUT = TIMEOUT_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_GROUP_ID = GROUP_ID_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_GROUP_NAME = GROUP_NAME_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_VALUE = VALUE_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_FORCE = FORCE_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_ID = ID_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_UNASSDIGNED = UNASSIGNED_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_SOURCE_DSID = SOURCE_DSID_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_SENSOR_TYPE = SENSOR_TYPE_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_SENSOR_VALUE = SENSOR_VALUE_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_QUERY = QUERY_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_FLAG = FLAG_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_PATH = PATH_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_CONTEXT = CONTEXT_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_LOCATION = LOCATION_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_SELF = SELF_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_ZONE_ID = ZONE_ID_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_ZONE_NAME = ZONE_NAME_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_OTHER = OTHER_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_DEVICE_ID = DEVICE_ID_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_ZONE = ZONE_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_SET = SET_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_TYPE = TYPE_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_RESOLUTION = RESOLUTION_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_UNIT = UNIT_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_START_TIME = START_TIME_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_END_TIME = END_TIME_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_VALUE_COUNT = VALUE_COUNT_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_FROM = FROM_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_TAG = TAG_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_CLASS = CLASS_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_INDEX = INDEX_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_BUTTON_ID = BUTTON_ID_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_MODE_ID = MODE_ID_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_MODE = MODE_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_OFFSET = OFFSET_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_SCENE_ID = SCENE_ID_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_DONT_CARE = DONT_CARE_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_LOCAL_PRIO = LOCAL_PRIO_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_SPECIAL_MODE = SPECIAL_MODE_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_FLASH_MODE = FLASH_MODE_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_LED_CON_INDEX = LED_CON_INDEX_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_DIM_TIME_INDEX = DIM_TIME_INDEX_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_UP = UP_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_DOWN = DOWN_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_COLOR_SELECT = COLOR_SELECT_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_MODE_SELECT = MODE_SELECT_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_DIM_MODE = DIM_MODE_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_RGB_MODE = RGB_MODE_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_GROUP_COLOR_MODE = GROUP_COLOR_MODE_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_SENSOR_INDEX = SENSOR_INDEX_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_EVENT_INDEX = EVENT_INDEX_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_AREA_SCENE = AREA_SCENE_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_EVENT_NAME = EVENT_NAME_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_TEST = TEST_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_HYSTERSIS = HYSTERSIS_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_VALIDITY = VALIDITY_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_ACTION = ACTION_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_BUTTON_NUMBER = BUTTON_NUMBER_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_CLICK_TYPE = CLICK_TYPE_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String PARAMETER_SCENE_DEVICE_MODE = SCENE_DEVICE_MODE_TO_STRING+EQUAL_SIGN_SYMBOL; + + // Infix-Parameter + public final static String INFIX_PARAMETER_TIMEOUT = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_TIMEOUT; + + public final static String INFIX_PARAMETER_SUBSCRIPTION_ID = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_SUBSCRIPTION_ID; + + public final static String INFIX_PARAMETER_PASSWORD = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_PASSWORD; + + public final static String INFIX_PARAMETER_NEW_NAME = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_NEW_NAME; + + public final static String INFIX_PARAMETER_GROUP_ID = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_GROUP_ID; + + public final static String INFIX_PARAMETER_GROUP_NAME = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_GROUP_NAME; + + public final static String INFIX_PARAMETER_VALUE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_VALUE; + + public final static String INFIX_PARAMETER_SCENE_NUMBER = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_SCENE_NUMBER; + + public final static String INFIX_PARAMETER_FORCE_TRUE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_FORCE+TRUE_TO_STRING; + + public final static String INFIX_PARAMETER_NAME = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_NAME; + + public final static String INFIX_PARAMETER_ID = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_ID; + + public final static String INFIX_PARAMETER_UNASSIGNED_TRUE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_UNASSDIGNED+TRUE_TO_STRING; + + public final static String INFIX_PARAMETER_SOURCE_DSID = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_SOURCE_DSID; + + public final static String INFIX_PARAMETER_SENSOR_TYPE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_SENSOR_TYPE; + + public final static String INFIX_PARAMETER_SENSOR_VALUE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_SENSOR_VALUE; + + public final static String INFIX_PARAMETER_DSID = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_DSID; + + public final static String INFIX_PARAMETER_QUERY = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_QUERY; + + public final static String INFIX_PARAMETER_FLAG = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_FLAG; + + public final static String INFIX_PARAMETER_PATH = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_PATH; + + public final static String INFIX_PARAMETER_CONTEXT = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_CONTEXT; + + public final static String INFIX_PARAMETER_LOCATION = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_LOCATION; + + public final static String INFIX_PARAMETER_SELF = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_SELF; + + public final static String INFIX_PARAMETER_ZONE_ID = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_ZONE_ID; + + public final static String INFIX_PARAMETER_ZONE_NAME = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_ZONE_NAME; + + public final static String INFIX_PARAMETER_OTHER = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_OTHER; + + public final static String INFIX_PARAMETER_DEVICE_ID = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_DEVICE_ID; + + public final static String INFIX_PARAMETER_ZONE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_ZONE; + + public final static String INFIX_PARAMETER_SET = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_SET; + + public final static String INFIX_PARAMETER_TYPE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_TYPE; + + public final static String INFIX_PARAMETER_RESOLUTION = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_RESOLUTION; + + public final static String INFIX_PARAMETER_UNIT = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_UNIT; + + public final static String INFIX_PARAMETER_START_TIME = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_START_TIME; + + public final static String INFIX_PARAMETER_END_TIME = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_END_TIME; + + public final static String INFIX_PARAMETER_VALUE_COUNT = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_VALUE_COUNT; + + public final static String INFIX_PARAMETER_FROM = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_FROM; + + public final static String INFIX_PARAMETER_TAG = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_TAG; + + public final static String INFIX_PARAMETER_CLASS = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_CLASS; + + public final static String INFIX_PARAMETER_INDEX = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_INDEX; + + public final static String INFIX_PARAMETER_BUTTON_ID = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_BUTTON_ID; + + public final static String INFIX_PARAMETER_MODE_ID = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_MODE_ID; + + public final static String INFIX_PARAMETER_MODE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_MODE; + + public final static String INFIX_PARAMETER_OFFSET = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_OFFSET; + + public final static String INFIX_PARAMETER_SCENE_ID = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_SCENE_ID; + + public final static String INFIX_PARAMETER_DONT_CARE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_DONT_CARE; + + public final static String INFIX_PARAMETER_LOCAL_PRIO = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_LOCAL_PRIO; + + public final static String INFIX_PARAMETER_SPECIAL_MODE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_SPECIAL_MODE; + + public final static String INFIX_PARAMETER_FLASH_MODE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_FLASH_MODE; + + public final static String INFIX_PARAMETER_LED_CON_INDEX = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_LED_CON_INDEX; + + public final static String INFIX_PARAMETER_DIM_TIME_INDEX = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_DIM_TIME_INDEX; + + public final static String INFIX_PARAMETER_UP = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_UP; + + public final static String INFIX_PARAMETER_DOWN = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_DOWN; + + public final static String INFIX_PARAMETER_COLOR_SELECT = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_COLOR_SELECT; + + public final static String INFIX_PARAMETER_MODE_SELECT = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_MODE_SELECT; + + public final static String INFIX_PARAMETER_DIM_MODE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_DIM_MODE; + + public final static String INFIX_PARAMETER_RGB_MODE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_RGB_MODE; + + public final static String INFIX_PARAMETER_GROUP_COLOR_MODE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_GROUP_COLOR_MODE; + + public final static String INFIX_PARAMETER_SENSOR_INDEX = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_SENSOR_INDEX; + + public final static String INFIX_PARAMETER_EVENT_INDEX = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_EVENT_INDEX; + + public final static String INFIX_PARAMETER_AREA_SCENE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_AREA_SCENE; + + public final static String INFIX_PARAMETER_EVENT_NAME = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_EVENT_NAME; + + public final static String INFIX_PARAMETER_TEST = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_TEST; + + public final static String INFIX_PARAMETER_HYSTERSIS = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_HYSTERSIS; + + public final static String INFIX_PARAMETER_VALIDITY = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_VALIDITY; + + public final static String INFIX_PARAMETER_ACTION = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_ACTION; + + public final static String INFIX_PARAMETER_BUTTON_NUMBER = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_BUTTON_NUMBER; + + public final static String INFIX_PARAMETER_CLICK_TYPE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_CLICK_TYPE; + + public final static String INFIX_PARAMETER_SCENE_DEVICE_MODE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_SCENE_DEVICE_MODE; + + + // Apartment + public final static String JSON_APARTMENT_GET_STRUCTURE = JSON_APARTMENT_PREFIX+GET_STRUCTURE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_APARTMENT_GET_NAME = JSON_APARTMENT_PREFIX+GET_NAME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_APARTMENT_SET_NAME = JSON_APARTMENT_PREFIX+SET_NAME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_APARTMENT_TURN_ON = JSON_APARTMENT_PREFIX+TURN_ON_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_APARTMENT_TURN_OFF = JSON_APARTMENT_PREFIX+TURN_OFF_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_APARTMENT_INCREASE_VALUE = JSON_APARTMENT_PREFIX+INCREASE_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_APARTMENT_DECREASE_VALUE = JSON_APARTMENT_PREFIX+DECREASE_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_APARTMENT_SET_VALUE = JSON_APARTMENT_PREFIX+SET_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_APARTMENT_CALLSCENE = JSON_APARTMENT_PREFIX+CALLSCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_APARTMENT_SAVESCENE = JSON_APARTMENT_PREFIX+SAVESCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_APARTMENT_UNDOSCENE = JSON_APARTMENT_PREFIX+UNDOSCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_APARTMENT_GET_CONSUMPTION = JSON_APARTMENT_PREFIX+GET_CONSUMPTION_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_APARTMENT_GET_DEVICES = JSON_APARTMENT_PREFIX+GET_DEVICES_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_APARTMENT_GET_CIRCUITS = JSON_APARTMENT_PREFIX+GET_CIRCUITS_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_APARTMENT_RESCAN = JSON_APARTMENT_PREFIX+RESCAN_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + + // Zone + public final static String JSON_ZONE_GET_NAME = JSON_ZONE_PREFIX+GET_NAME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_ZONE_SET_NAME = JSON_ZONE_PREFIX+SET_NAME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_ZONE_TURN_ON = JSON_ZONE_PREFIX+TURN_ON_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_ZONE_TURN_OFF = JSON_ZONE_PREFIX+TURN_OFF_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_ZONE_INCREASE_VALUE = JSON_ZONE_PREFIX+INCREASE_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_ZONE_DECREASE_VALUE = JSON_ZONE_PREFIX+DECREASE_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_ZONE_ENABLE = JSON_ZONE_PREFIX+ENABLE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_ZONE_DISABLE = JSON_ZONE_PREFIX+DISABLE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_ZONE_SET_VALUE = JSON_ZONE_PREFIX+SET_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_ZONE_CALLSCENE = JSON_ZONE_PREFIX+CALLSCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_ZONE_SAVESCENE = JSON_ZONE_PREFIX+SAVESCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_ZONE_UNDOSCENE = JSON_ZONE_PREFIX+UNDOSCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_ZONE_GET_CONSUMPTION = JSON_ZONE_PREFIX+GET_CONSUMPTION_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_ZONE_SCENE_SET_NAME = JSON_ZONE_PREFIX+SCENE_SET_NAME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_ZONE_SCENE_GET_NAME = JSON_ZONE_PREFIX+SCENE_GET_NAME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_ZONE_PUSH_SENSOR_VALUES = JSON_ZONE_PREFIX+PUSH_SENSOR_VALUES_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_ZONE_GET_REACHABLE_SCENES = JSON_ZONE_PREFIX+GET_REACHABLE_SCENES_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + + // Device + public final static String JSON_DEVICE_GET_NAME = JSON_DEVICE_PREFIX+GET_NAME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_SET_NAME = JSON_DEVICE_PREFIX+SET_NAME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_GET_SPEC = JSON_DEVICE_PREFIX+GET_SPEC_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_GET_GROUPS = JSON_DEVICE_PREFIX+GET_GROUPS_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_TURN_ON = JSON_DEVICE_PREFIX+TURN_ON_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_TURN_OFF = JSON_DEVICE_PREFIX+TURN_OFF_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_INCREASE_VALUE = JSON_DEVICE_PREFIX+INCREASE_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_DECREASE_VALUE = JSON_DEVICE_PREFIX+DECREASE_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_SAVESCENE = JSON_DEVICE_PREFIX+SAVESCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_UNDOSCENE = JSON_DEVICE_PREFIX+UNDOSCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_GET_CONSUMPTION = JSON_DEVICE_PREFIX+GET_CONSUMPTION_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_ADD_TAG = JSON_DEVICE_PREFIX+ADD_TAG_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_REMOVE_TAG = JSON_DEVICE_PREFIX+REMOVE_TAG_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_HAS_TAG = JSON_DEVICE_PREFIX+HAS_TAG_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_GET_TAGS = JSON_DEVICE_PREFIX+GET_TAGS_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_LOCK = JSON_DEVICE_PREFIX+LOCK_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_UNLOCK = JSON_DEVICE_PREFIX+UNLOCK_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_GET_SENSOR_EVENT_TABLE_ENTRY = JSON_DEVICE_PREFIX+GET_SENSOR_EVENT_TABLE_ENTRY_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_SET_SENSOR_EVENT_TABLE_ENTRY = JSON_DEVICE_PREFIX+SET_SENSOR_EVENT_TABLE_ENTRY_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_ADD_TO_AREA = JSON_DEVICE_PREFIX+ADD_TO_AREA_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_REMOVE_FROM_AREA = JSON_DEVICE_PREFIX+REMOVE_FROM_AREA_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_ENABLE = JSON_DEVICE_PREFIX+ENABLE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_DISABLE = JSON_DEVICE_PREFIX+DISABLE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_GET_STATE = JSON_DEVICE_PREFIX+GET_STATE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_CALLSCENE = JSON_DEVICE_PREFIX+CALLSCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_SET_CONFIG = JSON_DEVICE_PREFIX+SET_CONFIG_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_GET_CONFIG = JSON_DEVICE_PREFIX+GET_CONFIG_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_GET_CONFIG_WORD = JSON_DEVICE_PREFIX+GET_CONFIG_WORD_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_SET_JOKER_GROUP = JSON_DEVICE_PREFIX+SET_JOKER_GROUP_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_BUTTON_ID = JSON_DEVICE_PREFIX+SET_BUTTON_ID_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_BUTTON_INPUT_MODE = JSON_DEVICE_PREFIX+SET_BUTTON_INPUT_MODE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_SET_OUTPUT_MODE = JSON_DEVICE_PREFIX+SET_OUTPUT_MODE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_SET_PROG_MODE = JSON_DEVICE_PREFIX+SET_PROG_MODE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_GET_OUTPUT_VALUE = JSON_DEVICE_PREFIX+GET_OUTPUT_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_SET_OUTPUT_VALUE = JSON_DEVICE_PREFIX+SET_OUTPUT_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_GET_SCENE_MODE = JSON_DEVICE_PREFIX+GET_SCENE_MODE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_SET_SCENE_MODE = JSON_DEVICE_PREFIX+SET_SCENE_MODE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_GET_TRANSITION_TIME = JSON_DEVICE_PREFIX+GET_TRANSITION_TIME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_SET_TRANSITION_TIME = JSON_DEVICE_PREFIX+SET_TRANSITION_TIME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_GET_LED_MODE = JSON_DEVICE_PREFIX+GET_LED_MODE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_SET_LED_MODE = JSON_DEVICE_PREFIX+SET_LED_MODE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_GET_SENSOR_VALUE = JSON_DEVICE_PREFIX+GET_SENSOR_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_GET_SENSOR_TYPE = JSON_DEVICE_PREFIX+GET_SENSOR_TYPE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_DEVICE_SET_VALUE = JSON_DEVICE_PREFIX+SET_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + + // Circuit + public final static String JSON_CIRCUIT_GET_NAME = JSON_CIRCUIT_PREFIX+GET_NAME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_CIRCUIT_SET_NAME = JSON_CIRCUIT_PREFIX+SET_NAME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_CIRCUIT_GET_CONSUMPTION = JSON_CIRCUIT_PREFIX+GET_CONSUMPTION_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_CIRCUIT_GET_ENERGY_METER_VALUE = JSON_CIRCUIT_PREFIX+GET_ENERGY_METER_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_CIRCUIT_RESCAN = JSON_CIRCUIT_PREFIX+RESCAN_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + + // Property + public final static String JSON_PROPERTY_GET_STRING = JSON_PROPERTY_PREFIX+GET_STRING_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_PROPERTY_GET_INTEGER = JSON_PROPERTY_PREFIX+GET_INTEGER_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_PROPERTY_GET_BOOLEAN = JSON_PROPERTY_PREFIX+GET_BOOLEAN_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_PROPERTY_SET_STRING = JSON_PROPERTY_PREFIX+SET_STRING_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_PROPERTY_SET_INTEGER = JSON_PROPERTY_PREFIX+SET_INTEGER_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_PROPERTY_SET_BOOLEAN = JSON_PROPERTY_PREFIX+SET_BOOLEAN_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_PROPERTY_GET_CHILDREN = JSON_PROPERTY_PREFIX+GET_CHILDREN_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_PROPERTY_GET_TYPE = JSON_PROPERTY_PREFIX+GET_TYPE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_PROPERTY_SET_FLAG = JSON_PROPERTY_PREFIX+SET_FLAG_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_PROPERTY_GET_FLAGS = JSON_PROPERTY_PREFIX+GET_FLAGS_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_PROPERTY_QUERY = JSON_PROPERTY_PREFIX+QUERY_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_PROPERTY_REMOVE = JSON_PROPERTY_PREFIX+REMOVE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + + // Event + public final static String JSON_EVENT_RAISE = JSON_EVENT_PREFIX+RAISE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_EVENT_SUBSCRIBE = JSON_EVENT_PREFIX+SUBSCRIBE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_EVENT_UNSUBSCRIBE = JSON_EVENT_PREFIX+UNSUBSCRIBE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_EVENT_GET = JSON_EVENT_PREFIX+GET_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + + // System + public final static String JSON_SYSTEM_VERSION = JSON_SYSTEM_PREFIX+VERSION_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_SYSTEM_TIME = JSON_SYSTEM_PREFIX+TIME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_SYSTEM_LOGIN = JSON_SYSTEM_PREFIX+LOGIN_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_SYSTEM_LOGIN_APPLICATION = JSON_SYSTEM_PREFIX+LOGIN_APPLICATION_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL+LOGIN_TOKEN_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String JSON_SYSTEM_LOGOUT = JSON_SYSTEM_PREFIX+LOGOUT_TO_STRING; + + public final static String JSON_SYSTEM_LOGGED_IN_USER = JSON_SYSTEM_PREFIX+LOGGED_IN_USER_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + + // Set + public final static String JSON_SET_FROM_APARTMENT = JSON_SET_PREFIX+FROM_APARTMENT_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_SET_BY_ZONE = JSON_SET_PREFIX+BY_ZONE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_SET_BY_GROUP = JSON_SET_PREFIX+BY_GROUP_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_SET_BY_DSID = JSON_SET_PREFIX+BY_DSID_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_SET_ADD = JSON_SET_PREFIX+ADD_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_SET_SUBTRACT = JSON_SET_PREFIX+SUBTRACT_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_SET_TURN_ON = JSON_SET_PREFIX+TURN_ON_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_SET_TURN_OFF = JSON_SET_PREFIX+TURN_OFF_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_SET_INCREASE_VALUE = JSON_SET_PREFIX+INCREASE_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_SET_DECREASE_VALUE = JSON_SET_PREFIX+DECREASE_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_SET_ENABLE = JSON_SET_PREFIX+ENABLE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_SET_DISABLE = JSON_SET_PREFIX+DISABLE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_SET_SET_VALUE = JSON_SET_PREFIX+SET_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_SET_CALLSCENE = JSON_SET_PREFIX+CALLSCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_SET_SAVESCENE = JSON_SET_PREFIX+SAVESCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_SET_UNDOSCENE = JSON_SET_PREFIX+UNDOSCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_SET_GET_CONSUMPTION = JSON_SET_PREFIX+GET_CONSUMPTION_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + + // Structure + public final static String JSON_STRUCTURE_ZONE_ADD_DEVICE = JSON_STRUCTURE_PREFIX+ZONE_ADD_DEVICE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_STRUCTURE_ADD_ZONE = JSON_STRUCTURE_PREFIX+ADD_ZONE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_STRUCTURE_REMOVE_ZONE = JSON_STRUCTURE_PREFIX+REMOVE_ZONE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_STRUCTURE_REMOVE_DEVICE = JSON_STRUCTURE_PREFIX+REMOVE_DEVICE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_STRUCTURE_PERSIST_SET = JSON_STRUCTURE_PREFIX+PERSIST_SET_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_STRUCTURE_UNPERSIST_SET = JSON_STRUCTURE_PREFIX+UNPERSIST_SET_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_STRUCTURE_ADD_GROUP = JSON_STRUCTURE_PREFIX+ADD_GROUP_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_STRUCTURE_GROUP_ADD_DEVICE = JSON_STRUCTURE_PREFIX+GROUP_ADD_DEVICE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_STRUCTURE_GROUP_REMOVE_DEVICE = JSON_STRUCTURE_PREFIX+GROUP_REMOVE_DEVICE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + + // Metering + public final static String JSON_METERING_GET_RESOLUTIONS = JSON_METERING_PREFIX+GET_RESOLUTIONS_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_METERING_GET_SERIES = JSON_METERING_PREFIX+GET_SERIES_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_METERING_GET_VALUES = JSON_METERING_PREFIX+GET_VALUES_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + public final static String JSON_METERING_GET_LATEST = JSON_METERING_PREFIX+GET_LATEST_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; + + + // Token + public final static String JSON_TOKEN_AT_LAST_PREFIX = NEXT_PARAMETER_CONCAT_SYMBOL+TOKEN_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String JSON_TOKEN_AT_FIRST = FIRST_PARAMETER_CONCAT_SYMBOL+TOKEN_TO_STRING+EQUAL_SIGN_SYMBOL; + + public final static String QUERY_GET_METERLIST = "/apartment/dSMeters/*(dSID)"; +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDiscoveryParticipant.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDiscoveryParticipant.java new file mode 100644 index 0000000000000..e5232db3ad22f --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDiscoveryParticipant.java @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.binding.siemenshvac.internal.discovery; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.eclipse.smarthome.config.discovery.DiscoveryResult; +import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; +import org.eclipse.smarthome.config.discovery.UpnpDiscoveryParticipant; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.jupnp.model.meta.RemoteDevice; +import org.openhab.binding.siemenshvac.SiemensHvacBindingConstants; +import org.osgi.service.component.ComponentContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.Collections2; + +/** + * An UpnpDiscoveryParticipant which allows to discover Pioneer AVRs. + * + * @author Laurent ARNAL + * + */ +public class SiemensHvacDiscoveryParticipant implements UpnpDiscoveryParticipant { + + private Logger logger = LoggerFactory.getLogger(SiemensHvacDiscoveryParticipant.class); + + private boolean isAutoDiscoveryEnabled; + private Set supportedThingTypes; + + public SiemensHvacDiscoveryParticipant() { + this.isAutoDiscoveryEnabled = true; + this.supportedThingTypes = SiemensHvacBindingConstants.SUPPORTED_THING_TYPES_UIDS; + } + + /** + * Called at the service activation. + * + * @param componentContext + */ + protected void activate(ComponentContext componentContext) { + if (componentContext.getProperties() != null) { + String autoDiscoveryPropertyValue = (String) componentContext.getProperties().get("enableAutoDiscovery"); + if (StringUtils.isNotEmpty(autoDiscoveryPropertyValue)) { + isAutoDiscoveryEnabled = Boolean.valueOf(autoDiscoveryPropertyValue); + } + } + supportedThingTypes = isAutoDiscoveryEnabled ? SiemensHvacBindingConstants.SUPPORTED_THING_TYPES_UIDS + : new HashSet(); + } + + @Override + public Set getSupportedThingTypeUIDs() { + return supportedThingTypes; + } + + @Override + public DiscoveryResult createResult(RemoteDevice device) { + DiscoveryResult result = null; + ThingUID thingUid = getThingUID(device); + if (thingUid != null) { + + String label = StringUtils.isEmpty(device.getDetails().getFriendlyName()) ? device.getDisplayString() + : device.getDetails().getFriendlyName(); + Map properties = new HashMap<>(2, 1); + properties.put(SiemensHvacBindingConstants.HOST_PARAMETER, + device.getIdentity().getDescriptorURL().getHost()); + properties.put(SiemensHvacBindingConstants.PROTOCOL_PARAMETER, + SiemensHvacBindingConstants.IP_PROTOCOL_NAME); + + result = DiscoveryResultBuilder.create(thingUid).withLabel(label).withProperties(properties).build(); + } + + return result; + } + + @Override + public ThingUID getThingUID(RemoteDevice device) { + ThingUID result = null; + if (isAutoDiscoveryEnabled) { + + if (StringUtils.containsIgnoreCase(device.getDetails().getManufacturerDetails().getManufacturer(), + SiemensHvacBindingConstants.MANUFACTURER)) { + logger.debug("Manufacturer matched: search: {}, device value: {}.", + SiemensHvacBindingConstants.MANUFACTURER, + device.getDetails().getManufacturerDetails().getManufacturer()); + if (StringUtils.containsIgnoreCase(device.getType().getType(), + SiemensHvacBindingConstants.UPNP_DEVICE_TYPE)) { + logger.debug("Device type matched: search: {}, device value: {}.", + SiemensHvacBindingConstants.UPNP_DEVICE_TYPE, device.getType().getType()); + + String deviceModel = device.getDetails().getModelDetails() != null + ? device.getDetails().getModelDetails().getModelName() : null; + ThingTypeUID thingTypeUID = SiemensHvacBindingConstants.HVAC_THING_TYPE; + if (!isSupportedDeviceModel(deviceModel)) { + logger.debug("Device model {} not supported. Odd behaviors may happen.", deviceModel); + thingTypeUID = SiemensHvacBindingConstants.HVAC_UNSUPPORTED_THING_TYPE; + } + + result = new ThingUID(thingTypeUID, device.getIdentity().getUdn().getIdentifierString()); + } + } + } + + return result; + } + + /** + * Return true only if the given device model is supported. + * + * @param deviceModel + * @return + */ + private boolean isSupportedDeviceModel(final String deviceModel) { + return StringUtils.isNotBlank(deviceModel) + && !Collections2.filter(SiemensHvacBindingConstants.SUPPORTED_DEVICE_MODELS, + new com.google.common.base.Predicate() { + @Override + public boolean apply(String input) { + return StringUtils.startsWithIgnoreCase(deviceModel, input); + } + }).isEmpty(); + } + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacConnector.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacConnector.java new file mode 100644 index 0000000000000..d9e6f25281159 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacConnector.java @@ -0,0 +1,481 @@ +package org.openhab.binding.siemenshvac.internal; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.util.Date; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class siemensHvacConnector extends Thread { + + private static final Logger logger = LoggerFactory.getLogger(siemensHvacConnector.class); + + private String sessionId = null; + private String baseUrl = ""; + private String userName = ""; + private String userPassword = ""; + private Date lastUpdate; + + // private Map updateCommand; + + private boolean interrupted = false; + + public siemensHvacConnector(String host, String userName, String userPassword) { + this.baseUrl = "http://" + host + "/"; + this.userName = userName; + this.userPassword = userPassword; + + readDpTree(); + // this.updateCommand = new Hashtable(); + start(); + } + + private void readDpTree() { + logger.debug("siemensHvac:listDP():"); + readDpTree(null, 0, 1); + } + + private void readDpTree(String parentId, int level, int maxLevel) { + logger.debug("siemensHvac:listDP():" + parentId); + + if (level >= maxLevel) { + return; + } + + try { + String request = "api/menutree/list.json?"; + if (parentId != null) { + request = request + "Id=" + parentId; + } + + JSONObject result = DoRequest(request); + + if (result != null) { + if (result.containsKey("MenuItems")) { + JSONArray menuItems = (JSONArray) result.get("MenuItems"); + + for (Object item : menuItems) { + + JSONObject jItem = (JSONObject) item; + String Id = jItem.get("Id").toString().trim(); + JSONObject Text = (JSONObject) jItem.get("Text"); + + String longLabel = Text.get("Long").toString().trim(); + String shortLabel = Text.get("Short").toString().trim(); + String catId = Text.get("CatId").toString().trim(); + String groupdId = Text.get("GroupId").toString().trim(); + String textId = Text.get("Id").toString().trim(); + + System.out.println( + String.format("Id : %s ShortLabel: %s LongLabel : %s", Id, shortLabel, longLabel)); + + readDpTree(Id, level + 1, maxLevel); + + } + } + if (result.containsKey("DatapointItems")) { + JSONArray dataPtItems = (JSONArray) result.get("DatapointItems"); + + for (Object item : dataPtItems) { + + JSONObject dItem = (JSONObject) item; + String Id = dItem.get("Id").toString().trim(); + String Address = dItem.get("Address").toString().trim(); + String DpSubKey = dItem.get("DpSubKey").toString().trim(); + String WriteAccess = dItem.get("WriteAccess").toString().trim(); + + JSONObject Text = (JSONObject) dItem.get("Text"); + + String longLabel = Text.get("Long").toString().trim(); + String shortLabel = Text.get("Short").toString().trim(); + String catId = Text.get("CatId").toString().trim(); + String groupdId = Text.get("GroupId").toString().trim(); + String textId = Text.get("Id").toString().trim(); + + System.out.println(String.format("Id : %s Address: %s ShortLabel: %s LongLabel : %s", Id, + Address, shortLabel, longLabel)); + } + } + } + + } catch (Exception e) { + logger.error("siemensHvac:ReadDp:Error during dp reading: " + e.getLocalizedMessage()); + // Reset sessionId so we redone _auth on error + sessionId = null; + } + + } + + private void doAuth() { + + HttpURLConnection connection = null; + logger.debug("siemensHvac:doAuth()"); + + String request = "api/auth/login.json?user=" + userName + "&pwd=" + userPassword; + StringBuilder response = new StringBuilder(); + BufferedReader in = null; + + try { + URL url = new URL(baseUrl + request); + logger.debug("siemensHvac:doAuth:openCnx()"); + connection = (HttpURLConnection) url.openConnection(); + + int responseCode = -1; + if (connection != null) { + connection.setConnectTimeout(60000); + connection.setReadTimeout(60000); + + try { + logger.debug("siemensHvac:doAuth:connect()"); + connection.connect(); + responseCode = connection.getResponseCode(); + } catch (SocketTimeoutException e) { + logger.error("siemensHvac:doAuth:" + e.getMessage() + " : " + request); + return; + } + + logger.debug("siemensHvac:doAuth:response()" + responseCode); + if (responseCode == HttpURLConnection.HTTP_OK) { + String inputLine = null; + in = new BufferedReader(new InputStreamReader(connection.getInputStream())); + + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + logger.debug("siemensHvac:doAuthaddline:()" + inputLine); + } + + in.close(); + } else { + response = null; + } + logger.debug("siemensHvac:doAuth:Endresponse:()" + responseCode); + } + + if (response != null) { + String st = response.toString(); + logger.debug("siemensHvac:doAuth:decodeResponse:()" + st); + JSONObject result = JSONResponseHandler.toJSONObject(st); + logger.debug("siemensHvac:doAuth:afterJson"); + if (result.containsKey("Result")) { + JSONObject subResult = (JSONObject) result.get("Result"); + if (subResult.containsKey("Success")) { + if (subResult.get("Success").equals("true")) { + if (result.containsKey("SessionId")) { + sessionId = result.get("SessionId").toString(); + } + } + + } + + } + } + } catch (MalformedURLException e) { + logger.error("siemensHvac:MalformedURLException by executing jsonRequest: " + request + " ; " + + e.getLocalizedMessage()); + } catch (IOException e) { + logger.error( + "siemensHvac:IOException by executing jsonRequest: " + request + " ; " + e.getLocalizedMessage()); + } catch (Exception e) { + logger.error("siemensHvac:Error during auth: " + e.getLocalizedMessage()); + } finally { + if (connection != null) { + connection.disconnect(); + } + } + + } + + public void ReadAllDp() { + /* + * siemensHvacGenericBindingProvider provider = hvacBinding.getProvider(); + * logger.debug("siemensHvac:readAllDP():begin"); + * + * Iterator it = provider.getBindingConfigs().values().iterator(); + * + * while (it.hasNext()) { + * siemensHvacBindingConfig config = (siemensHvacBindingConfig) it.next(); + * String name = config.getName(); + * String type = config.getType(); + * String dp = config.getDpt(); + * + * ReadDp(name, dp, type); + * } + * logger.debug("siemensHvac:readAllDP():end"); + */ + } + + public String ReadDp(String name, String dp, String type) { + String value = _readDpInternal(name, dp, type); + + logger.debug("siemensHvac:ReadDP:" + name + ":" + dp + ":" + value); + + if (value == null || value.equals("----") || value.equals("")) { + return null; + } + + if (type.equals("Numeric")) { + // hvacBinding.getEventPublisher().postUpdate(name, new DecimalType(value)); + } else if (type.equals("Enumeration")) { + String valueEnum = value; + if (type.equals("Enumeration")) { + String[] values = value.split(":"); + valueEnum = values[0]; + value = values[1]; + } + + // hvacBinding.getEventPublisher().postUpdate(name, new StringType(valueEnum)); + } else if (type.equals("Text")) { + // hvacBinding.getEventPublisher().postUpdate(name, new StringType(value)); + } else { + // hvacBinding.getEventPublisher().postUpdate(name, new StringType(value)); + } + + return value; + } + + /* + * public void WriteDp(String name, Type dp) { + * if (sessionId == null) { + * _doAuth(); + * } + * + * + * siemensHvacGenericBindingProvider provider = hvacBinding.getProvider(); + * + * siemensHvacBindingConfig bindingConfig = (siemensHvacBindingConfig) provider.getBindingConfigs().get(name); + * String dpt = bindingConfig.getDpt(); + * String type = bindingConfig.getType(); + * + * String valAct = _readDpInternal(name, dpt, type); + * String valActEnum = valAct; + * + * if (type.equals("Enumeration")) { + * String[] values = valAct.split(":"); + * valActEnum = values[0]; + * valAct = values[1]; + * } + * String val = ""; + * if (dp instanceof PercentType) { + * PercentType pct = (PercentType) dp; + * val = pct.toString(); + * } else if (dp instanceof DecimalType) { + * DecimalType bdc = (DecimalType) dp; + * val = bdc.toString(); + * } else if (dp instanceof StringType) { + * StringType bdc = (StringType) dp; + * val = bdc.toString(); + * } + * + * if (valAct != null && val.equals(valAct)) { + * return; + * } + * if (valActEnum != null && val.equals(valActEnum)) { + * return; + * } + * + * siemensHvacBindingConfig config = (siemensHvacBindingConfig) provider.getBindingConfigs().get(name); + * String dpType = config.getType(); + * + * String request = "api/menutree/write_datapoint.json?SessionId=" + sessionId + "&Id=" + dpt + "&Value=" + val + * + "&Type=" + dpType; + * + * String response = DoRequest(request); + * logger.debug("siemensHvac:WriteDP(response) = " + response); + * + * ReadDp(name, dpt, type); + * + * } + */ + public JSONObject DoRequest(String request) { + HttpURLConnection connection = null; + StringBuilder response = new StringBuilder(); + BufferedReader in = null; + + if (sessionId == null) { + doAuth(); + } + + request = request + "&SessionId=" + sessionId; + + logger.debug("siemensHvac:ReadDp:DoRequest():" + request); + + try { + URL url = new URL(baseUrl + request); + + connection = (HttpURLConnection) url.openConnection(); + + int responseCode = -1; + if (connection != null) { + connection.setConnectTimeout(6000); + connection.setReadTimeout(6000); + + try { + connection.connect(); + responseCode = connection.getResponseCode(); + } catch (SocketTimeoutException e) { + logger.error("siemensHvac:DoRequest:" + e.getMessage() + " : " + request); + return null; + } + + if (responseCode == HttpURLConnection.HTTP_OK) { + String inputLine = null; + in = new BufferedReader(new InputStreamReader(connection.getInputStream())); + + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + + in.close(); + } else { + response = null; + } + + logger.debug("siemensHvac:ReadDp:response:" + response); + + if (response != null) { + + JSONObject result = JSONResponseHandler.toJSONObject(response.toString()); + if (result.containsKey("Result")) { + JSONObject subResult = (JSONObject) result.get("Result"); + if (subResult.containsKey("Success")) { + if (subResult.get("Success").equals("true")) { + return result; + } + } + } + return null; + } + + } + } catch (MalformedURLException e) { + logger.error("siemensHvac:DoRequest:MalformedURLException by executing jsonRequest: " + request + " ; " + + e.getLocalizedMessage()); + } catch (IOException e) { + logger.error("siemensHvac:DoRequest:IOException by executing jsonRequest: " + request + " ; " + + e.getLocalizedMessage()); + } catch (Exception e) { + logger.error("siemensHvac:DoRequest:Exception by executing jsonRequest: " + request + " ; " + + e.getLocalizedMessage()); + } finally { + if (connection != null) { + connection.disconnect(); + } + } + + return null; + } + + private String _readDpInternal(String name, String dp, String type) { + logger.debug("siemensHvac:readDP():" + name); + /* + * try { + * + * JSONObject subResult = DoRequest("api/menutree/read_datapoint.json?Id=" + dp); + * + * logger.debug("siemensHvac:ReadDp:response:" + response); + * if (response != null) { + * JSONObject result = JSONResponseHandler.toJSONObject(response); + * if (result.containsKey("Result")) { + * JSONObject subResult = (JSONObject) result.get("Result"); + * if (subResult.containsKey("Success")) { + * if (subResult.get("Success").equals("true")) { + * if (result.containsKey("Data")) { + * subResult = (JSONObject) result.get("Data"); + * + * // {"Value":"Automatique","Type":"Enumeration","EnumValue":"1","Unit":""} + * String typer = ""; + * String value = ""; + * String enumValue = ""; + * + * if (subResult.containsKey("Type")) { + * typer = subResult.get("Type").toString().trim(); + * } + * if (subResult.containsKey("Value")) { + * value = subResult.get("Value").toString().trim(); + * } + * if (subResult.containsKey("EnumValue")) { + * enumValue = subResult.get("EnumValue").toString().trim(); + * } + * + * if (typer.equals("Enumeration")) { + * return "" + enumValue + ":" + value; + * } else { + * return value; + * } + * } + * } else { + * sessionId = null; + * } + * } + * } + * } + * } catch (Exception e) { + * logger.error("siemensHvac:ReadDp:Error during dp reading: " + name + " ; " + e.getLocalizedMessage()); + * // Reset sessionId so we redone _auth on error + * sessionId = null; + * } + */ + return ""; + } + + /* + * public void AddDpUpdate(String itemName, Type dp) { + * synchronized (updateCommand) { + * updateCommand.put(itemName, dp); + * lastUpdate = new java.util.Date(); + * } + * } + */ + + @Override + public void run() { + logger.debug("siemensHvac:sender thread start"); + + // as long as no interrupt is requested, continue running + while (!interrupted) { + try { + Thread.sleep(2000); + Date currentDate = new java.util.Date(); + + logger.debug("siemensHvac:sender thread alive:" + currentDate); + + if (lastUpdate == null) { + continue; + } + + long ms = currentDate.getTime() - lastUpdate.getTime(); + if (ms < 3000) { + continue; + } + + /* + * synchronized (updateCommand) { + * if (updateCommand.isEmpty()) { + * continue; + * } + * + * logger.debug("siemensHvac:sender thread updateCommand"); + * + * for (String key : updateCommand.keySet()) { + * Type dp = updateCommand.get(key); + * WriteDp(key, dp); + * + * } + * + * updateCommand.clear(); + * } + */ + } catch (Exception e) { + logger.error("siemensHvac:Error occured will sending update values", e); + } + } + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacDiscoveryService.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacDiscoveryService.java new file mode 100644 index 0000000000000..fa24632960e00 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacDiscoveryService.java @@ -0,0 +1,132 @@ +/** + * Copyright (c) 2014-2016 by the respective copyright holders. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.binding.siemenshvac.internal; + +import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService; +import org.openhab.binding.siemenshvac.handler.SiemensHvacHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link HueBridgeServiceTracker} tracks for hue lights which are connected + * to a paired hue bridge. The default search time for hue is 60 seconds. + * + * @author Kai Kreuzer - Initial contribution + * @author Andre Fuechsel - changed search timeout, changed discovery result creation to support generic thing types + * @author Thomas Höfer - Added representation + */ +public class siemensHvacDiscoveryService extends AbstractDiscoveryService { + + private final Logger logger = LoggerFactory.getLogger(siemensHvacDiscoveryService.class); + + private final static int SEARCH_TIME = 60; + private final static String MODEL_ID = "modelId"; + + // @formatter:off + + private SiemensHvacHandler hvacHandler; + + public siemensHvacDiscoveryService(SiemensHvacHandler hvacHandler) { + super(SEARCH_TIME); + this.hvacHandler = hvacHandler; + } + + public void activate() { + //hvacHandler.registerLightStatusListener(this); + } + + @Override + public void deactivate() { + //removeOlderResults(new Date().getTime()); + //hueBridgeHandler.unregisterLightStatusListener(this); + } + + /* + @Override + public Set getSupportedThingTypes() { + return HueLightHandler.SUPPORTED_THING_TYPES; + } +*/ + + @Override + public void startScan() { + /* + List lights = hueBridgeHandler.getFullLights(); + if (lights != null) { + for (FullLight l : lights) { + onLightAddedInternal(l); + } + } + // search for unpaired lights + hueBridgeHandler.startSearch(); + */ + } + + @Override + protected synchronized void stopScan() { + super.stopScan(); + removeOlderResults(getTimestampOfLastScan()); + } + +/* + private void onLightAddedInternal(FullLight light) { + ThingUID thingUID = getThingUID(light); + ThingTypeUID thingTypeUID = getThingTypeUID(light); + + String modelId = light.getModelID().replaceAll(HueLightHandler.NORMALIZE_ID_REGEX, "_"); + + if (thingUID != null && thingTypeUID != null) { + ThingUID bridgeUID = hueBridgeHandler.getThing().getUID(); + Map properties = new HashMap<>(1); + properties.put(LIGHT_ID, light.getId()); + properties.put(MODEL_ID, modelId); + + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID) + .withProperties(properties).withBridge(bridgeUID).withLabel(light.getName()).build(); + + thingDiscovered(discoveryResult); + } else { + logger.debug("discovered unsupported light of type '{}' and model '{}' with id {}", light.getType(), + modelId, light.getId()); + } + } + + + @Override + public void onLightRemoved(HueBridge bridge, FullLight light) { + ThingUID thingUID = getThingUID(light); + + if (thingUID != null) { + thingRemoved(thingUID); + } + } + + @Override + public void onLightStateChanged(HueBridge bridge, FullLight light) { + // nothing to do + } + + private ThingUID getThingUID(FullLight light) { + ThingUID bridgeUID = hueBridgeHandler.getThing().getUID(); + ThingTypeUID thingTypeUID = getThingTypeUID(light); + + if (thingTypeUID != null && getSupportedThingTypes().contains(thingTypeUID)) { + return new ThingUID(thingTypeUID, bridgeUID, light.getId()); + } else { + return null; + } + } + + private ThingTypeUID getThingTypeUID(FullLight light) { + String thingTypeId = TYPE_TO_ZIGBEE_ID_MAP + .get(light.getType().replaceAll(HueLightHandler.NORMALIZE_ID_REGEX, "_").toLowerCase()); + return thingTypeId != null ? new ThingTypeUID(BINDING_ID, thingTypeId) : null; + } + + */ +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacGenericBindingProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacGenericBindingProvider.java new file mode 100644 index 0000000000000..e72e712b773d3 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacGenericBindingProvider.java @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2010-2015, openHAB.org and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +/* + * package org.openhab.binding.siemenshvac.internal; + * + * + * import java.util.Map; + * + * import org.openhab.binding.siemenshvac.siemensHvacBindingProvider; + * import org.openhab.core.binding.BindingConfig; + * import org.openhab.core.items.Item; + * import org.openhab.core.library.items.DimmerItem; + * import org.openhab.core.library.items.SwitchItem; + * import org.openhab.model.item.binding.AbstractGenericBindingProvider; + * import org.openhab.model.item.binding.BindingConfigParseException; + * import org.slf4j.Logger; + * import org.slf4j.LoggerFactory; + * + * + * public class siemensHvacGenericBindingProvider extends + * AbstractGenericBindingProvider implements siemensHvacBindingProvider { + * private static final Logger logger = LoggerFactory + * .getLogger(siemensHvacGenericBindingProvider.class); + * + * + * public String getBindingType() { + * logger.debug("siemensHvac:hvac getBindingType()!"); + * return "siemenshvac"; + * } + * + * + * @Override + * public void validateItemType(Item item, String bindingConfig) + * throws BindingConfigParseException { + * // if (!(item instanceof SwitchItem || item instanceof DimmerItem)) { + * // throw new BindingConfigParseException("item '" + item.getName() + * // + "' is of type '" + item.getClass().getSimpleName() + * // + + * // "', only Switch- and DimmerItems are allowed - please check your *.items configuration"); + * // } + * } + * + * + * @Override + * public void processBindingConfiguration(String context, Item item, + * String bindingConfig) throws BindingConfigParseException { + * logger.debug("siemensHvac:hvac processBindingConfiguration()!"); + * super.processBindingConfiguration(context, item, bindingConfig); + * siemensHvacBindingConfig config = new siemensHvacBindingConfig( + * item.getName(), bindingConfig); + * + * // parse bindingconfig here ... + * + * addBindingConfig(item, config); + * } + * + * public Map getBindingConfigs() { + * return bindingConfigs; + * } + * + * + * class siemensHvacBindingConfig implements BindingConfig { + * // put member fields here which holds the parsed values + * private String name; + * private String bindingConfig; + * private String dpt; + * private String type; + * + * public siemensHvacBindingConfig(String name, String bindingConfig) { + * this.name = name; + * this.bindingConfig = bindingConfig; + * + * String[] bindingValues = bindingConfig.split(";"); + * + * for (String bindingValue : bindingValues) { + * String[] bindingParts = bindingValue.split(":"); + * + * String bindingKey = bindingParts[0]; + * String bindingVal = bindingParts[1]; + * + * if (bindingKey.equals("dpt")) + * this.dpt = bindingVal; + * else if (bindingKey.equals("type")) + * this.type = bindingVal; + * } + * } + * + * public String getDpt() { + * return dpt; + * } + * + * public String getName() { + * return name; + * } + * + * public String getType() { + * return type; + * } + * + * } + * + * } + */ \ No newline at end of file From 17000f714f06c8ca1eb1c3703df546e44d62de56 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 8 Jan 2023 11:55:44 +0100 Subject: [PATCH 002/214] first working 3.x versions Signed-off-by: Laurent ARNAL --- .../ESH-INF/binding/binding.xml | 11 - .../ESH-INF/thing/thing-types.xml | 53 - .../META-INF/MANIFEST.MF | 31 - .../org.openhab.binding.siemenshvac/NOTICE | 13 + .../OSGI-INF/SiemensHvacDiscovery.xml | 17 - .../OSGI-INF/SiemensHvacHandlerFactory.xml | 19 - .../org.openhab.binding.siemenshvac/README.md | 56 + .../build.properties | 7 - .../lib/json-simple-1.1.1.jar | Bin 23737 -> 0 bytes .../org.openhab.binding.siemenshvac/pom.xml | 13 +- .../src/main/feature/feature.xml | 25 + .../SiemensHvacBindingConstants.java | 51 - .../handler/SiemensHvacHandler.java | 77 -- .../internal/JSONResponseHandler.java | 63 -- .../Metadata/RuntimeTypeAdapterFactory.java | 453 ++++++++ .../Metadata/SiemensHvacMetadata.java | 70 ++ .../SiemensHvacMetadataDataPoint.java | 214 ++++ .../Metadata/SiemensHvacMetadataDevice.java | 77 ++ .../Metadata/SiemensHvacMetadataMenu.java | 21 + .../SiemensHvacMetadataPointChild.java | 50 + .../Metadata/SiemensHvacMetadataRegistry.java | 46 + .../SiemensHvacMetadataRegistryImpl.java | 869 ++++++++++++++++ .../internal/SiemensHvacHandlerFactory.java | 79 -- .../config/SiemensHvacConfiguration.java | 26 + .../constants/JSONApiResponseKeysEnum.java | 198 ---- .../constants/JSONRequestConstants.java | 973 ------------------ .../SiemensHvacBindingConstants.java | 78 ++ .../SiemenesHvacDiscoveryParticipant.java | 95 ++ .../SiemensHvacDeviceDiscoveryService.java | 172 ++++ .../SiemensHvacDiscoveryParticipant.java | 137 --- .../factory/SiemensHvacHandlerFactory.java | 114 ++ .../SiemensHvacBridgeBaseThingHandler.java | 113 ++ .../internal/handler/SiemensHvacHandler.java | 8 + .../handler/SiemensHvacHandlerImpl.java | 332 ++++++ .../SiemensHvacOZW672BridgeThingHandler.java | 86 ++ .../internal/network/SiemensHvacCallback.java | 15 + .../network/SiemensHvacConnector.java | 19 + .../network/SiemensHvacConnectorImpl.java | 456 ++++++++ .../network/SiemensHvacRequestListener.java | 101 ++ .../internal/siemensHvacConnector.java | 481 --------- .../internal/siemensHvacDiscoveryService.java | 132 --- .../siemensHvacGenericBindingProvider.java | 109 -- .../SiemensHvacChannelGroupTypeProvider.java | 47 + ...emensHvacChannelGroupTypeProviderImpl.java | 69 ++ .../type/SiemensHvacChannelTypeProvider.java | 47 + .../SiemensHvacChannelTypeProviderImpl.java | 70 ++ .../SiemensHvacConfigDescriptionProvider.java | 60 ++ ...mensHvacConfigDescriptionProviderImpl.java | 58 ++ .../type/SiemensHvacThingTypeProvider.java | 47 + .../SiemensHvacThingTypeProviderImpl.java | 64 ++ .../siemenshvac/internal/type/UidUtils.java | 97 ++ .../main/resources/OH-INF/binding/binding.xml | 9 + .../OH-INF}/i18n/siemenshvac_xx_XX.properties | 8 +- .../main/resources/OH-INF/thing/ozw672.xml | 26 + 54 files changed, 4115 insertions(+), 2447 deletions(-) delete mode 100644 bundles/org.openhab.binding.siemenshvac/ESH-INF/binding/binding.xml delete mode 100644 bundles/org.openhab.binding.siemenshvac/ESH-INF/thing/thing-types.xml delete mode 100644 bundles/org.openhab.binding.siemenshvac/META-INF/MANIFEST.MF create mode 100644 bundles/org.openhab.binding.siemenshvac/NOTICE delete mode 100644 bundles/org.openhab.binding.siemenshvac/OSGI-INF/SiemensHvacDiscovery.xml delete mode 100644 bundles/org.openhab.binding.siemenshvac/OSGI-INF/SiemensHvacHandlerFactory.xml create mode 100644 bundles/org.openhab.binding.siemenshvac/README.md delete mode 100644 bundles/org.openhab.binding.siemenshvac/build.properties delete mode 100644 bundles/org.openhab.binding.siemenshvac/lib/json-simple-1.1.1.jar create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/feature/feature.xml delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/SiemensHvacBindingConstants.java delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/handler/SiemensHvacHandler.java delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/JSONResponseHandler.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/SiemensHvacHandlerFactory.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/config/SiemensHvacConfiguration.java delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/JSONApiResponseKeysEnum.java delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/JSONRequestConstants.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDiscoveryParticipant.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandler.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacCallback.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacConnector.java delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacDiscoveryService.java delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacGenericBindingProvider.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProviderImpl.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProviderImpl.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/binding/binding.xml rename bundles/org.openhab.binding.siemenshvac/{ESH-INF => src/main/resources/OH-INF}/i18n/siemenshvac_xx_XX.properties (53%) create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml diff --git a/bundles/org.openhab.binding.siemenshvac/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.siemenshvac/ESH-INF/binding/binding.xml deleted file mode 100644 index b082c33750a15..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/ESH-INF/binding/binding.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - SiemensHvac Binding - This is the binding for SiemensHvac. - Laurent ARNAL - - diff --git a/bundles/org.openhab.binding.siemenshvac/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.siemenshvac/ESH-INF/thing/thing-types.xml deleted file mode 100644 index 0215ec7128622..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/ESH-INF/thing/thing-types.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - Control a Siemens Hvac Bridge over IP - - - - - - - - - network-address - - The address of the Siemens Hvac bridge to control. - - - 80 - - The TCP port number used to connect to the bridge. - - - Administrator - - Nom de l'utilisateur. - - - Mnbo32tyu2! - - Password de l'utilisateur. - - - - - - - - - Dimmer - - Increase/Decrease the temperature (°c) - TemperatureControl - - - - diff --git a/bundles/org.openhab.binding.siemenshvac/META-INF/MANIFEST.MF b/bundles/org.openhab.binding.siemenshvac/META-INF/MANIFEST.MF deleted file mode 100644 index edff17c2743ff..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/META-INF/MANIFEST.MF +++ /dev/null @@ -1,31 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: SiemensHvac Binding -Bundle-SymbolicName: org.openhab.binding.siemenshvac;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.0.0.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-1.7 -Bundle-ClassPath: ., - lib/json-simple-1.1.1.jar -Import-Package: com.google.common.base, - com.google.common.collect, - gnu.io;resolution:=optional, - org.apache.commons.lang, - org.openhab.binding.siemenshvac, - org.openhab.binding.siemenshvac.handler, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.type, - org.eclipse.smarthome.core.types, - org.jupnp.model.meta, - org.jupnp.model.types, - org.osgi.service.cm, - org.osgi.service.component, - org.slf4j -Service-Component: OSGI-INF/*.xml -Export-Package: org.openhab.binding.siemenshvac, - org.openhab.binding.siemenshvac.handler -Require-Bundle: org.eclipse.smarthome.config.discovery diff --git a/bundles/org.openhab.binding.siemenshvac/NOTICE b/bundles/org.openhab.binding.siemenshvac/NOTICE new file mode 100644 index 0000000000000..38d625e349232 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/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.siemenshvac/OSGI-INF/SiemensHvacDiscovery.xml b/bundles/org.openhab.binding.siemenshvac/OSGI-INF/SiemensHvacDiscovery.xml deleted file mode 100644 index ee57a46d6fb29..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/OSGI-INF/SiemensHvacDiscovery.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - diff --git a/bundles/org.openhab.binding.siemenshvac/OSGI-INF/SiemensHvacHandlerFactory.xml b/bundles/org.openhab.binding.siemenshvac/OSGI-INF/SiemensHvacHandlerFactory.xml deleted file mode 100644 index 3bb5022abccd6..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/OSGI-INF/SiemensHvacHandlerFactory.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - diff --git a/bundles/org.openhab.binding.siemenshvac/README.md b/bundles/org.openhab.binding.siemenshvac/README.md new file mode 100644 index 0000000000000..b6df0459eef3a --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/README.md @@ -0,0 +1,56 @@ +# SiemensHvac Binding + +_Give some details about what this binding is meant for - a protocol, system, specific device._ + +_If possible, provide some resources like pictures, a YouTube 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._ + +## Supported Things + +_Please describe the different supported things / devices 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._ + +## 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 Philips Hue Binding +# +# Default secret key for the pairing of the Philips Hue Bridge. +# 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 (Paper) UI or via a thing-file. This should be mainly about its mandatory and optional configuration parameters. A short example entry for a thing file can help!_ + +_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._ + +## 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 | description | +|----------|--------|------------------------------| +| control | Switch | This is the control channel | + +## Full Example + +_Provide a full usage example based on textual configuration files (*.things, *.items, *.sitemap)._ + +## Any custom content here! + +_Feel free to add additional sections for whatever you think should also be mentioned about your binding!_ diff --git a/bundles/org.openhab.binding.siemenshvac/build.properties b/bundles/org.openhab.binding.siemenshvac/build.properties deleted file mode 100644 index cbd2b7cb6a4cd..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/build.properties +++ /dev/null @@ -1,7 +0,0 @@ -source.. = src/main/java/ -output.. = target/classes -bin.includes = META-INF/,\ - lib/json-simple-1.1.1.jar,\ - .,\ - OSGI-INF/,\ - ESH-INF/ \ No newline at end of file diff --git a/bundles/org.openhab.binding.siemenshvac/lib/json-simple-1.1.1.jar b/bundles/org.openhab.binding.siemenshvac/lib/json-simple-1.1.1.jar deleted file mode 100644 index 66347a6c86b7d6442358ca7643e4dc484fb01866..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23737 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 A{Qv*} diff --git a/bundles/org.openhab.binding.siemenshvac/pom.xml b/bundles/org.openhab.binding.siemenshvac/pom.xml index f53f6cd5f3202..84c8fa94a1d9b 100644 --- a/bundles/org.openhab.binding.siemenshvac/pom.xml +++ b/bundles/org.openhab.binding.siemenshvac/pom.xml @@ -1,19 +1,16 @@ - + 4.0.0 - org.openhab.binding - pom - 2.0.0-SNAPSHOT + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 3.1.0-SNAPSHOT - org.openhab.binding org.openhab.binding.siemenshvac - 2.0.0-SNAPSHOT - SiemensHvac Binding - eclipse-plugin + openHAB Add-ons :: Bundles :: SiemensHvac Binding diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/feature/feature.xml b/bundles/org.openhab.binding.siemenshvac/src/main/feature/feature.xml new file mode 100644 index 0000000000000..47cf3e8717670 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/feature/feature.xml @@ -0,0 +1,25 @@ + + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features + + + openhab-runtime-base + openhab-transport-upnp + esh-io-transport-upnp + mvn:org.openhab.addons.bundles/org.openhab.binding.siemenshvac/${project.version} + + diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/SiemensHvacBindingConstants.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/SiemensHvacBindingConstants.java deleted file mode 100644 index e14ca9c942b09..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/SiemensHvacBindingConstants.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (c) 2014-2015 openHAB UG (haftungsbeschraenkt) and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.openhab.binding.siemenshvac; - -import java.util.Set; - -import org.eclipse.smarthome.core.thing.ThingTypeUID; - -import com.google.common.collect.ImmutableSet; - -/** - * The {@link SiemensHvacBinding} class defines common constants, which are - * used across the whole binding. - * - * @author Laurent ARNAL - Initial contribution - */ -public class SiemensHvacBindingConstants { - - public static final String BINDING_ID = "siemenshvac"; - - public final static String TEMPERATURE = "temperature"; - - public final static Set SUPPORTED_DEVICE_MODELS = ImmutableSet.of("Web Server OZW672.01"); - - // List of thing parameters names - public final static String PROTOCOL_PARAMETER = "protocol"; - public final static String HOST_PARAMETER = "address"; - public final static String TCP_PORT_PARAMETER = "tcpPort"; - public final static String USER_PARAMETER = "userName"; - public final static String PASSWORD_PARAMETER = "userPassword"; - - public final static String IP_PROTOCOL_NAME = "IP"; - - public final static ThingTypeUID HVAC_THING_TYPE = new ThingTypeUID(BINDING_ID, "hvacBridge"); - public final static ThingTypeUID TEST_THING_TYPE = new ThingTypeUID(BINDING_ID, "hvacBridge"); - public final static ThingTypeUID HVAC_UNSUPPORTED_THING_TYPE = new ThingTypeUID(BINDING_ID, - "hvacBridgeUnsupported"); - - // Used for Discovery service - public final static String MANUFACTURER = "Siemens Switzerland Ltd."; - public final static String UPNP_DEVICE_TYPE = "Basic"; - - public final static Set SUPPORTED_THING_TYPES_UIDS = ImmutableSet.of(HVAC_THING_TYPE, - HVAC_UNSUPPORTED_THING_TYPE); - -} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/handler/SiemensHvacHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/handler/SiemensHvacHandler.java deleted file mode 100644 index a3b32778add58..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/handler/SiemensHvacHandler.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (c) 2014-2015 openHAB UG (haftungsbeschraenkt) and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.openhab.binding.siemenshvac.handler; - -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.smarthome.core.thing.Channel; -import org.eclipse.smarthome.core.thing.ChannelUID; -import org.eclipse.smarthome.core.thing.Thing; -import org.eclipse.smarthome.core.thing.ThingStatus; -import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; -import org.eclipse.smarthome.core.thing.binding.builder.ChannelBuilder; -import org.eclipse.smarthome.core.thing.binding.builder.ThingBuilder; -import org.eclipse.smarthome.core.types.Command; -import org.openhab.binding.siemenshvac.SiemensHvacBindingConstants; -import org.openhab.binding.siemenshvac.internal.siemensHvacConnector; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The {@link SiemensHvacHandler} is responsible for handling commands, which are - * sent to one of the channels. - * - * @author Laurent ARNAL - Initial contribution - */ -public class SiemensHvacHandler extends BaseThingHandler { - - private Logger logger = LoggerFactory.getLogger(SiemensHvacHandler.class); - private siemensHvacConnector hvacConnector; - - public SiemensHvacHandler(Thing thing) { - super(thing); - } - - @Override - public void handleCommand(ChannelUID channelUID, Command command) { - if (channelUID.getId().equals(SiemensHvacBindingConstants.TEMPERATURE)) { - - } - } - - @Override - public void initialize() { - List channels = new ArrayList<>(1); - - ThingBuilder thingBuilder = ThingBuilder.create(thing.getThingTypeUID(), thing.getUID()) - .withBridge(thing.getBridgeUID()).withChannels(channels).withConfiguration(thing.getConfiguration()); - - ChannelUID channelUID = new ChannelUID(thing.getUID(), "0"); - Channel channel = ChannelBuilder.create(channelUID, "switch").build(); - thingBuilder.withChannel(channel); - - // ChannelUID channelUID2 = new ChannelUID(thing.getUID(), "1"); - // Channel channel2 = ChannelBuilder.create(channelUID2, "switch").build(); - // thingBuilder.withChannel(channel2); - - updateThing(thingBuilder.build()); - - updateStatus(ThingStatus.ONLINE); - hvacConnector = createConnection(); - } - - protected siemensHvacConnector createConnection() { - String host = (String) this.getConfig().get(SiemensHvacBindingConstants.HOST_PARAMETER); - String user = (String) this.getConfig().get(SiemensHvacBindingConstants.USER_PARAMETER); - String password = (String) this.getConfig().get(SiemensHvacBindingConstants.PASSWORD_PARAMETER); - - return new siemensHvacConnector(host, user, password); - } - -} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/JSONResponseHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/JSONResponseHandler.java deleted file mode 100644 index 7c5366302d9f1..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/JSONResponseHandler.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright (c) 2010-2015, openHAB.org and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.openhab.binding.siemenshvac.internal; - - -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; -import org.json.simple.parser.ParseException; -import org.openhab.binding.siemenshvac.internal.constants.JSONApiResponseKeysEnum; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author Alexander Betker - * @author Alex Maier - * @since 1.3.0 - */ -public class JSONResponseHandler { - - private static final Logger logger = LoggerFactory.getLogger(JSONResponseHandler.class); - - public JSONResponseHandler() { - - } - - public boolean checkResponse(JSONObject jsonResponse) { - if(jsonResponse == null) - return false; - else if (jsonResponse.get(JSONApiResponseKeysEnum.RESPONSE_OK.getKey()) != null) { - return jsonResponse.get(JSONApiResponseKeysEnum.RESPONSE_OK.getKey()).toString().equals(JSONApiResponseKeysEnum.RESPONSE_SUCCESSFUL.getKey()); - } - else{ - logger.error("error in json request. Error message : "+jsonResponse.get(JSONApiResponseKeysEnum.RESPONSE_MESSAGE).toString()); - } - return false; - } - - public static JSONObject toJSONObject(String jsonResponse) { - if (jsonResponse != null && !jsonResponse.trim().equals("")) { - try { - return (JSONObject)new JSONParser().parse(jsonResponse); - } - catch (ParseException e) { - logger.error(e.getLocalizedMessage()); - } - } - return null; - } - - public JSONObject getResultJSONObject(JSONObject jsonObject) { - if (jsonObject != null) { - return (JSONObject) jsonObject.get(JSONApiResponseKeysEnum.RESULT.getKey()); - } - return null; - } - -} \ No newline at end of file diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java new file mode 100644 index 0000000000000..caf478b14a218 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java @@ -0,0 +1,453 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Copied from + * https://raw.githubusercontent.com/google/gson/master/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java + * and repackaged here with additional content from + * com.google.gson.internal.{Streams,TypeAdapters,LazilyParsedNumber} + * to avoid using the internal package. + */ +package org.openhab.binding.siemenshvac.internal.Metadata; + +import java.io.EOFException; +import java.io.IOException; +import java.io.ObjectStreamException; +import java.math.BigDecimal; +import java.util.LinkedHashMap; +import java.util.Map; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonIOException; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSyntaxException; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import com.google.gson.stream.MalformedJsonException; + +/** + * Adapts values whose runtime type may differ from their declaration type. This + * is necessary when a field's type is not the same type that GSON should create + * when deserializing that field. For example, consider these types: + * + *
+ * {
+ *     @code
+ *     abstract class Shape {
+ *         int x;
+ *         int y;
+ *     }
+ *     class Circle extends Shape {
+ *         int radius;
+ *     }
+ *     class Rectangle extends Shape {
+ *         int width;
+ *         int height;
+ *     }
+ *     class Diamond extends Shape {
+ *         int width;
+ *         int height;
+ *     }
+ *     class Drawing {
+ *         Shape bottomShape;
+ *         Shape topShape;
+ *     }
+ * }
+ * 
+ *

+ * Without additional type information, the serialized JSON is ambiguous. Is + * the bottom shape in this drawing a rectangle or a diamond? + * + *

+ *    {@code
+ *   {
+ *     "bottomShape": {
+ *       "width": 10,
+ *       "height": 5,
+ *       "x": 0,
+ *       "y": 0
+ *     },
+ *     "topShape": {
+ *       "radius": 2,
+ *       "x": 4,
+ *       "y": 1
+ *     }
+ *   }}
+ * 
+ * + * This class addresses this problem by adding type information to the + * serialized JSON and honoring that type information when the JSON is + * deserialized: + * + *
+ *    {@code
+ *   {
+ *     "bottomShape": {
+ *       "type": "Diamond",
+ *       "width": 10,
+ *       "height": 5,
+ *       "x": 0,
+ *       "y": 0
+ *     },
+ *     "topShape": {
+ *       "type": "Circle",
+ *       "radius": 2,
+ *       "x": 4,
+ *       "y": 1
+ *     }
+ *   }}
+ * 
+ * + * Both the type field name ({@code "type"}) and the type labels ({@code + * "Rectangle"}) are configurable. + * + *

Registering Types

+ * Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field + * name to the {@link #of} factory method. If you don't supply an explicit type + * field name, {@code "type"} will be used. + * + *
+ * {
+ *     @code
+ *     RuntimeTypeAdapterFactory shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class, "type");
+ * }
+ * 
+ * + * Next register all of your subtypes. Every subtype must be explicitly + * registered. This protects your application from injection attacks. If you + * don't supply an explicit type label, the type's simple name will be used. + * + *
+ *    {@code
+ *   shapeAdapter.registerSubtype(Rectangle.class, "Rectangle");
+ *   shapeAdapter.registerSubtype(Circle.class, "Circle");
+ *   shapeAdapter.registerSubtype(Diamond.class, "Diamond");
+ * }
+ * 
+ * + * Finally, register the type adapter factory in your application's GSON builder: + * + *
+ * {
+ *     @code
+ *     Gson gson = new GsonBuilder().registerTypeAdapterFactory(shapeAdapterFactory).create();
+ * }
+ * 
+ * + * Like {@code GsonBuilder}, this API supports chaining: + * + *
+ * {
+ *     @code
+ *     RuntimeTypeAdapterFactory shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
+ *             .registerSubtype(Rectangle.class).registerSubtype(Circle.class).registerSubtype(Diamond.class);
+ * }
+ * 
+ */ +public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory { + private final Class baseType; + private final String typeFieldName; + private final Map> labelToSubtype = new LinkedHashMap>(); + private final Map, String> subtypeToLabel = new LinkedHashMap, String>(); + + private RuntimeTypeAdapterFactory(Class baseType, String typeFieldName) { + if (typeFieldName == null || baseType == null) { + throw new NullPointerException(); + } + this.baseType = baseType; + this.typeFieldName = typeFieldName; + } + + /** + * Creates a new runtime type adapter using for {@code baseType} using {@code + * typeFieldName} as the type field name. Type field names are case sensitive. + */ + public static RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName) { + return new RuntimeTypeAdapterFactory(baseType, typeFieldName); + } + + /** + * Creates a new runtime type adapter for {@code baseType} using {@code "type"} as + * the type field name. + */ + public static RuntimeTypeAdapterFactory of(Class baseType) { + return new RuntimeTypeAdapterFactory(baseType, "type"); + } + + /** + * Registers {@code type} identified by {@code label}. Labels are case + * sensitive. + * + * @throws IllegalArgumentException if either {@code type} or {@code label} + * have already been registered on this type adapter. + */ + public RuntimeTypeAdapterFactory registerSubtype(Class type, String label) { + if (type == null || label == null) { + throw new NullPointerException(); + } + if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) { + throw new IllegalArgumentException("types and labels must be unique"); + } + labelToSubtype.put(label, type); + subtypeToLabel.put(type, label); + return this; + } + + /** + * Registers {@code type} identified by its {@link Class#getSimpleName simple + * name}. Labels are case sensitive. + * + * @throws IllegalArgumentException if either {@code type} or its simple name + * have already been registered on this type adapter. + */ + public RuntimeTypeAdapterFactory registerSubtype(Class type) { + return registerSubtype(type, type.getSimpleName()); + } + + @Override + public TypeAdapter create(Gson gson, TypeToken type) { + if (type.getRawType() != baseType) { + return null; + } + + final Map> labelToDelegate = new LinkedHashMap>(); + final Map, TypeAdapter> subtypeToDelegate = new LinkedHashMap, TypeAdapter>(); + for (Map.Entry> entry : labelToSubtype.entrySet()) { + TypeAdapter delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue())); + labelToDelegate.put(entry.getKey(), delegate); + subtypeToDelegate.put(entry.getValue(), delegate); + } + + return new TypeAdapter() { + @Override + public R read(JsonReader in) throws IOException { + JsonElement jsonElement = RuntimeTypeAdapterFactory.parse(in); + JsonElement labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName); + if (labelJsonElement == null) { + throw new JsonParseException("cannot deserialize " + baseType + + " because it does not define a field named " + typeFieldName); + } + String label = labelJsonElement.getAsString(); + @SuppressWarnings("unchecked") // registration requires that subtype extends T + TypeAdapter delegate = (TypeAdapter) labelToDelegate.get(label); + if (delegate == null) { + throw new JsonParseException("cannot deserialize " + baseType + " subtype named " + label + + "; did you forget to register a subtype?"); + } + return delegate.fromJsonTree(jsonElement); + } + + @Override + public void write(JsonWriter out, R value) throws IOException { + Class srcType = value.getClass(); + String label = subtypeToLabel.get(srcType); + @SuppressWarnings("unchecked") // registration requires that subtype extends T + TypeAdapter delegate = (TypeAdapter) subtypeToDelegate.get(srcType); + if (delegate == null) { + throw new JsonParseException( + "cannot serialize " + srcType.getName() + "; did you forget to register a subtype?"); + } + JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject(); + if (jsonObject.has(typeFieldName)) { + throw new JsonParseException("cannot serialize " + srcType.getName() + + " because it already defines a field named " + typeFieldName); + } + JsonObject clone = new JsonObject(); + clone.add(typeFieldName, new JsonPrimitive(label)); + for (Map.Entry e : jsonObject.entrySet()) { + clone.add(e.getKey(), e.getValue()); + } + RuntimeTypeAdapterFactory.write(clone, out); + } + }.nullSafe(); + } + + /** + * Takes a reader in any state and returns the next value as a JsonElement. + */ + private static JsonElement parse(JsonReader reader) throws JsonParseException { + boolean isEmpty = true; + try { + reader.peek(); + isEmpty = false; + return RuntimeTypeAdapterFactory.JSON_ELEMENT.read(reader); + } catch (EOFException e) { + /* + * For compatibility with JSON 1.5 and earlier, we return a JsonNull for + * empty documents instead of throwing. + */ + if (isEmpty) { + return JsonNull.INSTANCE; + } + // The stream ended prematurely so it is likely a syntax error. + throw new JsonSyntaxException(e); + } catch (MalformedJsonException e) { + throw new JsonSyntaxException(e); + } catch (IOException e) { + throw new JsonIOException(e); + } catch (NumberFormatException e) { + throw new JsonSyntaxException(e); + } + } + + /** + * Writes the JSON element to the writer, recursively. + */ + private static void write(JsonElement element, JsonWriter writer) throws IOException { + RuntimeTypeAdapterFactory.JSON_ELEMENT.write(writer, element); + } + + private static final TypeAdapter JSON_ELEMENT = new TypeAdapter() { + @Override + public JsonElement read(JsonReader in) throws IOException { + switch (in.peek()) { + case STRING: + return new JsonPrimitive(in.nextString()); + case NUMBER: + String number = in.nextString(); + return new JsonPrimitive(new LazilyParsedNumber(number)); + case BOOLEAN: + return new JsonPrimitive(in.nextBoolean()); + case NULL: + in.nextNull(); + return JsonNull.INSTANCE; + case BEGIN_ARRAY: + JsonArray array = new JsonArray(); + in.beginArray(); + while (in.hasNext()) { + array.add(read(in)); + } + in.endArray(); + return array; + case BEGIN_OBJECT: + JsonObject object = new JsonObject(); + in.beginObject(); + while (in.hasNext()) { + object.add(in.nextName(), read(in)); + } + in.endObject(); + return object; + case END_DOCUMENT: + case NAME: + case END_OBJECT: + case END_ARRAY: + default: + throw new IllegalArgumentException(); + } + } + + @Override + public void write(JsonWriter out, JsonElement value) throws IOException { + if (value == null || value.isJsonNull()) { + out.nullValue(); + } else if (value.isJsonPrimitive()) { + JsonPrimitive primitive = value.getAsJsonPrimitive(); + if (primitive.isNumber()) { + out.value(primitive.getAsNumber()); + } else if (primitive.isBoolean()) { + out.value(primitive.getAsBoolean()); + } else { + out.value(primitive.getAsString()); + } + + } else if (value.isJsonArray()) { + out.beginArray(); + for (JsonElement e : value.getAsJsonArray()) { + write(out, e); + } + out.endArray(); + + } else if (value.isJsonObject()) { + out.beginObject(); + for (Map.Entry e : value.getAsJsonObject().entrySet()) { + out.name(e.getKey()); + write(out, e.getValue()); + } + out.endObject(); + + } else { + throw new IllegalArgumentException("Couldn't write " + value.getClass()); + } + } + }; + + /** + * This class holds a number value that is lazily converted to a specific number type + * + * @author Inderjeet Singh + */ + public static final class LazilyParsedNumber extends Number { + private final String value; + + public LazilyParsedNumber(String value) { + this.value = value; + } + + @Override + public int intValue() { + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + try { + return (int) Long.parseLong(value); + } catch (NumberFormatException nfe) { + return new BigDecimal(value).intValue(); + } + } + } + + @Override + public long longValue() { + try { + return Long.parseLong(value); + } catch (NumberFormatException e) { + return new BigDecimal(value).longValue(); + } + } + + @Override + public float floatValue() { + return Float.parseFloat(value); + } + + @Override + public double doubleValue() { + return Double.parseDouble(value); + } + + @Override + public String toString() { + return value; + } + + /** + * If somebody is unlucky enough to have to serialize one of these, serialize + * it as a BigDecimal so that they won't need Gson on the other side to + * deserialize it. + */ + private Object writeReplace() throws ObjectStreamException { + return new BigDecimal(value); + } + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java new file mode 100644 index 0000000000000..544460e1b9d78 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java @@ -0,0 +1,70 @@ +package org.openhab.binding.siemenshvac.internal.Metadata; + +public class SiemensHvacMetadata { + private int Id = -1; + private int menuId = -1; + private int groupId = -1; + private int catId = -1; + private String shortDesc = null; + private String longDesc = null; + private transient SiemensHvacMetadata parent; + + public SiemensHvacMetadata() { + } + + public int getId() { + return Id; + } + + public void setId(int Id) { + this.Id = Id; + } + + public int getMenuId() { + return menuId; + } + + public void setMenuId(int menuId) { + this.menuId = menuId; + } + + public int getGroupId() { + return groupId; + } + + public void setGroupId(int groupId) { + this.groupId = groupId; + } + + public int getCatId() { + return catId; + } + + public void setCatId(int catId) { + this.catId = catId; + } + + public String getShortDesc() { + return shortDesc; + } + + public void setShortDesc(String shortDesc) { + this.shortDesc = shortDesc; + } + + public String getLongDesc() { + return longDesc; + } + + public void setLongDesc(String longDesc) { + this.longDesc = longDesc; + } + + public SiemensHvacMetadata getParent() { + return parent; + } + + public void setParent(SiemensHvacMetadata parent) { + this.parent = parent; + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java new file mode 100644 index 0000000000000..292278dcb2a35 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java @@ -0,0 +1,214 @@ +package org.openhab.binding.siemenshvac.internal.Metadata; + +import java.util.ArrayList; +import java.util.List; + +import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +public class SiemensHvacMetadataDataPoint extends SiemensHvacMetadata { + private int dptId = -1; + private String dptType = null; + private String dptUnit = null; + private String min = null; + private String max = null; + private String resolution = null; + private String fieldWitdh = null; + private String decimalDigits = null; + private Boolean detailsResolved = false; + private String dialogType = null; + private String maxLength = null; + private String address = null; + private int dptSubKey = -1; + private boolean writeAccess = false; + private List child = null; + + public String getDptType() { + return dptType; + } + + public void setDptType(String dptType) { + this.dptType = dptType; + } + + public List getChild() { + return child; + } + + public void setChild(List child) { + this.child = child; + } + + public int getDptId() { + return dptId; + } + + public void setDptId(int dptId) { + this.dptId = dptId; + } + + public int getDptSubKey() { + return dptSubKey; + } + + public void setDptSubKey(int dptSubKey) { + this.dptSubKey = dptSubKey; + } + + public String getAddress() { + return address; + } + + public void setWriteAccess(boolean writeAccess) { + this.writeAccess = writeAccess; + } + + public boolean getWriteAccess() { + return writeAccess; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getDptUnit() { + return dptUnit; + } + + public void setDptUnit(String dptUnit) { + this.dptUnit = dptUnit; + } + + public String getMaxLength() { + return maxLength; + } + + public void setMaxLength(String maxLength) { + this.maxLength = maxLength; + } + + public String getDialogType() { + return dialogType; + } + + public void setDialogType(String dialogType) { + this.dialogType = dialogType; + } + + public String getMin() { + return min; + } + + public void setMin(String min) { + this.min = min; + } + + public String getMax() { + return max; + } + + public void setMax(String max) { + this.max = max; + } + + public String getResolution() { + return resolution; + } + + public void setResolution(String resolution) { + this.resolution = resolution; + } + + public String getFieldWitdh() { + return fieldWitdh; + } + + public void setFieldWitdh(String fieldWitdh) { + this.fieldWitdh = fieldWitdh; + } + + public String getDecimalDigits() { + return decimalDigits; + } + + public void setDecimalDigits(String decimalDigits) { + this.decimalDigits = decimalDigits; + } + + public Boolean getDetailsResolved() { + return detailsResolved; + } + + public void setDetailsResolved(Boolean detailsResolved) { + this.detailsResolved = detailsResolved; + } + + public void resolveDptDetails(JsonObject result) { + if (result == null) { + return; + } + + JsonObject desc = result.getAsJsonObject("Description"); + + if (desc != null) { + this.dptType = desc.get("Type").getAsString(); + + if (dptType.equals(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { + + JsonArray enums = desc.getAsJsonArray("Enums"); + child = new ArrayList(); + + for (Object obj : enums) { + JsonObject entry = (JsonObject) obj; + + SiemensHvacMetadataPointChild ch = new SiemensHvacMetadataPointChild(); + ch.setText(entry.get("Text").getAsString()); + ch.setValue(entry.get("Value").getAsString()); + ch.setIsActive(entry.get("IsCurrentValue").getAsString()); + child.add(ch); + } + } else if (dptType.equals(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { + this.dptUnit = desc.get("Unit").getAsString(); + this.min = desc.get("Min").getAsString(); + this.max = desc.get("Max").getAsString(); + this.resolution = desc.get("Resolution").getAsString(); + this.fieldWitdh = desc.get("FieldWitdh").getAsString(); + this.decimalDigits = desc.get("DecimalDigits").getAsString(); + } else if (dptType.equals(SiemensHvacBindingConstants.DPT_TYPE_STRING)) { + this.dialogType = desc.get("DialogType").getAsString(); + this.maxLength = desc.get("MaxLength").getAsString(); + } else if (dptType.equals(SiemensHvacBindingConstants.DPT_TYPE_RADIO)) { + JsonArray buttons = desc.getAsJsonArray("Buttons"); + + child = new ArrayList(); + + for (Object obj : buttons) { + JsonObject button = (JsonObject) obj; + + SiemensHvacMetadataPointChild ch = new SiemensHvacMetadataPointChild(); + ch.setOpt0(button.get("TextOpt0").getAsString()); + ch.setOpt1(button.get("TextOpt1").getAsString()); + ch.setIsActive(button.get("IsActive").getAsString()); + child.add(ch); + + String signifiance = button.get("Significance").getAsString(); + } + } else if (dptType.equals(SiemensHvacBindingConstants.DPT_TYPE_DATE)) { + System.out.println(""); + } else if (dptType.equals(SiemensHvacBindingConstants.DPT_TYPE_TIME)) { + System.out.println(""); + } else if (dptType.equals(SiemensHvacBindingConstants.DPT_TYPE_SCHEDULER)) { + System.out.println(""); + } else if (dptType.equals(SiemensHvacBindingConstants.DPT_TYPE_CALENDAR)) { + System.out.println(""); + } else { + System.out.println(""); + } + detailsResolved = true; + } + + } + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java new file mode 100644 index 0000000000000..ad686f96d0209 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java @@ -0,0 +1,77 @@ +package org.openhab.binding.siemenshvac.internal.Metadata; + +public class SiemensHvacMetadataDevice { + private String name; + private String addr; + private String type; + private String serialNr; + private String treeDate; + private String treeTime; + private boolean treeGenerated; + private int treeId; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAddr() { + return addr; + } + + public void setAddr(String addr) { + this.addr = addr; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getSerialNr() { + return serialNr; + } + + public void setSerialNr(String serialNr) { + this.serialNr = serialNr; + } + + public String getTreeDate() { + return treeDate; + } + + public void setTreeDate(String treeDate) { + this.treeDate = treeDate; + } + + public String getTreeTime() { + return treeTime; + } + + public void setTreeTime(String treeTime) { + this.treeTime = treeTime; + } + + public boolean getTreeGenerated() { + return treeGenerated; + } + + public void setTreeGenerated(boolean treeGenerated) { + this.treeGenerated = treeGenerated; + } + + public int getTreeId() { + return treeId; + } + + public void setTreeId(int treeId) { + this.treeId = treeId; + } + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java new file mode 100644 index 0000000000000..e2ee906bcb40b --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java @@ -0,0 +1,21 @@ +package org.openhab.binding.siemenshvac.internal.Metadata; + +import java.util.HashMap; +import java.util.LinkedHashMap; + +public class SiemensHvacMetadataMenu extends SiemensHvacMetadata { + private LinkedHashMap childList; + + public SiemensHvacMetadataMenu() { + childList = new LinkedHashMap(); + } + + public void AddChild(SiemensHvacMetadata information) { + childList.put(information.getLongDesc(), information); + } + + public HashMap getChilds() { + return this.childList; + } + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java new file mode 100644 index 0000000000000..76a5f469bf60c --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java @@ -0,0 +1,50 @@ +package org.openhab.binding.siemenshvac.internal.Metadata; + +public class SiemensHvacMetadataPointChild { + + private String text; + private String value; + private String opt0; + private String opt1; + private String isActive; + + public String getText() { + return this.text; + } + + public void setText(String text) { + this.text = text; + } + + public String getValue() { + return this.value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getOpt0() { + return this.opt0; + } + + public void setOpt0(String opt0) { + this.opt0 = opt0; + } + + public String getOpt1() { + return this.opt1; + } + + public void setOpt1(String opt1) { + this.opt1 = opt1; + } + + public String getIsActive() { + return this.isActive; + } + + public void setIsActive(String isActive) { + this.isActive = isActive; + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java new file mode 100644 index 0000000000000..5c729121422d6 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2010-2020 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.siemenshvac.internal.Metadata; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; +import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelTypeProvider; + +/** + * + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault +public interface SiemensHvacMetadataRegistry { + + /** + * Initializes the type generator. + */ + void initialize(); + + void ReadMeta(); + + @Nullable + SiemensHvacMetadataMenu getRoot(); + + @Nullable + SiemensHvacMetadata getDptMap(String key); + + @Nullable + SiemensHvacChannelTypeProvider getChannelTypeProvider(); + + @Nullable + SiemensHvacConnector getSiemensHvacConnector(); + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java new file mode 100644 index 0000000000000..f59ec70c41a78 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -0,0 +1,869 @@ +package org.openhab.binding.siemenshvac.internal.Metadata; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigDecimal; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + +import org.apache.commons.io.IOUtils; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; +import org.openhab.binding.siemenshvac.internal.network.SiemensHvacCallback; +import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; +import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnectorImpl; +import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelGroupTypeProvider; +import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelTypeProvider; +import org.openhab.binding.siemenshvac.internal.type.SiemensHvacConfigDescriptionProvider; +import org.openhab.binding.siemenshvac.internal.type.SiemensHvacThingTypeProvider; +import org.openhab.binding.siemenshvac.internal.type.UidUtils; +import org.openhab.core.config.core.ConfigDescriptionBuilder; +import org.openhab.core.config.core.ConfigDescriptionParameter; +import org.openhab.core.config.core.ConfigDescriptionParameterGroup; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.type.ChannelDefinition; +import org.openhab.core.thing.type.ChannelDefinitionBuilder; +import org.openhab.core.thing.type.ChannelGroupDefinition; +import org.openhab.core.thing.type.ChannelGroupType; +import org.openhab.core.thing.type.ChannelGroupTypeBuilder; +import org.openhab.core.thing.type.ChannelGroupTypeUID; +import org.openhab.core.thing.type.ChannelType; +import org.openhab.core.thing.type.ChannelTypeBuilder; +import org.openhab.core.thing.type.ChannelTypeUID; +import org.openhab.core.thing.type.ThingType; +import org.openhab.core.thing.type.ThingTypeBuilder; +import org.openhab.core.types.StateDescriptionFragmentBuilder; +import org.openhab.core.types.StateOption; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +@Component(immediate = true) +@NonNullByDefault +public class SiemensHvacMetadataRegistryImpl implements SiemensHvacMetadataRegistry { + + private static final Logger logger = LoggerFactory.getLogger(SiemensHvacMetadataRegistryImpl.class); + + // private Map thingTypesByUID = new HashMap<>(); + // protected List homematicThingTypeExcluders = new CopyOnWriteArrayList<>(); + + // A map contains data point config read from Api and/or WebPages + private @Nullable Map dptMap = null; + private @Nullable SiemensHvacMetadata root = null; + private @Nullable ArrayList devices = null; + + private boolean interrupted = false; + + private static final String CONFIG_DIR = "." + File.separator + "configurations"; + private static final String CONTENT_DIR = CONFIG_DIR + File.separator + "services"; + + private @Nullable static URI configDescriptionUriChannel; + + private @Nullable SiemensHvacThingTypeProvider thingTypeProvider; + private @Nullable SiemensHvacChannelTypeProvider channelTypeProvider; + private @Nullable SiemensHvacChannelGroupTypeProvider channelGroupTypeProvider; + private @Nullable SiemensHvacConfigDescriptionProvider configDescriptionProvider; + private @Nullable SiemensHvacConnector hvacConnector; + + public SiemensHvacMetadataRegistryImpl() { + } + + @Reference + protected void setSiemensHvacConnector(SiemensHvacConnector hvacConnector) { + this.hvacConnector = hvacConnector; + } + + protected void unsetSiemensHvacConnector(SiemensHvacConnector hvacConnector) { + this.hvacConnector = null; + } + + @Reference + protected void setThingTypeProvider(SiemensHvacThingTypeProvider thingTypeProvider) { + this.thingTypeProvider = thingTypeProvider; + } + + protected void unsetThingTypeProvider(SiemensHvacThingTypeProvider thingTypeProvider) { + this.thingTypeProvider = null; + } + + @Reference + protected void setChannelTypeProvider(SiemensHvacChannelTypeProvider channelTypeProvider) { + this.channelTypeProvider = channelTypeProvider; + } + + protected void unsetChannelTypeProvider(SiemensHvacChannelTypeProvider channelTypeProvider) { + this.channelTypeProvider = null; + } + + // + @Reference + protected void setChannelGroupTypeProvider(SiemensHvacChannelGroupTypeProvider channelGroupTypeProvider) { + this.channelGroupTypeProvider = channelGroupTypeProvider; + } + + protected void unsetChannelGroupTypeProvider(SiemensHvacChannelGroupTypeProvider channelGroupTypeProvider) { + this.channelGroupTypeProvider = null; + } + + @Reference + protected void setConfigDescriptionProvider(SiemensHvacConfigDescriptionProvider configDescriptionProvider) { + this.configDescriptionProvider = configDescriptionProvider; + } + + protected void unsetConfigDescriptionProvider(SiemensHvacConfigDescriptionProvider configDescriptionProvider) { + this.configDescriptionProvider = null; + } + + @Override + public @Nullable SiemensHvacConnector getSiemensHvacConnector() { + return this.hvacConnector; + } + + @Override + public @Nullable SiemensHvacChannelTypeProvider getChannelTypeProvider() { + return this.channelTypeProvider; + } + + /** + * Initializes the type generator. + */ + @Override + @Activate + public void initialize() { + } + + public void InitDptMap(@Nullable SiemensHvacMetadata node) { + + if (node.getClass() == SiemensHvacMetadataMenu.class) { + SiemensHvacMetadataMenu mInformation = (SiemensHvacMetadataMenu) node; + + for (SiemensHvacMetadata child : mInformation.getChilds().values()) { + InitDptMap(child); + } + } + + if (node != null) { + if (node.getLongDesc() != null) { + dptMap.put("byName" + node.getLongDesc(), node); + } + if (node.getShortDesc() != null) { + dptMap.put("byName" + node.getShortDesc(), node); + } + } + + dptMap.put("byId" + node.getId(), node); + dptMap.put("byMenu" + node.getMenuId(), node); + if (node.getClass() == SiemensHvacMetadataDataPoint.class) { + SiemensHvacMetadataDataPoint dpi = (SiemensHvacMetadataDataPoint) node; + dptMap.put("byDptId" + dpi.getDptId(), node); + } + } + + @Override + public @Nullable SiemensHvacMetadataMenu getRoot() { + return (SiemensHvacMetadataMenu) root; + } + + @Override + public void ReadMeta() { + root = null; + + if (root == null) { + logger.debug("siemensHvac:InitDptMap():begin"); + + LoadMetaDataFromCache(); + + ReadDeviceList(); + + if (root == null) { + root = new SiemensHvacMetadataMenu(); + ReadMetaData(root, -1); + hvacConnector.WaitAllPendingRequest(); + } + + dptMap = new Hashtable(); + InitDptMap(root); + SaveMetaDataToCache(); + logger.debug("siemensHvac:InitDptMap():end"); + } + + SiemensHvacMetadataMenu rootMenu = getRoot(); + for (SiemensHvacMetadataDevice device : devices) { + if (device.getType().indexOf("OZW672") >= 0) { + continue; + } + + generateThingsType(device); + } + + logger.info("end configuration"); + } + + private void generateThingsType(SiemensHvacMetadataDevice device) { + if (thingTypeProvider != null) { + ThingTypeUID thingTypeUID = UidUtils.generateThingTypeUID(device); + ThingType tt = thingTypeProvider.getInternalThingType(thingTypeUID); + + if (tt == null) { + + List groupTypes = new ArrayList<>(); + + int treeId = device.getTreeId(); + if (dptMap.containsKey("byMenu" + treeId)) { + SiemensHvacMetadataMenu menu = (SiemensHvacMetadataMenu) dptMap.get("byMenu" + treeId); + + for (SiemensHvacMetadata child : menu.getChilds().values()) { + + if (child instanceof SiemensHvacMetadataMenu) { + SiemensHvacMetadataMenu subMenu = (SiemensHvacMetadataMenu) child; + + List channelDefinitions = new ArrayList<>(); + + for (SiemensHvacMetadata childDt : subMenu.getChilds().values()) { + + if (childDt instanceof SiemensHvacMetadataDataPoint) { + SiemensHvacMetadataDataPoint dataPoint = (SiemensHvacMetadataDataPoint) childDt; + + ChannelTypeUID channelTypeUID = UidUtils.generateChannelTypeUID(dataPoint); + + ChannelType channelType = channelTypeProvider + .getInternalChannelType(channelTypeUID); + if (channelType == null) { + channelType = createChannelType(dataPoint, channelTypeUID); + channelTypeProvider.addChannelType(channelType); + } + + SiemensHvacMetadataDataPoint dpt = ((SiemensHvacMetadataDataPoint) childDt); + + Map props = new Hashtable(); + props.put("dptId", "" + dpt.getDptId()); + props.put("groupdId", "" + subMenu.getMenuId()); + + String id = dataPoint.getDptId() + "_" + + UidUtils.sanetizeId(dataPoint.getShortDesc()); + ChannelDefinition channelDef = new ChannelDefinitionBuilder(id, + channelType.getUID()).withLabel(dataPoint.getShortDesc()) + .withDescription(dataPoint.getLongDesc()).withProperties(props) + .build(); + + channelDefinitions.add(channelDef); + } + } + + // generate group + ChannelGroupTypeUID groupTypeUID = UidUtils.generateChannelGroupTypeUID(subMenu); + ChannelGroupType groupType = channelGroupTypeProvider + .getInternalChannelGroupType(groupTypeUID); + + if (groupType == null) { + String groupLabel = subMenu.getShortDesc(); + groupType = ChannelGroupTypeBuilder.instance(groupTypeUID, groupLabel) + .withChannelDefinitions(channelDefinitions).withCategory("") + .withDescription(menu.getLongDesc()).build(); + channelGroupTypeProvider.addChannelGroupType(groupType); + groupTypes.add(groupType); + } + + } + } + + } + + tt = createThingType(device, groupTypes); + thingTypeProvider.addThingType(tt); + } + } + } + + private ChannelType createChannelType(SiemensHvacMetadataDataPoint dpt, ChannelTypeUID channelTypeUID) { + ChannelType channelType; + + String itemType = getItemType(dpt); + String category = getCategory(dpt); + String label = dpt.getShortDesc(); + String description = dpt.getLongDesc(); + + StateDescriptionFragmentBuilder stateFragment = StateDescriptionFragmentBuilder.create(); + + List options = new ArrayList(); + if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { + for (SiemensHvacMetadataPointChild opt : dpt.getChild()) { + StateOption stOpt = new StateOption(opt.getValue(), opt.getText()); + options.add(stOpt); + } + } + + if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { + BigDecimal min = new BigDecimal(dpt.getMin()); + BigDecimal max = new BigDecimal(dpt.getMax()); + BigDecimal step = new BigDecimal(dpt.getResolution()); + + stateFragment.withMinimum(min).withMaximum(max).withStep(step).withReadOnly(false); + } else { + stateFragment.withPattern(getStatePattern(dpt)).withReadOnly(dpt.getWriteAccess() == false); + } + + if (options != null && !options.isEmpty()) { + stateFragment.withOptions(options); + } + + ChannelTypeBuilder channelTypeBuilder = ChannelTypeBuilder.state(channelTypeUID, label, itemType) + .withStateDescriptionFragment(stateFragment.build()); + + channelType = channelTypeBuilder.isAdvanced(false).withDescription(description).withCategory(category).build(); + + return channelType; + + } + + /** + * Creates the ThingType for the given device. + */ + private ThingType createThingType(SiemensHvacMetadataDevice device, List groupTypes) { + String name = device.getName(); + String description = device.getName(); + + List supportedBridgeTypeUids = new ArrayList<>(); + supportedBridgeTypeUids.add(SiemensHvacBindingConstants.THING_TYPE_OZW672.toString()); + ThingTypeUID thingTypeUID = UidUtils.generateThingTypeUID(device); + + Map properties = new HashMap<>(); + properties.put(Thing.PROPERTY_VENDOR, SiemensHvacBindingConstants.PROPERTY_VENDOR_NAME); + properties.put(Thing.PROPERTY_MODEL_ID, device.getType()); + + URI configDescriptionURI = getConfigDescriptionURI(device); + if (configDescriptionProvider.getInternalConfigDescription(configDescriptionURI) == null) { + generateConfigDescription(device, groupTypes, configDescriptionURI); + } + + List groupDefinitions = new ArrayList<>(); + for (ChannelGroupType groupType : groupTypes) { + String id = groupType.getUID().getId(); + groupDefinitions.add(new ChannelGroupDefinition(id, groupType.getUID())); + } + + return ThingTypeBuilder.instance(thingTypeUID, name).withSupportedBridgeTypeUIDs(supportedBridgeTypeUids) + .withDescription(description).withChannelGroupDefinitions(groupDefinitions).withProperties(properties) + .withRepresentationProperty(Thing.PROPERTY_SERIAL_NUMBER).withConfigDescriptionURI(configDescriptionURI) + .withCategory(SiemensHvacBindingConstants.CATEGORY_THING_HVAC).build(); + } + + private URI getConfigDescriptionURI(SiemensHvacMetadataDevice device) { + try { + return new URI(String.format("%s:%s", SiemensHvacBindingConstants.CONFIG_DESCRIPTION_URI_THING_PREFIX, + UidUtils.generateThingTypeUID(device))); + } catch (URISyntaxException ex) { + logger.warn("Can't create configDescriptionURI for device type {}", device.getName()); + throw new RuntimeException("can't construct URI"); + } + } + + private void generateConfigDescription(SiemensHvacMetadataDevice device, List groupTypes, + URI configDescriptionURI) { + List parms = new ArrayList<>(); + List groups = new ArrayList<>(); + + /* + * for (ChannelGroupType groupTp : groupTypes) { + * String groupName = groupTp.getLabel(); + * String groupLabel = groupTp.getLabel(); + * List channelDefinitions = groupTp.getChannelDefinitions(); + * groups.add(ConfigDescriptionParameterGroupBuilder.create(groupName).withLabel(groupLabel).build()); + * + * for (ChannelDefinition chanDef : channelDefinitions) { + * + * try { + * ConfigDescriptionParameterBuilder builder = ConfigDescriptionParameterBuilder + * .create(chanDef.getLabel(), Type.INTEGER); + * + * ChannelTypeUID channelTypeUID = chanDef.getChannelTypeUID(); + * ChannelType channelType = channelTypeProvider.getInternalChannelType(channelTypeUID); + * + * String chanId = chanDef.getId(); + * builder.withLabel(chanDef.getLabel()); + * + * builder.withDefault("0"); + * builder.withDescription(chanDef.getDescription()); + * + * builder.withMinimum(channelType.getState().getMinimum()); + * builder.withMaximum(channelType.getState().getMaximum()); + * builder.withStepSize(channelType.getState().getStep()); + * // builder.withUnitLabel(channelType.getState().getMaximum()); + * + * builder.withGroupName(groupName); + * parms.add(builder.build()); + * + * Map chanProps = chanDef.getProperties(); + * } catch (Exception ex) { + * logger.debug("p1"); + * } + * } + * + * } + */ + + configDescriptionProvider.addConfigDescription(ConfigDescriptionBuilder.create(configDescriptionURI) + .withParameters(parms).withParameterGroups(groups).build()); + } + + public static String getItemType(SiemensHvacMetadataDataPoint dpt) { + if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_STRING)) { + return SiemensHvacBindingConstants.ITEM_TYPE_STRING; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { + return SiemensHvacBindingConstants.ITEM_TYPE_NUMBER; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { + return SiemensHvacBindingConstants.ITEM_TYPE_NUMBER; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_DATE)) { + return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_TIME)) { + return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_RADIO)) { + return SiemensHvacBindingConstants.ITEM_TYPE_CONTACT; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_SCHEDULER)) { + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_CALENDAR)) { + } else { + logger.debug("unknow type in getItemType()"); + + } + + return ""; + + } + + /** + * Determines the category for the given Datapoint. + */ + public static String getCategory(SiemensHvacMetadataDataPoint dp) { + String dpDialog = dp.getDialogType(); + int dpCatId = dp.getCatId(); + String dpType = dp.getDptType(); + + // String channelType = StringUtils.defaultString(dp.getChannel().getType()); + + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_CONTROL_HEATING; + } + + /** + * Returns the state pattern metadata string with unit for the given Datapoint. + */ + public static String getStatePattern(SiemensHvacMetadataDataPoint dpt) { + String unit = dpt.getDptUnit(); + + if ("%".equals(unit)) { + return "%d %%"; + } + + if (unit != null && unit != "") { + if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { + return String.format("%s %s", "%d", "%unit%"); + } + } + + return ""; + } + + public void ReadDeviceList() { + try { + devices = new ArrayList(); + String request = "api/devicelist/list.json?"; + + JsonObject response = hvacConnector.DoRequest(request, null); + JsonArray devicesList = response.getAsJsonArray("Devices"); + + for (JsonElement device : devicesList) { + + JsonObject obj = (JsonObject) device; + String Name = ""; + String Addr = ""; + String Type = ""; + String SerialNr = ""; + String TreeDate = ""; + String TreeTime = ""; + boolean TreeGenerated = false; + + if (obj.has("Name")) { + Name = obj.get("Name").getAsString(); + } + + if (obj.has("Addr")) { + Addr = obj.get("Addr").getAsString(); + } + + if (obj.has("Type")) { + Type = obj.get("Type").getAsString(); + } + + if (obj.has("SerialNr")) { + SerialNr = obj.get("SerialNr").getAsString(); + } + + if (obj.has("TreeDate")) { + TreeDate = obj.get("TreeDate").getAsString(); + } + + if (obj.has("TreeTime")) { + TreeTime = obj.get("TreeTime").getAsString(); + } + + if (obj.has("TreeGenerated")) { + TreeGenerated = obj.get("TreeGenerated").getAsBoolean(); + } + + SiemensHvacMetadataDevice deviceObj = new SiemensHvacMetadataDevice(); + deviceObj.setName(Name); + deviceObj.setAddr(Addr); + deviceObj.setSerialNr(SerialNr); + deviceObj.setType(Type); + deviceObj.setTreeDate(TreeDate); + deviceObj.setTreeTime(TreeTime); + deviceObj.setTreeGenerated(TreeGenerated); + + String request2 = "api/menutree/device_root.json?TreeName=Web&SerialNumber=" + SerialNr; + JsonObject response2 = hvacConnector.DoRequest(request2, null); + + if (response2.has("TreeItem")) { + JsonObject tree = response2.getAsJsonObject("TreeItem"); + if (tree.has("Id")) { + int treeId = tree.get("Id").getAsInt(); + deviceObj.setTreeId(treeId); + } + } + + devices.add(deviceObj); + } + + } catch (Exception e) { + logger.error("siemensHvac:ResolveDpt:Error during dp reading: " + e.getLocalizedMessage()); + // Reset sessionId so we redone _auth on error + } + + } + + public void ReadMetaData(SiemensHvacMetadata parent, int id) { + try { + String request = "api/menutree/list.json?"; + if (id != -1) { + request = request + "&Id=" + id; + } + + hvacConnector.DoRequest(request, new SiemensHvacCallback() { + + @Override + public void execute(URI uri, int status, @Nullable Object response) { + if (response instanceof JsonObject) { + DecodeMetaDataResult((JsonObject) response, parent, id); + } + } + }); + } catch (Exception e) { + logger.error("siemensHvac:ResolveDpt:Error during dp reading: " + id + " ; " + e.getLocalizedMessage()); + // Reset sessionId so we redone _auth on error + } + + } + + private static int nbDpt = 0; + + public void DecodeMetaDataResult(JsonObject resultObj, SiemensHvacMetadata parent, int id) { + if (resultObj.has("MenuItems")) { + if (parent != null) { + logger.debug("Decode menuItem for :" + parent.getShortDesc()); + } else { + logger.debug("Decode menuItem for root"); + } + + SiemensHvacMetadata childNode; + JsonArray menuItems = resultObj.getAsJsonArray("MenuItems"); + + for (JsonElement child : menuItems) { + JsonObject menuItem = child.getAsJsonObject(); + + if (menuItem == null) { + continue; + } + + childNode = new SiemensHvacMetadataMenu(); + childNode.setParent(parent); + + int itemId = -1; + if (menuItem.has("Id")) { + itemId = menuItem.get("Id").getAsInt(); + } + + childNode.setMenuId(itemId); + + if (menuItem.has("Text")) { + JsonObject descObj = menuItem.getAsJsonObject("Text"); + + int catId = -1; + int groupId = -1; + int subItemId = -1; + String longDesc = ""; + String shortDesc = ""; + + if (descObj.has("CatId")) { + catId = descObj.get("CatId").getAsInt(); + } + if (descObj.has("GroupId")) { + groupId = descObj.get("GroupId").getAsInt(); + } + if (descObj.has("Id")) { + subItemId = descObj.get("Id").getAsInt(); + } + if (descObj.has("Long")) { + longDesc = descObj.get("Long").getAsString(); + } + if (descObj.has("Short")) { + shortDesc = descObj.get("Short").getAsString(); + } + + childNode.setId(subItemId); + childNode.setCatId(catId); + childNode.setGroupId(groupId); + childNode.setShortDesc(shortDesc); + childNode.setLongDesc(longDesc); + ((SiemensHvacMetadataMenu) parent).AddChild(childNode); + + // logger.debug(String.format("siemensHvac:ResolveDpt():findMenuItem: %d, %s, %s, %s, %s", itemId, + // subItemId, groupId, catId, longDesc)); + + if (itemId == 931 || itemId == 932 || itemId == 992 || itemId == 1017 || itemId == 1030 + || itemId == 1036 || itemId == 1040 || itemId == 1300 || itemId == 1489 || itemId == 1505 + || itemId == 1558) { + ReadMetaData(childNode, itemId); + } + + } + } + } + if (resultObj.has("DatapointItems")) { + if (parent != null) { + logger.debug("Decode dp for :" + parent.getShortDesc()); + } else { + logger.debug("Decode dp for root"); + } + + SiemensHvacMetadata childNode; + JsonArray dptItems = resultObj.getAsJsonArray("DatapointItems"); + + for (JsonElement child : dptItems) { + JsonObject dptItem = child.getAsJsonObject(); + + if (dptItem == null) { + continue; + } + + nbDpt++; + logger.debug("dpt1:" + nbDpt); + + childNode = new SiemensHvacMetadataDataPoint(); + childNode.setParent(parent); + + int dptId = -1; + int dpSubKey = -1; + boolean hasWriteAccess = false; + String address = ""; + + if (dptItem.has("Id")) { + dptId = dptItem.get("Id").getAsInt(); + } + if (dptItem.has("Address")) { + address = dptItem.get("Address").getAsString(); + } + if (dptItem.has("DpSubKey")) { + dpSubKey = dptItem.get("DpSubKey").getAsInt(); + } + if (dptItem.has("WriteAccess")) { + hasWriteAccess = dptItem.get("WriteAccess").getAsBoolean(); + } + + SiemensHvacMetadataDataPoint dptChild = (SiemensHvacMetadataDataPoint) childNode; + + dptChild.setDptId(dptId); + dptChild.setAddress(address); + dptChild.setDptSubKey(dpSubKey); + dptChild.setWriteAccess(hasWriteAccess); + + if (dptItem.has("Text")) { + JsonObject descObj = dptItem.getAsJsonObject("Text"); + + int catId = -1; + int groupId = -1; + int subItemId = -1; + String longDesc = ""; + String shortDesc = ""; + + if (descObj.has("CatId")) { + catId = descObj.get("CatId").getAsInt(); + } + if (descObj.has("GroupId")) { + groupId = descObj.get("GroupId").getAsInt(); + } + if (descObj.has("Id")) { + subItemId = descObj.get("Id").getAsInt(); + } + if (descObj.has("Long")) { + longDesc = descObj.get("Long").getAsString(); + } + if (descObj.has("Short")) { + shortDesc = descObj.get("Short").getAsString(); + } + + childNode.setMenuId(subItemId); + childNode.setCatId(catId); + childNode.setGroupId(groupId); + childNode.setShortDesc(shortDesc); + childNode.setLongDesc(longDesc); + + // logger.debug(String.format("siemensHvac:ResolveDpt():findDpItem: %d, %s, %s, %s, %s %s", dptId, + // catId, groupId, subItemId, shortDesc, longDesc)); + } + + resolveDptDetails(dptChild); + + ((SiemensHvacMetadataMenu) parent).AddChild(childNode); + + } + + } + if (resultObj.has("WidgetItems")) { + // JSONArray wgItems = (JSONArray) result.get("WidgetItems"); + } + + } + + @Override + public @Nullable SiemensHvacMetadata getDptMap(String key) { + + if (dptMap.containsKey("byMenu" + key)) { + return dptMap.get("byMenu" + key); + } + if (dptMap.containsKey("byName" + key)) { + return dptMap.get("byName" + key); + } + if (dptMap.containsKey("byDptId" + key)) { + return dptMap.get("byDptId" + key); + } + + return null; + + } + + public void VerifyBindingConfig(/* siemensHvacBindingConfig bindingConfig */) { + /* + * String dptType = bindingConfig.getDptType(); + * + * int dptId = bindingConfig.getDptId(); + * int dptMenuId = bindingConfig.getDptMenuId(); + * String dptName = bindingConfig.getDptName(); + * + * + * if (entry != null && entry.getClass() == siemensMetadataDataPoint.class) { + * siemensMetadataDataPoint dpt = (siemensMetadataDataPoint) entry; + * + * resolveDptDetails(dpt); + * + * if (dptId == -1) { + * bindingConfig.setDptId(dpt.getDptId()); + * } + * if (dptName == null || dptName.equals("")) { + * bindingConfig.setDptName(entry.getLongDesc()); + * } + * if (dptMenuId == -1) { + * bindingConfig.setDptMenuId(entry.getMenuId()); + * } + * + * if (dptType == null) { + * bindingConfig.setDptType(dpt.getDptType()); + * } + * } + */ + } + + public void LoadMetaDataFromCache() { + File file = null; + + try { + file = new File(CONFIG_DIR + File.separator + "siemens.json"); + + if (!file.exists()) { + return; + } + + FileInputStream is = new FileInputStream(file); + String js = IOUtils.toString(is); + + root = SiemensHvacConnectorImpl.getGsonWithAdapter().fromJson(js, SiemensHvacMetadataMenu.class); + } catch (IOException ioe) { + logger.error("Couldn't write WithingsAccount to file '{}'.", file.getAbsolutePath()); + + } + + } + + public void SaveMetaDataToCache() { + File file = null; + + try { + file = new File(CONFIG_DIR + File.separator + "siemens.json"); + + if (!file.exists()) { + file.getParentFile().mkdirs(); + file.createNewFile(); + } + + FileOutputStream os = new FileOutputStream(file); + + String js = SiemensHvacConnectorImpl.getGsonWithAdapter().toJson(root); + + IOUtils.write(js, os); + IOUtils.closeQuietly(os); + + } catch (IOException ioe) { + logger.error("Couldn't write WithingsAccount to file '{}'.", file.getAbsolutePath()); + + } + } + + public static int nbDptRq = 0; + public static int nbDptRs = 0; + + public void resolveDptDetails(SiemensHvacMetadataDataPoint dpt) { + if (dpt.getDetailsResolved()) { + return; + } + + String request = "api/menutree/datapoint_desc.json?Id=" + dpt.getDptId(); + nbDptRq++; + logger.debug("dpt21:" + nbDptRq); + hvacConnector.DoRequest(request, new SiemensHvacCallback() { + + @Override + public void execute(URI uri, int status, @Nullable Object response) { + if (response instanceof JsonObject) { + dpt.resolveDptDetails((JsonObject) response); + nbDptRs++; + logger.debug("dpt22:" + nbDptRs); + } else { + logger.debug("errror"); + } + } + }); + } + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/SiemensHvacHandlerFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/SiemensHvacHandlerFactory.java deleted file mode 100644 index 94ea5359c3e2a..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/SiemensHvacHandlerFactory.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright (c) 2014 openHAB UG (haftungsbeschraenkt) and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.openhab.binding.siemenshvac.internal; - -import static org.openhab.binding.siemenshvac.SiemensHvacBindingConstants.HVAC_THING_TYPE; - -import java.util.Collections; -import java.util.Set; - -import org.eclipse.smarthome.core.thing.Thing; -import org.eclipse.smarthome.core.thing.ThingTypeUID; -import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; -import org.eclipse.smarthome.core.thing.binding.ThingHandler; -import org.openhab.binding.siemenshvac.handler.SiemensHvacHandler; - -/** - * The {@link SiemensHvacHandlerFactory} is responsible for creating things and thing - * handlers. - * - * @author Laurent ARNAL - Initial contribution - */ -public class SiemensHvacHandlerFactory extends BaseThingHandlerFactory { - - private final static Set SUPPORTED_THING_TYPES_UIDS = Collections.singleton(HVAC_THING_TYPE); - - @Override - public boolean supportsThingType(ThingTypeUID thingTypeUID) { - return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); - } - - @Override - protected ThingHandler createHandler(Thing thing) { - - ThingTypeUID thingTypeUID = thing.getThingTypeUID(); - SiemensHvacHandler handler; - - handler = new SiemensHvacHandler(thing); - - return handler; - - // return null; - } - - private synchronized void registerDiscoveryService( - SiemensHvacHandler bridgeHandler) {/* - * HueLightDiscoveryService discoveryService = new - * HueLightDiscoveryService(bridgeHandler); - * discoveryService.activate(); - * - * this.discoveryServiceRegs.put(bridgeHandler.getThing().getUID(), - * bundleContext - * .registerService(DiscoveryService.class.getName(), discoveryService, - * new Hashtable())); - */ - } - - @Override - protected synchronized void removeHandler(ThingHandler thingHandler) { - /* - * if (thingHandler instanceof HueBridgeHandler) { - * ServiceRegistration serviceReg = this.discoveryServiceRegs.get(thingHandler.getThing().getUID()); - * if (serviceReg != null) { - * // remove discovery service, if bridge handler is removed - * HueLightDiscoveryService service = (HueLightDiscoveryService) bundleContext - * .getService(serviceReg.getReference()); - * service.deactivate(); - * serviceReg.unregister(); - * discoveryServiceRegs.remove(thingHandler.getThing().getUID()); - * } - * } - */ - } - -} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/config/SiemensHvacConfiguration.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/config/SiemensHvacConfiguration.java new file mode 100644 index 0000000000000..cfb58da199b47 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/config/SiemensHvacConfiguration.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2010-2020 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.siemenshvac.internal.config; + +/** + * The {@link SiemensHvacConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Laurent ARNAL - Initial contribution + */ +public class SiemensHvacConfiguration { + + /** + * Sample configuration parameter. Replace with your own. + */ + public String config1; +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/JSONApiResponseKeysEnum.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/JSONApiResponseKeysEnum.java deleted file mode 100644 index d748900656487..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/JSONApiResponseKeysEnum.java +++ /dev/null @@ -1,198 +0,0 @@ -/** - * Copyright (c) 2010-2015, openHAB.org and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.openhab.binding.siemenshvac.internal.constants; - -/** - * @author Alexander Betker - * @since 1.3.0 - * @version digitalSTROM-API 1.14.5 - */ -public enum JSONApiResponseKeysEnum { - - RESPONSE_OK ("ok"), - RESPONSE_SUCCESSFUL ("true"), - RESPONSE_MESSAGE ("message"), - - RESULT ("result"), - - APARTMENT_GET_NAME ("name"), - APARTMENT_GET_CONSUMPTION ("consumption"), - APARTMENT_GET_STRUCTURE ("apartment"), - APARTMENT_GET_STRUCTURE_ZONES ("zones"), - APARTMENT_GET_STRUCTURE_ZONES_ID ("id"), - APARTMENT_GET_STRUCTURE_ZONES_NAME ("name"), - APARTMENT_GET_STRUCTURE_ZONES_ISPRESENT ("isPresent"), - APARTMENT_GET_STRUCTURE_ZONES_DEVICES ("devices"), - APARTMENT_GET_STRUCTURE_ZONES_GROUPS ("groups"), - - APARTMENT_GET_DEVICES ("result"), - APARTMENT_GET_CIRCUITS ("circuits"), - - ZONE_GET_NAME ("name"), - ZONE_GET_CONSUMPTION ("consumption"), - ZONE_SCENE_GET_NAME ("name"), - ZONE_GET_REACHABLE_SCENES ("reachableScenes"), - - DEVICE_GET_NAME ("name"), - DEVICE_GET_SPEC ("result"), - DEVICE_GET_GROUPS ("groups"), - DEVICE_GET_GROUPS_ID ("id"), - DEVICE_GET_GROUPS_NAME ("name"), - DEVICE_GET_STATE ("isOn"), - DEVICE_GET_CONSUMPTION ("consumption"), - DEVICE_HAS_TAG ("hasTag"), - DEVICE_GET_TAGS ("tags"), - DEVICE_GET_CONFIG ("result"), - DEVICE_GET_CONFIG_CLASS ("class"), - DEVICE_GET_CONFIG_INDEX ("index"), - DEVICE_GET_CONFIG_VALUE ("value"), - DEVICE_GET_CONFIG_WORD ("result"), - DEVICE_GET_OUTPUT_VALUE ("value"), - DEVICE_GET_SCENE_MODE ("result"), - DEVICE_GET_SCENE_MODE_SCENE_ID ("sceneID"), - DEVICE_GET_SCENE_MODE_DONT_CARE ("dontCare"), - DEVICE_GET_SCENE_MODE_LOCAL_PRIO ("localPrio"), - DEVICE_GET_SCENE_MODE_SPECIAL_MODE ("specialMode"), - DEVICE_GET_SCENE_MODE_FLASH_MODE ("flashMode"), - DEVICE_GET_SCENE_MODE_LEDCON_INDEX ("ledconIndex"), - DEVICE_GET_SCENE_MODE_DIMTIME_INDEX ("dimtimeIndex"), - DEVICE_GET_TRANSITION_TIME ("result"), - DEVICE_GET_TRANSITION_TIME_INDEX ("dimtimeIndex"), - DEVICE_GET_TRANSITION_TIME_UP ("up"), - DEVICE_GET_TRANSITION_TIME_DOWN ("down"), - - DEVICE_GET_LED_MODE ("result"), - DEVICE_GET_LED_MODE_INDEX ("ledconIndex"), - DEVICE_GET_LED_MODE_COLOR ("colorSelect"), - DEVICE_GET_LED_MODE_SELECT ("modeSelect"), - DEVICE_GET_LED_MODE_DIM_MODE ("dimMode"), - DEVICE_GET_LED_MODE_RGB ("rgbMode"), - DEVICE_GET_LED_MODE_GROUP_COLOR_MODE ("groupColorMode"), - - DEVICE_GET_SENSOR_VALUE ("result"), - DEVICE_GET_SENSOR_VALUE_SENSOR_VALUE ("sensorValue"), - DEVICE_GET_SENSOR_TYPE ("result"), - DEVICE_GET_SENSOR_TYPE_TYPE ("sensorType"), - - DEVICE_GET_SENSOR_EVENT_TABLE_ENTRY ("result"), - DEVICE_GET_SENSOR_EVENT_TABLE_ENTRY_EVENT_INDEX ("eventIndex"), - DEVICE_GET_SENSOR_EVENT_TABLE_ENTRY_EVENT_NAME ("eventName"), - DEVICE_GET_SENSOR_EVENT_TABLE_ENTRY_IS_SCENE_DEVICE ("isSceneDevice"), - DEVICE_GET_SENSOR_EVENT_TABLE_ENTRY_SENSOR_INDEX ("sensorIndex"), - DEVICE_GET_SENSOR_EVENT_TABLE_TEST ("test"), - DEVICE_GET_SENSOR_EVENT_TABLE_ACTION ("action"), - DEVICE_GET_SENSOR_EVENT_TABLE_VALUE ("value"), - DEVICE_GET_SENSOR_EVENT_TABLE_HYSTERSIS ("hysteresis"), - DEVICE_GET_SENSOR_EVENT_TABLE_VALIDITY ("validity"), - - // Device - DEVICE_NAME ("name"), - DEVICE_ID ("id"), - DEVICE_ID_QUERY ("dSID"), - DEVICE_FUNCTION_ID ("functionID"), - DEVICE_PRODUCT_REVISION ("productRevision"), - DEVICE_PRODUCT_ID ("productID"), - DEVICE_HW_INFO ("hwInfo"), - DEVICE_ON ("on"), - DEVICE_OUTPUT_MODE ("outputMode"), - DEVICE_BUTTON_ID ("buttonID"), - DEVICE_IS_PRESENT ("isPresent"), - DEVICE_IS_PRESENT_QUERY ("present"), - DEVICE_ZONE_ID ("zoneID"), - DEVICE_ZONE_ID_QUERY ("ZoneID"), - DEVICE_GROUPS ("groups"), - - // DeviceSpec - DEVICE_SPEC_FUNCTION_ID ("functionID"), - DEVICE_SPEC_PRODUCT_ID ("productID"), - DEVICE_SPEC_REVISION_ID ("revisionID"), - - EVENT_GET_EVENT ("events"), - EVENT_GET_EVENT_ERROR ("message"), - EVENT_NAME ("name"), - EVENT_PROPERTIES ("properties"), - - DS_METER_QUERY ("dSMeters"), - DS_METER_DSID ("dsid"), - DS_METER_DSID_QUERY ("dSID"), - DS_METER_IS_PRESENT ("isPresent"), - DS_METER_IS_PRESENT_QUERY ("present"), - - DS_METER_POWER_CONSUMPTION_QUERY ("powerConsumption"), - DS_METER_ENERGY_METER_VALUE_QUERY ("energyMeterValue"), - DS_METER_ENERGY_METER_VALUE_WS_QUERY ("energyMeterValueWs"), - - // Group - GROUP_ID ("id"), - GROUP_NAME ("name"), - GROUP_IS_PRESENT ("isPresent"), - GROUP_DEVICES ("devices"), - - CIRCUIT_GET_NAME ("name"), - CIRCUIT_GET_CONSUMPTION ("consumption"), - CIRCUIT_GET_METER_VALUE ("meterValue"), - - PROPERTY_GET_STRING ("value"), - PROPERTY_GET_INTEGER ("value"), - PROPERTY_GET_BOOLEAN ("value"), - PROPERTY_NAME ("name"), - PROPERTY_TYPE ("type"), - PROPERTY_QUERY ("result"), - PROPERTY_QUERY_ZONE_ID ("ZoneID"), - PROPERTY_QUERY_DEVICE_ID ("dSID"), - PROPERTY_GET_CHILDREN ("result"), - PROPERTY_GET_PROPERTY_TYPE ("type"), - PROPERTY_GET_FLAGS ("result"), - - SYSTEM_GET_VERSION ("version"), - SYSTEM_GET_TIME ("time"), - SYSTEM_LOGIN ("token"), - SYSTEM_LOGGED_IN_USER ("name"), - - SET_FROM_APARTMENT ("self"), - SET_BY_ZONE ("self"), - SET_BY_GROUP ("self"), - SET_BY_DSID ("self"), - SET_ADD ("self"), - SET_SUBTRACT ("self"), - SET_GET_CONSUMPTION ("consumption"), - - STRUCTURE_PERSIST ("groupID"), - - METERING_GET_RESOLUTIONS ("resolutions"), - METERING_GET_RESOLUTION ("resolution"), - METERING_GET_SERIES ("series"), - METERING_GET_SERIES_DSID ("dsid"), - METERING_GET_SERIES_TYPE ("type"), - METERING_GET_VALUES ("result"), - METERING_GET_VALUES_METER_ID ("meterID"), - METERING_GET_VALUES_TYPE ("type"), - METERING_GET_VALUES_UNIT ("unit"), - METERING_GET_VALUES_RESOLUTION ("resolution"), - METERING_GET_VALUES_VALUES ("values"), - - METERING_GET_LATEST ("values"), - METERING_GET_LATEST_DSID ("dsid"), - METERING_GET_LATEST_VALUE ("value"), - METERING_GET_LATEST_DATE ("date"), - - QUERY_ZONE_ID ("ZoneID"); - - - private final String key; - - private JSONApiResponseKeysEnum(String key) { - this.key = key; - } - - public String getKey() { - return key; - } - -} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/JSONRequestConstants.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/JSONRequestConstants.java deleted file mode 100644 index 3f87342c179be..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/JSONRequestConstants.java +++ /dev/null @@ -1,973 +0,0 @@ -/** - * Copyright (c) 2010-2015, openHAB.org and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.openhab.binding.siemenshvac.internal.constants; - -/** - * @author Alexander Betker - * @since 1.3.0 - * @version digitalSTROM-API 1.14.5 - */ -public interface JSONRequestConstants { - - // Symbols - public final static String SLASH_SYMBOL = "/"; - - public final static String FIRST_PARAMETER_CONCAT_SYMBOL = "?"; - - public final static String NEXT_PARAMETER_CONCAT_SYMBOL = "&"; - - public final static String EQUAL_SIGN_SYMBOL = "="; - - - // Classes - public final static String JSON_TO_STRING = "json"; - - public final static String SYSTEM_TO_STRING = "system"; - - public final static String DEVICE_TO_STRING = "device"; - - public final static String ZONE_TO_STRING = "zone"; - - public final static String APARTMENT_TO_STRING = "apartment"; - - public final static String SET_TO_STRING = "set"; - - public final static String CIRCUIT_TO_STRING = "circuit"; - - public final static String PROPERTY_TO_STRING = "property"; - - public final static String EVENT_TO_STRING = "event"; - - public final static String STRUCTURE_TO_STRING = "structure"; - - public final static String METERING_TO_STRING = "metering"; - - - // Methods - public final static String LOGIN_TO_STRING = "login"; - - public final static String LOGOUT_TO_STRING = "logout"; - - public final static String CALLSCENE_TO_STRING = "callScene"; - - public final static String SAVESCENE_TO_STRING = "saveScene"; - - public final static String UNDOSCENE_TO_STRING = "undoScene"; - - public final static String TURN_ON_TO_STRING = "turnOn"; - - public final static String TURN_OFF_TO_STRING = "turnOff"; - - public final static String INCREASE_VALUE_TO_STRING = "increaseValue"; - - public final static String DECREASE_VALUE_TO_STRING = "decreaseValue"; - - public final static String GET_STRUCTURE_TO_STRING = "getStructure"; - - public final static String GET_DEVICES_TO_STRING = "getDevices"; - - public final static String GET_CIRCUITS_TO_STRING = "getCircuits"; - - public final static String LOGIN_APPLICATION_TO_STRING = "loginApplication"; - - public final static String GET_NAME_TO_STRING = "getName"; - - public final static String SET_NAME_TO_STRING = "setName"; - - public final static String SUBSCRIBE_TO_STRING = "subscribe"; - - public final static String UNSUBSCRIBE_TO_STRING = "unsubscribe"; - - public final static String GET_TO_STRING = "get"; - - public final static String SET_VALUE_TO_STRING = "setValue"; - - public final static String GET_CONSUMPTION_TO_STRING = "getConsumption"; - - public final static String RESCAN_TO_STRING = "rescan"; - - public final static String SCENE_SET_NAME_TO_STRING = "sceneSetName"; - - public final static String SCENE_GET_NAME_TO_STRING = "sceneGetName"; - - public final static String PUSH_SENSOR_VALUES_TO_STRING = "pushSensorValues"; - - public final static String GET_REACHABLE_SCENES_TO_STRING = "getReachableScenes"; - - public final static String GET_STATE_TO_STRING = "getState"; - - public final static String GET_GROUPS_TO_STRING = "getGroups"; - - public final static String GET_ENERGY_METER_VALUE_TO_STRING = "getEnergyMeterValue"; - - public final static String GET_STRING_TO_STRING = "getString"; - - public final static String GET_INTEGER_TO_STRING = "getInteger"; - - public final static String GET_BOOLEAN_TO_STRING = "getBoolean"; - - public final static String SET_STRING_TO_STRING = "setString"; - - public final static String SET_INTEGER_TO_STRING = "setInteger"; - - public final static String SET_BOOLEAN_TO_STRING = "setBoolean"; - - public final static String GET_CHILDREN_TO_STRING = "getChildren"; - - public final static String SET_FLAG_TO_STRING = "setFlag"; - - public final static String GET_FLAGS_TO_STRING = "getFlags"; - - public final static String QUERY_TO_STRING = "query"; - - public final static String REMOVE_TO_STRING = "remove"; - - public final static String GET_TYPE_TO_STRING = "getType"; - - public final static String GET_SPEC_TO_STRING = "getSpec"; - - public final static String VERSION_TO_STRING = "version"; - - public final static String TIME_TO_STRING = "time"; - - public final static String FROM_APARTMENT_TO_STRING = "fromApartment"; - - public final static String BY_ZONE_TO_STRING = "byZone"; - - public final static String BY_GROUP_TO_STRING = "byGroup"; - - public final static String BY_DSID_TO_STRING = "byDSID"; - - public final static String ADD_TO_STRING = "add"; - - public final static String SUBTRACT_TO_STRING = "subtract"; - - public final static String LOGGED_IN_USER_TO_STRING = "loggedInUser"; - - public final static String ZONE_ADD_DEVICE_TO_STRING = "zoneAddDevice"; - - public final static String ADD_ZONE_TO_STRING = "addZone"; - - public final static String REMOVE_ZONE_TO_STRING = "removeZone"; - - public final static String REMOVE_DEVICE_TO_STRING = "removeDevice"; - - public final static String PERSIST_SET_TO_STRING = "persistSet"; - - public final static String UNPERSIST_SET_TO_STRING = "unpersistSet"; - - public final static String ADD_GROUP_TO_STRING = "addGroup"; - - public final static String GROUP_ADD_DEVICE_TO_STRING = "groupAddDevice"; - - public final static String GROUP_REMOVE_DEVICE_TO_STRING = "groupRemoveDevice"; - - public final static String GET_RESOLUTIONS_TO_STRING = "getResolutions"; - - public final static String GET_SERIES_TO_STRING = "getSeries"; - - public final static String GET_VALUES_TO_STRING = "getValues"; - - public final static String GET_LATEST_TO_STRING = "getLatest"; - - public final static String ADD_TAG_TO_STRING = "addTag"; - - public final static String REMOVE_TAG_TO_STRING = "removeTag"; - - public final static String HAS_TAG_TO_STRING = "hasTag"; - - public final static String GET_TAGS_TO_STRING = "getTags"; - - public final static String LOCK_TO_STRING = "lock"; - - public final static String UNLOCK_TO_STRING = "unlock"; - - public final static String GET_SENSOR_EVENT_TABLE_ENTRY_TO_STRING = "getSensorEventTableEntry"; - - public final static String SET_SENSOR_EVENT_TABLE_ENTRY_TO_STRING = "setSensorEventTableEntry"; - - public final static String ADD_TO_AREA_TO_STRING = "addToArea"; - - public final static String REMOVE_FROM_AREA_TO_STRING = "removeFromArea"; - - public final static String SET_CONFIG_TO_STRING = "setConfig"; - - public final static String GET_CONFIG_TO_STRING = "getConfig"; - - public final static String GET_CONFIG_WORD_TO_STRING = "getConfigWord"; - - public final static String SET_JOKER_GROUP_TO_STRING = "setJokerGroup"; - - public final static String SET_BUTTON_ID_TO_STRING = "setButtonID"; - - public final static String SET_BUTTON_INPUT_MODE_TO_STRING = "setButtonInputMode"; - - public final static String SET_OUTPUT_MODE_TO_STRING = "setOutputMode"; - - public final static String SET_PROG_MODE_TO_STRING = "setProgMode"; - - public final static String GET_OUTPUT_VALUE_TO_STRING = "getOutputValue"; - - public final static String SET_OUTPUT_VALUE_TO_STRING = "setOutputValue"; - - public final static String GET_SCENE_MODE_TO_STRING = "getSceneMode"; - - public final static String SET_SCENE_MODE_TO_STRING = "setSceneMode"; - - public final static String GET_TRANSITION_TIME_TO_STRING = "getTransitionTime"; - - public final static String SET_TRANSITION_TIME_TO_STRING = "setTransitionTime"; - - public final static String GET_LED_MODE_TO_STRING = "getLedMode"; - - public final static String SET_LED_MODE_TO_STRING = "setLedMode"; - - public final static String GET_SENSOR_VALUE_TO_STRING = "getSensorValue"; - - public final static String GET_SENSOR_TYPE_TO_STRING = "getSensorType"; - - - // Parameter-Names - public final static String TOKEN_TO_STRING = "token"; - - public final static String NAME_TO_STRING = "name"; - - public final static String NEW_NAME_TO_STRING = "newName"; - - public final static String DSID_TO_STRING = "dsid"; - - public final static String SCENENUMBER_TO_STRING = "sceneNumber"; - - public final static String LOGIN_TOKEN_TO_STRING = "loginToken"; - - public final static String USER_TO_STRING = "user"; - - public final static String PASSWORD_TO_STRING = "password"; - - public final static String SUBSCRIPTIONID_TO_STRING = "subscriptionID"; - - public final static String TIMEOUT_TO_STRING = "timeout"; - - public final static String GROUP_ID_TO_STRING = "groupID"; - - public final static String GROUP_NAME_TO_STRING = "groupName"; - - public final static String VALUE_TO_STRING = "value"; - - public final static String FORCE_TO_STRING = "force"; - - public final static String ID_TO_STRING = "id"; - - public final static String ENABLE_TO_STRING = "enable"; - - public final static String DISABLE_TO_STRING = "disable"; - - public final static String UNASSIGNED_TO_STRING = "unassigned"; - - public final static String SOURCE_DSID_TO_STRING = "sourceDSID"; - - public final static String SENSOR_TYPE_TO_STRING = "sensorType"; - - public final static String SENSOR_VALUE_TO_STRING = "sensorValue"; - - public final static String FLAG_TO_STRING = "flag"; - - public final static String PATH_TO_STRING = "path"; - - public final static String RAISE_TO_STRING = "raise"; - - public final static String CONTEXT_TO_STRING = "context"; - - public final static String LOCATION_TO_STRING = "location"; - - public final static String SELF_TO_STRING = "self"; - - public final static String ZONE_ID_TO_STRING = "zoneID"; - - public final static String ZONE_NAME_TO_STRING = "zoneName"; - - public final static String OTHER_TO_STRING = "other"; - - public final static String DEVICE_ID_TO_STRING = "deviceID"; - - public final static String UNIT_TO_STRING = "unit"; - - public final static String START_TIME_TO_STRING = "startTime"; - - public final static String END_TIME_TO_STRING = "endTime"; - - public final static String VALUE_COUNT_TO_STRING = "valueCount"; - - public final static String RESOLUTION_TO_STRING = "resolution"; - - public final static String TYPE_TO_STRING = "type"; - - public final static String FROM_TO_STRING = "from"; - - public final static String TAG_TO_STRING = "tag"; - - public final static String CLASS_TO_STRING = "class"; - - public final static String INDEX_TO_STRING = "index"; - - public final static String BUTTON_ID_TO_STRING = "buttonID"; - - public final static String MODE_ID_TO_STRING = "modeID"; - - public final static String MODE_TO_STRING = "mode"; - - public final static String OFFSET_TO_STRING = "offset"; - - public final static String SCENE_ID_TO_STRING = "sceneID"; - - public final static String DONT_CARE_TO_STRING = "dontCare"; - - public final static String LOCAL_PRIO_TO_STRING = "localPrio"; - - public final static String SPECIAL_MODE_TO_STRING = "specialMode"; - - public final static String FLASH_MODE_TO_STRING = "flashMode"; - - public final static String LED_CON_INDEX_TO_STRING = "ledconIndex"; - - public final static String DIM_TIME_INDEX_TO_STRING = "dimtimeIndex"; - - public final static String UP_TO_STRING = "up"; - - public final static String DOWN_TO_STRING = "down"; - - public final static String COLOR_SELECT_TO_STRING = "colorSelect"; - - public final static String MODE_SELECT_TO_STRING = "modeSelect"; - - public final static String DIM_MODE_TO_STRING = "dimMode"; - - public final static String RGB_MODE_TO_STRING = "rgbMode"; - - public final static String GROUP_COLOR_MODE_TO_STRING = "groupColorMode"; - - public final static String SENSOR_INDEX_TO_STRING = "sensorIndex"; - - public final static String EVENT_INDEX_TO_STRING = "eventIndex"; - - public final static String AREA_SCENE_TO_STRING = "areaScene"; - - public final static String EVENT_NAME_TO_STRING = "eventName"; - - public final static String TEST_TO_STRING = "test"; - - public final static String HYSTERSIS_TO_STRING = "hysteresis"; - - public final static String VALIDITY_TO_STRING = "validity"; - - public final static String ACTION_TO_STRING = "action"; - - public final static String BUTTON_NUMBER_TO_STRING = "buttonNumber"; - - public final static String CLICK_TYPE_TO_STRING = "clickType"; - - public final static String SCENE_DEVICE_MODE_TO_STRING = "sceneDeviceMode"; - - - // values - public final static String TRUE_TO_STRING = "true"; - - - // Prefixes-Classes - public final static String JSON_PREFIX = SLASH_SYMBOL+JSON_TO_STRING+SLASH_SYMBOL; - - public final static String JSON_SYSTEM_PREFIX = JSON_PREFIX+SYSTEM_TO_STRING+SLASH_SYMBOL; - - public final static String JSON_APARTMENT_PREFIX = JSON_PREFIX+APARTMENT_TO_STRING+SLASH_SYMBOL; - - public final static String JSON_DEVICE_PREFIX = JSON_PREFIX+DEVICE_TO_STRING+SLASH_SYMBOL; - - public final static String JSON_ZONE_PREFIX = JSON_PREFIX+ZONE_TO_STRING+SLASH_SYMBOL; - - public final static String JSON_SET_PREFIX = JSON_PREFIX+SET_TO_STRING+SLASH_SYMBOL; - - public final static String JSON_CIRCUIT_PREFIX = JSON_PREFIX+CIRCUIT_TO_STRING+SLASH_SYMBOL; - - public final static String JSON_EVENT_PREFIX = JSON_PREFIX+EVENT_TO_STRING+SLASH_SYMBOL; - - public final static String JSON_PROPERTY_PREFIX = JSON_PREFIX+PROPERTY_TO_STRING+SLASH_SYMBOL; - - public final static String JSON_STRUCTURE_PREFIX = JSON_PREFIX+STRUCTURE_TO_STRING+SLASH_SYMBOL; - - public final static String JSON_METERING_PREFIX = JSON_PREFIX+METERING_TO_STRING+SLASH_SYMBOL; - - - // Parameter - public final static String PARAMETER_NAME = NAME_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_NEW_NAME = NEW_NAME_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_USER = USER_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_PASSWORD = PASSWORD_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_DSID = DSID_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_SCENE_NUMBER = SCENENUMBER_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_TOKEN = TOKEN_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_SUBSCRIPTION_ID = SUBSCRIPTIONID_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_TIMEOUT = TIMEOUT_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_GROUP_ID = GROUP_ID_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_GROUP_NAME = GROUP_NAME_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_VALUE = VALUE_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_FORCE = FORCE_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_ID = ID_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_UNASSDIGNED = UNASSIGNED_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_SOURCE_DSID = SOURCE_DSID_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_SENSOR_TYPE = SENSOR_TYPE_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_SENSOR_VALUE = SENSOR_VALUE_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_QUERY = QUERY_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_FLAG = FLAG_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_PATH = PATH_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_CONTEXT = CONTEXT_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_LOCATION = LOCATION_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_SELF = SELF_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_ZONE_ID = ZONE_ID_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_ZONE_NAME = ZONE_NAME_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_OTHER = OTHER_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_DEVICE_ID = DEVICE_ID_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_ZONE = ZONE_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_SET = SET_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_TYPE = TYPE_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_RESOLUTION = RESOLUTION_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_UNIT = UNIT_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_START_TIME = START_TIME_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_END_TIME = END_TIME_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_VALUE_COUNT = VALUE_COUNT_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_FROM = FROM_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_TAG = TAG_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_CLASS = CLASS_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_INDEX = INDEX_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_BUTTON_ID = BUTTON_ID_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_MODE_ID = MODE_ID_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_MODE = MODE_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_OFFSET = OFFSET_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_SCENE_ID = SCENE_ID_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_DONT_CARE = DONT_CARE_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_LOCAL_PRIO = LOCAL_PRIO_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_SPECIAL_MODE = SPECIAL_MODE_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_FLASH_MODE = FLASH_MODE_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_LED_CON_INDEX = LED_CON_INDEX_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_DIM_TIME_INDEX = DIM_TIME_INDEX_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_UP = UP_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_DOWN = DOWN_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_COLOR_SELECT = COLOR_SELECT_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_MODE_SELECT = MODE_SELECT_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_DIM_MODE = DIM_MODE_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_RGB_MODE = RGB_MODE_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_GROUP_COLOR_MODE = GROUP_COLOR_MODE_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_SENSOR_INDEX = SENSOR_INDEX_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_EVENT_INDEX = EVENT_INDEX_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_AREA_SCENE = AREA_SCENE_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_EVENT_NAME = EVENT_NAME_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_TEST = TEST_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_HYSTERSIS = HYSTERSIS_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_VALIDITY = VALIDITY_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_ACTION = ACTION_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_BUTTON_NUMBER = BUTTON_NUMBER_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_CLICK_TYPE = CLICK_TYPE_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String PARAMETER_SCENE_DEVICE_MODE = SCENE_DEVICE_MODE_TO_STRING+EQUAL_SIGN_SYMBOL; - - // Infix-Parameter - public final static String INFIX_PARAMETER_TIMEOUT = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_TIMEOUT; - - public final static String INFIX_PARAMETER_SUBSCRIPTION_ID = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_SUBSCRIPTION_ID; - - public final static String INFIX_PARAMETER_PASSWORD = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_PASSWORD; - - public final static String INFIX_PARAMETER_NEW_NAME = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_NEW_NAME; - - public final static String INFIX_PARAMETER_GROUP_ID = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_GROUP_ID; - - public final static String INFIX_PARAMETER_GROUP_NAME = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_GROUP_NAME; - - public final static String INFIX_PARAMETER_VALUE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_VALUE; - - public final static String INFIX_PARAMETER_SCENE_NUMBER = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_SCENE_NUMBER; - - public final static String INFIX_PARAMETER_FORCE_TRUE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_FORCE+TRUE_TO_STRING; - - public final static String INFIX_PARAMETER_NAME = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_NAME; - - public final static String INFIX_PARAMETER_ID = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_ID; - - public final static String INFIX_PARAMETER_UNASSIGNED_TRUE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_UNASSDIGNED+TRUE_TO_STRING; - - public final static String INFIX_PARAMETER_SOURCE_DSID = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_SOURCE_DSID; - - public final static String INFIX_PARAMETER_SENSOR_TYPE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_SENSOR_TYPE; - - public final static String INFIX_PARAMETER_SENSOR_VALUE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_SENSOR_VALUE; - - public final static String INFIX_PARAMETER_DSID = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_DSID; - - public final static String INFIX_PARAMETER_QUERY = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_QUERY; - - public final static String INFIX_PARAMETER_FLAG = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_FLAG; - - public final static String INFIX_PARAMETER_PATH = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_PATH; - - public final static String INFIX_PARAMETER_CONTEXT = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_CONTEXT; - - public final static String INFIX_PARAMETER_LOCATION = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_LOCATION; - - public final static String INFIX_PARAMETER_SELF = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_SELF; - - public final static String INFIX_PARAMETER_ZONE_ID = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_ZONE_ID; - - public final static String INFIX_PARAMETER_ZONE_NAME = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_ZONE_NAME; - - public final static String INFIX_PARAMETER_OTHER = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_OTHER; - - public final static String INFIX_PARAMETER_DEVICE_ID = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_DEVICE_ID; - - public final static String INFIX_PARAMETER_ZONE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_ZONE; - - public final static String INFIX_PARAMETER_SET = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_SET; - - public final static String INFIX_PARAMETER_TYPE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_TYPE; - - public final static String INFIX_PARAMETER_RESOLUTION = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_RESOLUTION; - - public final static String INFIX_PARAMETER_UNIT = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_UNIT; - - public final static String INFIX_PARAMETER_START_TIME = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_START_TIME; - - public final static String INFIX_PARAMETER_END_TIME = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_END_TIME; - - public final static String INFIX_PARAMETER_VALUE_COUNT = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_VALUE_COUNT; - - public final static String INFIX_PARAMETER_FROM = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_FROM; - - public final static String INFIX_PARAMETER_TAG = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_TAG; - - public final static String INFIX_PARAMETER_CLASS = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_CLASS; - - public final static String INFIX_PARAMETER_INDEX = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_INDEX; - - public final static String INFIX_PARAMETER_BUTTON_ID = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_BUTTON_ID; - - public final static String INFIX_PARAMETER_MODE_ID = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_MODE_ID; - - public final static String INFIX_PARAMETER_MODE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_MODE; - - public final static String INFIX_PARAMETER_OFFSET = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_OFFSET; - - public final static String INFIX_PARAMETER_SCENE_ID = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_SCENE_ID; - - public final static String INFIX_PARAMETER_DONT_CARE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_DONT_CARE; - - public final static String INFIX_PARAMETER_LOCAL_PRIO = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_LOCAL_PRIO; - - public final static String INFIX_PARAMETER_SPECIAL_MODE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_SPECIAL_MODE; - - public final static String INFIX_PARAMETER_FLASH_MODE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_FLASH_MODE; - - public final static String INFIX_PARAMETER_LED_CON_INDEX = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_LED_CON_INDEX; - - public final static String INFIX_PARAMETER_DIM_TIME_INDEX = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_DIM_TIME_INDEX; - - public final static String INFIX_PARAMETER_UP = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_UP; - - public final static String INFIX_PARAMETER_DOWN = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_DOWN; - - public final static String INFIX_PARAMETER_COLOR_SELECT = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_COLOR_SELECT; - - public final static String INFIX_PARAMETER_MODE_SELECT = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_MODE_SELECT; - - public final static String INFIX_PARAMETER_DIM_MODE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_DIM_MODE; - - public final static String INFIX_PARAMETER_RGB_MODE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_RGB_MODE; - - public final static String INFIX_PARAMETER_GROUP_COLOR_MODE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_GROUP_COLOR_MODE; - - public final static String INFIX_PARAMETER_SENSOR_INDEX = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_SENSOR_INDEX; - - public final static String INFIX_PARAMETER_EVENT_INDEX = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_EVENT_INDEX; - - public final static String INFIX_PARAMETER_AREA_SCENE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_AREA_SCENE; - - public final static String INFIX_PARAMETER_EVENT_NAME = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_EVENT_NAME; - - public final static String INFIX_PARAMETER_TEST = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_TEST; - - public final static String INFIX_PARAMETER_HYSTERSIS = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_HYSTERSIS; - - public final static String INFIX_PARAMETER_VALIDITY = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_VALIDITY; - - public final static String INFIX_PARAMETER_ACTION = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_ACTION; - - public final static String INFIX_PARAMETER_BUTTON_NUMBER = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_BUTTON_NUMBER; - - public final static String INFIX_PARAMETER_CLICK_TYPE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_CLICK_TYPE; - - public final static String INFIX_PARAMETER_SCENE_DEVICE_MODE = NEXT_PARAMETER_CONCAT_SYMBOL+PARAMETER_SCENE_DEVICE_MODE; - - - // Apartment - public final static String JSON_APARTMENT_GET_STRUCTURE = JSON_APARTMENT_PREFIX+GET_STRUCTURE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_APARTMENT_GET_NAME = JSON_APARTMENT_PREFIX+GET_NAME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_APARTMENT_SET_NAME = JSON_APARTMENT_PREFIX+SET_NAME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_APARTMENT_TURN_ON = JSON_APARTMENT_PREFIX+TURN_ON_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_APARTMENT_TURN_OFF = JSON_APARTMENT_PREFIX+TURN_OFF_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_APARTMENT_INCREASE_VALUE = JSON_APARTMENT_PREFIX+INCREASE_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_APARTMENT_DECREASE_VALUE = JSON_APARTMENT_PREFIX+DECREASE_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_APARTMENT_SET_VALUE = JSON_APARTMENT_PREFIX+SET_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_APARTMENT_CALLSCENE = JSON_APARTMENT_PREFIX+CALLSCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_APARTMENT_SAVESCENE = JSON_APARTMENT_PREFIX+SAVESCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_APARTMENT_UNDOSCENE = JSON_APARTMENT_PREFIX+UNDOSCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_APARTMENT_GET_CONSUMPTION = JSON_APARTMENT_PREFIX+GET_CONSUMPTION_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_APARTMENT_GET_DEVICES = JSON_APARTMENT_PREFIX+GET_DEVICES_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_APARTMENT_GET_CIRCUITS = JSON_APARTMENT_PREFIX+GET_CIRCUITS_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_APARTMENT_RESCAN = JSON_APARTMENT_PREFIX+RESCAN_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - - // Zone - public final static String JSON_ZONE_GET_NAME = JSON_ZONE_PREFIX+GET_NAME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_ZONE_SET_NAME = JSON_ZONE_PREFIX+SET_NAME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_ZONE_TURN_ON = JSON_ZONE_PREFIX+TURN_ON_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_ZONE_TURN_OFF = JSON_ZONE_PREFIX+TURN_OFF_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_ZONE_INCREASE_VALUE = JSON_ZONE_PREFIX+INCREASE_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_ZONE_DECREASE_VALUE = JSON_ZONE_PREFIX+DECREASE_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_ZONE_ENABLE = JSON_ZONE_PREFIX+ENABLE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_ZONE_DISABLE = JSON_ZONE_PREFIX+DISABLE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_ZONE_SET_VALUE = JSON_ZONE_PREFIX+SET_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_ZONE_CALLSCENE = JSON_ZONE_PREFIX+CALLSCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_ZONE_SAVESCENE = JSON_ZONE_PREFIX+SAVESCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_ZONE_UNDOSCENE = JSON_ZONE_PREFIX+UNDOSCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_ZONE_GET_CONSUMPTION = JSON_ZONE_PREFIX+GET_CONSUMPTION_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_ZONE_SCENE_SET_NAME = JSON_ZONE_PREFIX+SCENE_SET_NAME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_ZONE_SCENE_GET_NAME = JSON_ZONE_PREFIX+SCENE_GET_NAME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_ZONE_PUSH_SENSOR_VALUES = JSON_ZONE_PREFIX+PUSH_SENSOR_VALUES_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_ZONE_GET_REACHABLE_SCENES = JSON_ZONE_PREFIX+GET_REACHABLE_SCENES_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - - // Device - public final static String JSON_DEVICE_GET_NAME = JSON_DEVICE_PREFIX+GET_NAME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_SET_NAME = JSON_DEVICE_PREFIX+SET_NAME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_GET_SPEC = JSON_DEVICE_PREFIX+GET_SPEC_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_GET_GROUPS = JSON_DEVICE_PREFIX+GET_GROUPS_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_TURN_ON = JSON_DEVICE_PREFIX+TURN_ON_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_TURN_OFF = JSON_DEVICE_PREFIX+TURN_OFF_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_INCREASE_VALUE = JSON_DEVICE_PREFIX+INCREASE_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_DECREASE_VALUE = JSON_DEVICE_PREFIX+DECREASE_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_SAVESCENE = JSON_DEVICE_PREFIX+SAVESCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_UNDOSCENE = JSON_DEVICE_PREFIX+UNDOSCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_GET_CONSUMPTION = JSON_DEVICE_PREFIX+GET_CONSUMPTION_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_ADD_TAG = JSON_DEVICE_PREFIX+ADD_TAG_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_REMOVE_TAG = JSON_DEVICE_PREFIX+REMOVE_TAG_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_HAS_TAG = JSON_DEVICE_PREFIX+HAS_TAG_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_GET_TAGS = JSON_DEVICE_PREFIX+GET_TAGS_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_LOCK = JSON_DEVICE_PREFIX+LOCK_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_UNLOCK = JSON_DEVICE_PREFIX+UNLOCK_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_GET_SENSOR_EVENT_TABLE_ENTRY = JSON_DEVICE_PREFIX+GET_SENSOR_EVENT_TABLE_ENTRY_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_SET_SENSOR_EVENT_TABLE_ENTRY = JSON_DEVICE_PREFIX+SET_SENSOR_EVENT_TABLE_ENTRY_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_ADD_TO_AREA = JSON_DEVICE_PREFIX+ADD_TO_AREA_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_REMOVE_FROM_AREA = JSON_DEVICE_PREFIX+REMOVE_FROM_AREA_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_ENABLE = JSON_DEVICE_PREFIX+ENABLE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_DISABLE = JSON_DEVICE_PREFIX+DISABLE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_GET_STATE = JSON_DEVICE_PREFIX+GET_STATE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_CALLSCENE = JSON_DEVICE_PREFIX+CALLSCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_SET_CONFIG = JSON_DEVICE_PREFIX+SET_CONFIG_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_GET_CONFIG = JSON_DEVICE_PREFIX+GET_CONFIG_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_GET_CONFIG_WORD = JSON_DEVICE_PREFIX+GET_CONFIG_WORD_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_SET_JOKER_GROUP = JSON_DEVICE_PREFIX+SET_JOKER_GROUP_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_BUTTON_ID = JSON_DEVICE_PREFIX+SET_BUTTON_ID_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_BUTTON_INPUT_MODE = JSON_DEVICE_PREFIX+SET_BUTTON_INPUT_MODE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_SET_OUTPUT_MODE = JSON_DEVICE_PREFIX+SET_OUTPUT_MODE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_SET_PROG_MODE = JSON_DEVICE_PREFIX+SET_PROG_MODE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_GET_OUTPUT_VALUE = JSON_DEVICE_PREFIX+GET_OUTPUT_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_SET_OUTPUT_VALUE = JSON_DEVICE_PREFIX+SET_OUTPUT_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_GET_SCENE_MODE = JSON_DEVICE_PREFIX+GET_SCENE_MODE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_SET_SCENE_MODE = JSON_DEVICE_PREFIX+SET_SCENE_MODE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_GET_TRANSITION_TIME = JSON_DEVICE_PREFIX+GET_TRANSITION_TIME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_SET_TRANSITION_TIME = JSON_DEVICE_PREFIX+SET_TRANSITION_TIME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_GET_LED_MODE = JSON_DEVICE_PREFIX+GET_LED_MODE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_SET_LED_MODE = JSON_DEVICE_PREFIX+SET_LED_MODE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_GET_SENSOR_VALUE = JSON_DEVICE_PREFIX+GET_SENSOR_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_GET_SENSOR_TYPE = JSON_DEVICE_PREFIX+GET_SENSOR_TYPE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_DEVICE_SET_VALUE = JSON_DEVICE_PREFIX+SET_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - - // Circuit - public final static String JSON_CIRCUIT_GET_NAME = JSON_CIRCUIT_PREFIX+GET_NAME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_CIRCUIT_SET_NAME = JSON_CIRCUIT_PREFIX+SET_NAME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_CIRCUIT_GET_CONSUMPTION = JSON_CIRCUIT_PREFIX+GET_CONSUMPTION_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_CIRCUIT_GET_ENERGY_METER_VALUE = JSON_CIRCUIT_PREFIX+GET_ENERGY_METER_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_CIRCUIT_RESCAN = JSON_CIRCUIT_PREFIX+RESCAN_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - - // Property - public final static String JSON_PROPERTY_GET_STRING = JSON_PROPERTY_PREFIX+GET_STRING_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_PROPERTY_GET_INTEGER = JSON_PROPERTY_PREFIX+GET_INTEGER_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_PROPERTY_GET_BOOLEAN = JSON_PROPERTY_PREFIX+GET_BOOLEAN_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_PROPERTY_SET_STRING = JSON_PROPERTY_PREFIX+SET_STRING_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_PROPERTY_SET_INTEGER = JSON_PROPERTY_PREFIX+SET_INTEGER_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_PROPERTY_SET_BOOLEAN = JSON_PROPERTY_PREFIX+SET_BOOLEAN_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_PROPERTY_GET_CHILDREN = JSON_PROPERTY_PREFIX+GET_CHILDREN_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_PROPERTY_GET_TYPE = JSON_PROPERTY_PREFIX+GET_TYPE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_PROPERTY_SET_FLAG = JSON_PROPERTY_PREFIX+SET_FLAG_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_PROPERTY_GET_FLAGS = JSON_PROPERTY_PREFIX+GET_FLAGS_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_PROPERTY_QUERY = JSON_PROPERTY_PREFIX+QUERY_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_PROPERTY_REMOVE = JSON_PROPERTY_PREFIX+REMOVE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - - // Event - public final static String JSON_EVENT_RAISE = JSON_EVENT_PREFIX+RAISE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_EVENT_SUBSCRIBE = JSON_EVENT_PREFIX+SUBSCRIBE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_EVENT_UNSUBSCRIBE = JSON_EVENT_PREFIX+UNSUBSCRIBE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_EVENT_GET = JSON_EVENT_PREFIX+GET_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - - // System - public final static String JSON_SYSTEM_VERSION = JSON_SYSTEM_PREFIX+VERSION_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_SYSTEM_TIME = JSON_SYSTEM_PREFIX+TIME_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_SYSTEM_LOGIN = JSON_SYSTEM_PREFIX+LOGIN_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_SYSTEM_LOGIN_APPLICATION = JSON_SYSTEM_PREFIX+LOGIN_APPLICATION_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL+LOGIN_TOKEN_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String JSON_SYSTEM_LOGOUT = JSON_SYSTEM_PREFIX+LOGOUT_TO_STRING; - - public final static String JSON_SYSTEM_LOGGED_IN_USER = JSON_SYSTEM_PREFIX+LOGGED_IN_USER_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - - // Set - public final static String JSON_SET_FROM_APARTMENT = JSON_SET_PREFIX+FROM_APARTMENT_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_SET_BY_ZONE = JSON_SET_PREFIX+BY_ZONE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_SET_BY_GROUP = JSON_SET_PREFIX+BY_GROUP_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_SET_BY_DSID = JSON_SET_PREFIX+BY_DSID_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_SET_ADD = JSON_SET_PREFIX+ADD_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_SET_SUBTRACT = JSON_SET_PREFIX+SUBTRACT_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_SET_TURN_ON = JSON_SET_PREFIX+TURN_ON_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_SET_TURN_OFF = JSON_SET_PREFIX+TURN_OFF_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_SET_INCREASE_VALUE = JSON_SET_PREFIX+INCREASE_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_SET_DECREASE_VALUE = JSON_SET_PREFIX+DECREASE_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_SET_ENABLE = JSON_SET_PREFIX+ENABLE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_SET_DISABLE = JSON_SET_PREFIX+DISABLE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_SET_SET_VALUE = JSON_SET_PREFIX+SET_VALUE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_SET_CALLSCENE = JSON_SET_PREFIX+CALLSCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_SET_SAVESCENE = JSON_SET_PREFIX+SAVESCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_SET_UNDOSCENE = JSON_SET_PREFIX+UNDOSCENE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_SET_GET_CONSUMPTION = JSON_SET_PREFIX+GET_CONSUMPTION_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - - // Structure - public final static String JSON_STRUCTURE_ZONE_ADD_DEVICE = JSON_STRUCTURE_PREFIX+ZONE_ADD_DEVICE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_STRUCTURE_ADD_ZONE = JSON_STRUCTURE_PREFIX+ADD_ZONE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_STRUCTURE_REMOVE_ZONE = JSON_STRUCTURE_PREFIX+REMOVE_ZONE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_STRUCTURE_REMOVE_DEVICE = JSON_STRUCTURE_PREFIX+REMOVE_DEVICE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_STRUCTURE_PERSIST_SET = JSON_STRUCTURE_PREFIX+PERSIST_SET_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_STRUCTURE_UNPERSIST_SET = JSON_STRUCTURE_PREFIX+UNPERSIST_SET_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_STRUCTURE_ADD_GROUP = JSON_STRUCTURE_PREFIX+ADD_GROUP_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_STRUCTURE_GROUP_ADD_DEVICE = JSON_STRUCTURE_PREFIX+GROUP_ADD_DEVICE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_STRUCTURE_GROUP_REMOVE_DEVICE = JSON_STRUCTURE_PREFIX+GROUP_REMOVE_DEVICE_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - - // Metering - public final static String JSON_METERING_GET_RESOLUTIONS = JSON_METERING_PREFIX+GET_RESOLUTIONS_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_METERING_GET_SERIES = JSON_METERING_PREFIX+GET_SERIES_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_METERING_GET_VALUES = JSON_METERING_PREFIX+GET_VALUES_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - public final static String JSON_METERING_GET_LATEST = JSON_METERING_PREFIX+GET_LATEST_TO_STRING+FIRST_PARAMETER_CONCAT_SYMBOL; - - - // Token - public final static String JSON_TOKEN_AT_LAST_PREFIX = NEXT_PARAMETER_CONCAT_SYMBOL+TOKEN_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String JSON_TOKEN_AT_FIRST = FIRST_PARAMETER_CONCAT_SYMBOL+TOKEN_TO_STRING+EQUAL_SIGN_SYMBOL; - - public final static String QUERY_GET_METERLIST = "/apartment/dSMeters/*(dSID)"; -} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java new file mode 100644 index 0000000000000..4281e38a0d49f --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2010-2020 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.siemenshvac.internal.constants; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.ThingTypeUID; + +/** + * The {@link SiemensHvacBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Laurent ARNAL - Initial contribution + */ +@NonNullByDefault +public class SiemensHvacBindingConstants { + + public static final String BINDING_ID = "siemenshvac"; + + public static final String CONFIG_DESCRIPTION_URI_CHANNEL = "channel-type:siemenshvac:config"; + + // List of all Thing Type UIDs + // Thing Type UIDs + public static final ThingTypeUID THING_TYPE_OZW672 = new ThingTypeUID(BINDING_ID, "ozw672"); + public static final ThingTypeUID THING_TYPE_RVS41_813_327 = new ThingTypeUID(BINDING_ID, "RVS41_813_327"); + + // List of all Channel ids + public static final String CHANNEL_1 = "channel1"; + + public static final String IP_ADDRESS = "ipAddress"; + public static final String BASE_URL = "baseUrl"; + + public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge"); + + public static final String PROPERTY_VENDOR_NAME = "eQ-3 AG"; + + public static final String ITEM_TYPE_SWITCH = "Switch"; + public static final String ITEM_TYPE_ROLLERSHUTTER = "Rollershutter"; + public static final String ITEM_TYPE_CONTACT = "Contact"; + public static final String ITEM_TYPE_STRING = "String"; + public static final String ITEM_TYPE_NUMBER = "Number"; + public static final String ITEM_TYPE_DIMMER = "Dimmer"; + public static final String ITEM_TYPE_DATETIME = "DateTime"; + + public static final String CONFIG_DESCRIPTION_URI_THING_PREFIX = "thing-type"; + + public static final String DPT_TYPE_STRING = "String"; + public static final String DPT_TYPE_ENUM = "Enumeration"; + public static final String DPT_TYPE_NUMERIC = "Numeric"; + public static final String DPT_TYPE_RADIO = "RadioButton"; + public static final String DPT_TYPE_DATE = "DateTime"; + public static final String DPT_TYPE_TIME = "TimeOfDay"; + public static final String DPT_TYPE_SCHEDULER = "Scheduler"; + public static final String DPT_TYPE_CALENDAR = "Calendar"; + + public static final String CATEGORY_THING_HVAC = "HVAC"; + + public static final String CATEGORY_CHANNEL_WIDGETS_NUMBER = "Number"; + public static final String CATEGORY_CHANNEL_WIDGETS_SLIDER = "Slider"; + public static final String CATEGORY_CHANNEL_WIDGETS_SWITCH = "Switch"; + public static final String CATEGORY_CHANNEL_WIDGETS_TEXT = "Text"; + public static final String CATEGORY_CHANNEL_WIDGETS_GROUP = "Group"; + + public static final String CATEGORY_CHANNEL_PROPS_TEMP = "Temperature"; + public static final String CATEGORY_CHANNEL_PROPS_TIME = "Time"; + + public static final String CATEGORY_CHANNEL_CONTROL_HEATING = "Heating"; + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java new file mode 100644 index 0000000000000..df0b8e5316863 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2010-2020 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.siemenshvac.internal.discovery; + +import static org.openhab.core.thing.Thing.PROPERTY_SERIAL_NUMBER; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.jupnp.model.meta.DeviceDetails; +import org.jupnp.model.meta.ModelDetails; +import org.jupnp.model.meta.RemoteDevice; +import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; +import org.openhab.core.config.discovery.DiscoveryResult; +import org.openhab.core.config.discovery.DiscoveryResultBuilder; +import org.openhab.core.config.discovery.upnp.UpnpDiscoveryParticipant; +import org.openhab.core.config.discovery.upnp.internal.UpnpDiscoveryService; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.ThingUID; +import org.osgi.service.component.annotations.Component; + +/** + * The {@link SiemenesHvacDiscoveryParticipant} is responsible for discovering new and + * removed siemensHvac bridges. It uses the central {@link UpnpDiscoveryService}. + * + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault +@Component(service = UpnpDiscoveryParticipant.class) +public class SiemenesHvacDiscoveryParticipant implements UpnpDiscoveryParticipant { + + @Override + public Set getSupportedThingTypeUIDs() { + return Collections.singleton(SiemensHvacBindingConstants.THING_TYPE_BRIDGE); + } + + @Override + public @Nullable DiscoveryResult createResult(RemoteDevice device) { + ThingUID uid = getThingUID(device); + if (uid != null) { + Map properties = new HashMap<>(); + String ipAddress = device.getDetails().getPresentationURI().getHost(); + properties.put(SiemensHvacBindingConstants.IP_ADDRESS, ipAddress); + properties.put(SiemensHvacBindingConstants.BASE_URL, "https://" + ipAddress + "/"); + + String serialNumber = device.getDetails().getSerialNumber(); + DiscoveryResult result; + if (serialNumber != null && !serialNumber.isBlank()) { + properties.put(PROPERTY_SERIAL_NUMBER, serialNumber.toLowerCase()); + + result = DiscoveryResultBuilder.create(uid).withProperties(properties) + .withLabel(device.getDetails().getFriendlyName()) + .withRepresentationProperty(PROPERTY_SERIAL_NUMBER).build(); + } else { + result = DiscoveryResultBuilder.create(uid).withProperties(properties) + .withLabel(device.getDetails().getFriendlyName()).build(); + } + return result; + } else { + return null; + } + } + + @Override + public @Nullable ThingUID getThingUID(RemoteDevice device) { + DeviceDetails details = device.getDetails(); + if (details != null) { + ModelDetails modelDetails = details.getModelDetails(); + String serialNumber = details.getSerialNumber(); + if (modelDetails != null && serialNumber != null && !serialNumber.isBlank()) { + String modelName = modelDetails.getModelName(); + if (modelName != null) { + if (modelName.startsWith("Web Server OZW672.01")) { + return new ThingUID(SiemensHvacBindingConstants.THING_TYPE_OZW672, serialNumber.toLowerCase()); + } + } + } + } + return null; + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java new file mode 100644 index 0000000000000..b6328b8ed7fb3 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java @@ -0,0 +1,172 @@ + +/** + * Copyright (c) 2010-2020 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.siemenshvac.internal.discovery; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadata; +import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataMenu; +import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataRegistry; +import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; +import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeBaseThingHandler; +import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; +import org.openhab.core.config.discovery.AbstractDiscoveryService; +import org.openhab.core.config.discovery.DiscoveryResult; +import org.openhab.core.config.discovery.DiscoveryResultBuilder; +import org.openhab.core.config.discovery.DiscoveryService; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link SiemensHvacDeviceDiscoveryService} tracks for Siemens Hvac device connected to the bus. + * + * @author Laurent Arnal - Initial contribution + */ +// @Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.siemenshvac") +public class SiemensHvacDeviceDiscoveryService extends AbstractDiscoveryService + implements DiscoveryService, ThingHandlerService { + + private static final Logger logger = LoggerFactory.getLogger(SiemensHvacDeviceDiscoveryService.class); + + public static final Set SUPPORTED_THING_TYPES = Collections + .singleton(SiemensHvacBindingConstants.THING_TYPE_RVS41_813_327); + + private @Nullable SiemensHvacMetadataRegistry metadataRegistry; + private @Nullable SiemensHvacBridgeBaseThingHandler siemensHvacBridgeHandler; + private @Nullable SiemensHvacConnector hvacConnector; + + private static final int SEARCH_TIME = 10; + + public SiemensHvacDeviceDiscoveryService() { + super(SUPPORTED_THING_TYPES, SEARCH_TIME); + } + + @Reference + public void setSiemensHvacMetadataRegistry(SiemensHvacMetadataRegistry metadataRegistry) { + this.metadataRegistry = metadataRegistry; + } + + public void unsetSiemensHvacMetadataRegistry(SiemensHvacMetadataRegistry metadataRegistry) { + this.metadataRegistry = null; + } + + @Override + protected void startBackgroundDiscovery() { + } + + @Override + protected void stopBackgroundDiscovery() { + // can be overridden + } + + private @Nullable ThingUID getThingUID() { + if (siemensHvacBridgeHandler != null) { + ThingUID localBridgeUID = siemensHvacBridgeHandler.getThing().getUID(); + if (localBridgeUID != null) { + ThingTypeUID thingTypeUID = SiemensHvacBindingConstants.THING_TYPE_RVS41_813_327; + + if (thingTypeUID != null && getSupportedThingTypes().contains(thingTypeUID)) { + return new ThingUID(thingTypeUID, localBridgeUID, "-1"); + } + } + } + return null; + } + + @Override + public void startScan() { + logger.debug("call startScan()"); + + final SiemensHvacBridgeBaseThingHandler handler = siemensHvacBridgeHandler; + + if (metadataRegistry != null) { + metadataRegistry.ReadMeta(); + + SiemensHvacMetadataMenu rootMenu = metadataRegistry.getRoot(); + for (SiemensHvacMetadata child : rootMenu.getChilds().values()) { + if (child.getLongDesc().indexOf("OZW672") >= 0) { + continue; + } + + ThingUID thingUID = getThingUID(); + ThingUID bridgeUID = siemensHvacBridgeHandler.getThing().getUID(); + + if (thingUID != null) { + Map properties = new HashMap<>(1); + properties.put("CatId", child.getCatId()); + properties.put("GroupId", child.getGroupId()); + properties.put("ItemId", child.getMenuId()); + properties.put("LongDesc", child.getLongDesc()); + properties.put("ShortDesc", child.getShortDesc()); + + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties) + .withBridge(bridgeUID).withLabel(child.getLongDesc()).build(); + + thingDiscovered(discoveryResult); + } + + } + } + + } + + @Override + protected synchronized void stopScan() { + super.stopScan(); + } + + @Override + public void setThingHandler(@Nullable ThingHandler handler) { + if (handler instanceof SiemensHvacBridgeBaseThingHandler) { + siemensHvacBridgeHandler = (SiemensHvacBridgeBaseThingHandler) handler; + // bridgeUID = handler.getThing().getUID(); + } + + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return siemensHvacBridgeHandler; + } + + @Override + public void activate() { + final SiemensHvacBridgeBaseThingHandler handler = siemensHvacBridgeHandler; + if (handler != null) { + handler.registerDiscoveryListener(this); + } + + } + + @Override + public void deactivate() { + /* + * removeOlderResults(new Date().getTime(), bridgeUID); + * final HueBridgeHandler handler = hueBridgeHandler; + * if (handler != null) { + * handler.unregisterDiscoveryListener(); + * } + */ + } + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDiscoveryParticipant.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDiscoveryParticipant.java deleted file mode 100644 index e5232db3ad22f..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDiscoveryParticipant.java +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright (c) 2014-2016 by the respective copyright holders. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.openhab.binding.siemenshvac.internal.discovery; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import org.apache.commons.lang.StringUtils; -import org.eclipse.smarthome.config.discovery.DiscoveryResult; -import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; -import org.eclipse.smarthome.config.discovery.UpnpDiscoveryParticipant; -import org.eclipse.smarthome.core.thing.ThingTypeUID; -import org.eclipse.smarthome.core.thing.ThingUID; -import org.jupnp.model.meta.RemoteDevice; -import org.openhab.binding.siemenshvac.SiemensHvacBindingConstants; -import org.osgi.service.component.ComponentContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.collect.Collections2; - -/** - * An UpnpDiscoveryParticipant which allows to discover Pioneer AVRs. - * - * @author Laurent ARNAL - * - */ -public class SiemensHvacDiscoveryParticipant implements UpnpDiscoveryParticipant { - - private Logger logger = LoggerFactory.getLogger(SiemensHvacDiscoveryParticipant.class); - - private boolean isAutoDiscoveryEnabled; - private Set supportedThingTypes; - - public SiemensHvacDiscoveryParticipant() { - this.isAutoDiscoveryEnabled = true; - this.supportedThingTypes = SiemensHvacBindingConstants.SUPPORTED_THING_TYPES_UIDS; - } - - /** - * Called at the service activation. - * - * @param componentContext - */ - protected void activate(ComponentContext componentContext) { - if (componentContext.getProperties() != null) { - String autoDiscoveryPropertyValue = (String) componentContext.getProperties().get("enableAutoDiscovery"); - if (StringUtils.isNotEmpty(autoDiscoveryPropertyValue)) { - isAutoDiscoveryEnabled = Boolean.valueOf(autoDiscoveryPropertyValue); - } - } - supportedThingTypes = isAutoDiscoveryEnabled ? SiemensHvacBindingConstants.SUPPORTED_THING_TYPES_UIDS - : new HashSet(); - } - - @Override - public Set getSupportedThingTypeUIDs() { - return supportedThingTypes; - } - - @Override - public DiscoveryResult createResult(RemoteDevice device) { - DiscoveryResult result = null; - ThingUID thingUid = getThingUID(device); - if (thingUid != null) { - - String label = StringUtils.isEmpty(device.getDetails().getFriendlyName()) ? device.getDisplayString() - : device.getDetails().getFriendlyName(); - Map properties = new HashMap<>(2, 1); - properties.put(SiemensHvacBindingConstants.HOST_PARAMETER, - device.getIdentity().getDescriptorURL().getHost()); - properties.put(SiemensHvacBindingConstants.PROTOCOL_PARAMETER, - SiemensHvacBindingConstants.IP_PROTOCOL_NAME); - - result = DiscoveryResultBuilder.create(thingUid).withLabel(label).withProperties(properties).build(); - } - - return result; - } - - @Override - public ThingUID getThingUID(RemoteDevice device) { - ThingUID result = null; - if (isAutoDiscoveryEnabled) { - - if (StringUtils.containsIgnoreCase(device.getDetails().getManufacturerDetails().getManufacturer(), - SiemensHvacBindingConstants.MANUFACTURER)) { - logger.debug("Manufacturer matched: search: {}, device value: {}.", - SiemensHvacBindingConstants.MANUFACTURER, - device.getDetails().getManufacturerDetails().getManufacturer()); - if (StringUtils.containsIgnoreCase(device.getType().getType(), - SiemensHvacBindingConstants.UPNP_DEVICE_TYPE)) { - logger.debug("Device type matched: search: {}, device value: {}.", - SiemensHvacBindingConstants.UPNP_DEVICE_TYPE, device.getType().getType()); - - String deviceModel = device.getDetails().getModelDetails() != null - ? device.getDetails().getModelDetails().getModelName() : null; - ThingTypeUID thingTypeUID = SiemensHvacBindingConstants.HVAC_THING_TYPE; - if (!isSupportedDeviceModel(deviceModel)) { - logger.debug("Device model {} not supported. Odd behaviors may happen.", deviceModel); - thingTypeUID = SiemensHvacBindingConstants.HVAC_UNSUPPORTED_THING_TYPE; - } - - result = new ThingUID(thingTypeUID, device.getIdentity().getUdn().getIdentifierString()); - } - } - } - - return result; - } - - /** - * Return true only if the given device model is supported. - * - * @param deviceModel - * @return - */ - private boolean isSupportedDeviceModel(final String deviceModel) { - return StringUtils.isNotBlank(deviceModel) - && !Collections2.filter(SiemensHvacBindingConstants.SUPPORTED_DEVICE_MODELS, - new com.google.common.base.Predicate() { - @Override - public boolean apply(String input) { - return StringUtils.startsWithIgnoreCase(deviceModel, input); - } - }).isEmpty(); - } - -} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java new file mode 100644 index 0000000000000..2b563692ed4a3 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java @@ -0,0 +1,114 @@ +/** + * Copyright (c) 2010-2020 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.siemenshvac.internal.factory; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataRegistry; +import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; +import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacHandlerImpl; +import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacOZW672BridgeThingHandler; +import org.openhab.core.config.core.Configuration; +import org.openhab.core.io.net.http.HttpClientFactory; +import org.openhab.core.net.NetworkAddressService; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.ThingUID; +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.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link SiemensHvacHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Laurent ARNAL - Initial contribution + */ +@NonNullByDefault +@Component(service = ThingHandlerFactory.class, configurationPid = "binding.siemenshvac") +public class SiemensHvacHandlerFactory extends BaseThingHandlerFactory { + + private final Logger logger = LoggerFactory.getLogger(SiemensHvacHandlerFactory.class); + + private @Nullable NetworkAddressService networkAddressService; + private @Nullable HttpClientFactory httpClientFactory; + private @Nullable SiemensHvacMetadataRegistry metaDataRegistry; + + // + + @Activate + public SiemensHvacHandlerFactory(final @Reference HttpClientFactory httpClientFactory, + final @Reference SiemensHvacMetadataRegistry metaDataRegistry) { + this.httpClientFactory = httpClientFactory; + this.metaDataRegistry = metaDataRegistry; + } + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SiemensHvacBindingConstants.BINDING_ID.equals(thingTypeUID.getBindingId()); + } + + @Override + public @Nullable Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration, + @Nullable ThingUID thingUID, @Nullable ThingUID bridgeUID) { + + if (SiemensHvacBindingConstants.THING_TYPE_OZW672.equals(thingTypeUID)) { + ThingUID IPBridgeUID = getIPBridgeThingUID(thingTypeUID, thingUID, configuration); + return super.createThing(thingTypeUID, configuration, IPBridgeUID, null); + } else if (SiemensHvacBindingConstants.BINDING_ID.equals(thingTypeUID.getBindingId())) { + return super.createThing(thingTypeUID, configuration, thingUID, bridgeUID); + } + throw new IllegalArgumentException("The thing type " + thingTypeUID + " is not supported by the KNX binding."); + + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + if (thing.getThingTypeUID().equals(SiemensHvacBindingConstants.THING_TYPE_OZW672)) { + return new SiemensHvacOZW672BridgeThingHandler((Bridge) thing, networkAddressService, httpClientFactory, + metaDataRegistry); + } else if (SiemensHvacBindingConstants.BINDING_ID.equals(thing.getThingTypeUID().getBindingId())) { + SiemensHvacHandlerImpl handler = new SiemensHvacHandlerImpl(thing); + handler.setChannelTypeProvider(metaDataRegistry.getChannelTypeProvider()); + handler.setSiemensHvacConnector(metaDataRegistry.getSiemensHvacConnector()); + handler.setSiemensHvacMetadataRegistry(metaDataRegistry); + return handler; + } + return null; + } + + private ThingUID getIPBridgeThingUID(ThingTypeUID thingTypeUID, @Nullable ThingUID thingUID, + Configuration configuration) { + if (thingUID != null) { + return thingUID; + } + String ipAddress = (String) configuration.get(SiemensHvacBindingConstants.IP_ADDRESS); + return new ThingUID(thingTypeUID, ipAddress); + } + + @Reference + protected void setNetworkAddressService(NetworkAddressService networkAddressService) { + this.networkAddressService = networkAddressService; + } + + protected void unsetNetworkAddressService(NetworkAddressService networkAddressService) { + this.networkAddressService = null; + } + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java new file mode 100644 index 0000000000000..f87a8442bfad9 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2010-2020 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.siemenshvac.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataRegistry; +import org.openhab.binding.siemenshvac.internal.discovery.SiemensHvacDeviceDiscoveryService; +import org.openhab.core.io.net.http.HttpClientFactory; +import org.openhab.core.net.NetworkAddressService; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.binding.BaseBridgeHandler; +import org.openhab.core.types.Command; + +/** + * The {@link SiemensHvacBridgeBaseThingHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Laurent Arnal - Initial contribution and API + */ +@NonNullByDefault +public abstract class SiemensHvacBridgeBaseThingHandler extends BaseBridgeHandler { + + // protected ConcurrentHashMap destinations = new ConcurrentHashMap<>(); + // private final ScheduledExecutorService knxScheduler = ThreadPoolManager.getScheduledPool("knx"); + // private final ScheduledExecutorService backgroundScheduler = Executors.newSingleThreadScheduledExecutor(); + + private @Nullable SiemensHvacDeviceDiscoveryService discoveryService; + private @Nullable final NetworkAddressService networkAddressService; + private @Nullable final HttpClientFactory httpClientFactory; + private @Nullable final SiemensHvacMetadataRegistry metaDataRegistry; + + public SiemensHvacBridgeBaseThingHandler(Bridge bridge, @Nullable NetworkAddressService networkAddressService, + @Nullable HttpClientFactory httpClientFactory, @Nullable SiemensHvacMetadataRegistry metaDataRegistry) { + super(bridge); + this.networkAddressService = networkAddressService; + this.httpClientFactory = httpClientFactory; + this.metaDataRegistry = metaDataRegistry; + this.metaDataRegistry.getSiemensHvacConnector().setSiemensHvacBridgeBaseThingHandler(this); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + // Nothing to do here + } + + /* + * public ScheduledExecutorService getScheduler() { + * return knxScheduler; + * } + * + * public ScheduledExecutorService getBackgroundScheduler() { + * return backgroundScheduler; + * } + */ + + @Override + public void initialize() { + + if (metaDataRegistry != null) { + metaDataRegistry.ReadMeta(); + } + + } + + @Override + public void updateStatus(ThingStatus status) { + super.updateStatus(status); + } + + @Override + public void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) { + super.updateStatus(status, statusDetail, description); + } + + public boolean registerDiscoveryListener(SiemensHvacDeviceDiscoveryService listener) { + if (discoveryService == null) { + discoveryService = listener; + discoveryService.setSiemensHvacMetadataRegistry(metaDataRegistry); + // getFullLights().forEach(listener::addLightDiscovery); + return true; + } + + return false; + } + + public boolean unregisterDiscoveryListener() { + if (discoveryService != null) { + discoveryService = null; + return true; + } + + return false; + } + + public @Nullable HttpClientFactory getHttpClientFactory() { + return this.httpClientFactory; + } + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandler.java new file mode 100644 index 0000000000000..90adb9068496a --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandler.java @@ -0,0 +1,8 @@ +package org.openhab.binding.siemenshvac.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +@NonNullByDefault +public interface SiemensHvacHandler { + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java new file mode 100644 index 0000000000000..cea908971d930 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -0,0 +1,332 @@ +/** + * Copyright (c) 2010-2020 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.siemenshvac.internal.handler; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.ZoneId; +import java.time.ZonedDateTime; +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.siemenshvac.internal.Metadata.SiemensHvacMetadataDataPoint; +import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataRegistry; +import org.openhab.binding.siemenshvac.internal.config.SiemensHvacConfiguration; +import org.openhab.binding.siemenshvac.internal.network.SiemensHvacCallback; +import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; +import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelGroupTypeProvider; +import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelTypeProvider; +import org.openhab.binding.siemenshvac.internal.type.SiemensHvacConfigDescriptionProvider; +import org.openhab.binding.siemenshvac.internal.type.SiemensHvacThingTypeProvider; +import org.openhab.core.library.types.DateTimeType; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.StringType; +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.binding.BaseThingHandler; +import org.openhab.core.thing.type.ChannelType; +import org.openhab.core.types.Command; +import org.openhab.core.types.RefreshType; +import org.openhab.core.types.Type; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +/** + * The {@link SiemensHvacHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Laurent ARNAL - Initial contribution + */ +@Component(immediate = true) +@NonNullByDefault +public class SiemensHvacHandlerImpl extends BaseThingHandler implements SiemensHvacHandler { + + private final Logger logger = LoggerFactory.getLogger(SiemensHvacHandlerImpl.class); + + private @Nullable ScheduledFuture pollingJob = null; + + private @Nullable SiemensHvacConfiguration config; + private @Nullable SiemensHvacThingTypeProvider thingTypeProvider; + private @Nullable SiemensHvacChannelTypeProvider channelTypeProvider; + private @Nullable SiemensHvacChannelGroupTypeProvider channelGroupTypeProvider; + private @Nullable SiemensHvacConfigDescriptionProvider configDescriptionProvider; + private @Nullable SiemensHvacConnector hvacConnector; + private @Nullable SiemensHvacMetadataRegistry metaDataRegistry; + + public SiemensHvacHandlerImpl(Thing thing) { + super(thing); + + logger.debug("==========================================================="); + logger.debug("Siemens HVac"); + logger.debug("==========================================================="); + } + + @Reference + public void setSiemensHvacConnector(@Nullable SiemensHvacConnector hvacConnector) { + this.hvacConnector = hvacConnector; + } + + public void unsetSiemensHvacConnector(SiemensHvacConnector hvacConnector) { + this.hvacConnector = null; + } + + @Reference + public void setSiemensHvacMetadataRegistry(@Nullable SiemensHvacMetadataRegistry metaDataRegistry) { + this.metaDataRegistry = metaDataRegistry; + } + + public void unsetSiemensHvacMetadataRegistry(SiemensHvacMetadataRegistry metaDataRegistry) { + this.metaDataRegistry = null; + } + + @Reference + public void setChannelTypeProvider(@Nullable SiemensHvacChannelTypeProvider channelTypeProvider) { + this.channelTypeProvider = channelTypeProvider; + } + + public void unsetChannelTypeProvider(@Nullable SiemensHvacChannelTypeProvider channelTypeProvider) { + this.channelTypeProvider = null; + } + + @Override + public void initialize() { + + updateStatus(ThingStatus.UNKNOWN); + + scheduler.execute(() -> { + boolean thingReachable = true; + if (thingReachable) { + updateStatus(ThingStatus.ONLINE); + } else { + updateStatus(ThingStatus.OFFLINE); + } + }); + + config = getConfigAs(SiemensHvacConfiguration.class); + var c1 = getThing().getConfiguration(); + var c2 = getBridge().getConfiguration(); + + pollingJob = scheduler.scheduleWithFixedDelay(this::pollingCode, 0, 10, TimeUnit.SECONDS); + } + + @Override + public void dispose() { + pollingJob.cancel(true); + } + + private void pollingCode() { + + var chList = this.getThing().getChannels(); + for (Channel ch : chList) { + logger.debug(ch.getDescription()); + + boolean isLink = this.getCallback().isChannelLinked(ch.getUID()); + + if (!isLink) { + continue; + } + + if (channelTypeProvider == null) { + return; + } + + ChannelType tp = channelTypeProvider.getInternalChannelType(ch.getChannelTypeUID()); + + String dptId = ch.getProperties().get("dptId"); + String groupId = ch.getProperties().get("groupdId"); + String label = ch.getLabel(); + String uid = ch.getUID().getId(); + String type = tp.getItemType(); + + ReadDp(dptId, uid, type, false); + logger.debug("" + isLink); + } + } + + public void DecodeReadDp(JsonObject response, @Nullable String uid, @Nullable String dp, @Nullable String type) { + if (response != null && response.has("Data")) { + JsonObject subResult = (JsonObject) response.get("Data"); + + String updateKey = "" + uid; + String typer = ""; + JsonElement value = null; + JsonElement enumValue = null; + String result = ""; + String unit = ""; + + if (subResult.has("Type")) { + typer = subResult.get("Type").getAsString().trim(); + } + if (subResult.has("Value")) { + value = subResult.get("Value"); + } + if (subResult.has("EnumValue")) { + enumValue = subResult.get("EnumValue"); + } + if (subResult.has("Unit")) { + unit = subResult.get("Unit").toString().trim(); + } + + if (value == null) { + return; + } + + if (type == null) { + logger.debug("siemensHvac:ReadDP:null type" + dp); + } + if (typer == null) { + logger.debug("siemensHvac:ReadDP:null typer" + dp); + } + + if (typer.equals("Numeric")) { + updateState(updateKey, new DecimalType(value.getAsDouble())); + } else if (typer.equals("Enumeration")) { + updateState(updateKey, new DecimalType(enumValue.getAsInt())); + } else if (typer.equals("Text")) { + updateState(updateKey, new StringType(value.getAsString())); + } else if (typer.equals("RadioButton")) { + updateState(updateKey, new StringType(value.getAsString())); + } else if (typer.equals("DayOfTime") || typer.equals("DateTime")) { + try { + SimpleDateFormat dtf = new SimpleDateFormat("EEEE, d. MMMM yyyy hh:mm"); // first example + ZonedDateTime zdt = dtf.parse(value.getAsString()).toInstant().atZone(ZoneId.systemDefault()); + updateState(updateKey, new DateTimeType(zdt)); + } catch (ParseException ex) { + logger.debug("error decoding date!"); + } + } else { + updateState(updateKey, new StringType(value.getAsString())); + } + + } + } + + private void ReadDp(@Nullable String dp, String uid, @Nullable String type, boolean async) { + if (dp.equals("-1")) { + return; + } + + try { + String request = "api/menutree/read_datapoint.json?Id=" + dp; + + // logger.debug("siemensHvac:ReadDp:DoRequest():" + request); + + if (async) { + hvacConnector.DoRequest(request, new SiemensHvacCallback() { + + @Override + public void execute(java.net.URI uri, int status, @Nullable Object response) { + if (response instanceof JsonObject) { + DecodeReadDp((JsonObject) response, uid, dp, type); + } + } + + }); + } else { + JsonObject js = hvacConnector.DoRequest(request, null); + DecodeReadDp(js, uid, dp, type); + } + + } catch (Exception e) { + logger.error("siemensHvac:ReadDp:Error during dp reading: " + dp + " ; " + e.getLocalizedMessage()); + // Reset sessionId so we redone _auth on error + } + } + + private void WriteDp(@Nullable String dp, Type dpVal, @Nullable String type) { + if (dp.equals("-1")) { + return; + } + + try { + String valUpdate = "0"; + String valUpdateEnum = ""; + String valUpdateLabel = ""; + + if (dpVal instanceof PercentType) { + PercentType pct = (PercentType) dpVal; + valUpdate = pct.toString(); + } else if (dpVal instanceof DecimalType) { + DecimalType bdc = (DecimalType) dpVal; + valUpdate = bdc.toString(); + } else if (dpVal instanceof StringType) { + StringType bdc = (StringType) dpVal; + valUpdate = bdc.toString(); + + if (type.equals("Enumeration")) { + String[] valuesUpdateDp = valUpdate.split(":"); + valUpdateEnum = valuesUpdateDp[0]; + valUpdateLabel = valuesUpdateDp[1]; + + // For enumeration, we always update using the raw value + valUpdate = valUpdateEnum; + } + } + + SiemensHvacMetadataDataPoint md = (SiemensHvacMetadataDataPoint) metaDataRegistry.getDptMap(dp); + String dptType = md.getDptType(); + + String request = "api/menutree/write_datapoint.json?Id=" + dp + "&Value=" + valUpdate + "&Type=" + dptType; + + // logger.debug("siemensHvac:ReadDp:DoRequest():" + request); + + hvacConnector.DoRequest(request, new SiemensHvacCallback() { + + @Override + public void execute(java.net.URI uri, int status, @Nullable Object response) { + if (response instanceof JsonObject) { + logger.debug("p1"); + } + } + + }); + + } catch (Exception e) { + logger.error("siemensHvac:ReadDp:Error during dp reading: " + dp + " ; " + e.getLocalizedMessage()); + // Reset sessionId so we redone _auth on error + } + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + logger.debug("handleCommand"); + if (command instanceof RefreshType) { + logger.debug("handleCommandRefresh"); + } else { + + Channel channel = getThing().getChannel(channelUID); + + ChannelType tp = channelTypeProvider.getInternalChannelType(channel.getChannelTypeUID()); + + String dptId = channel.getProperties().get("dptId"); + String groupId = channel.getProperties().get("groupdId"); + String label = channel.getLabel(); + String uid = channel.getUID().getId(); + String type = tp.getItemType(); + + WriteDp(dptId, command, type); + ReadDp(dptId, uid, type, false); + } + } + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java new file mode 100644 index 0000000000000..4cf9d9d7d64c7 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2010-2020 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.siemenshvac.internal.handler; + +import java.util.Collection; +import java.util.Collections; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataRegistry; +import org.openhab.binding.siemenshvac.internal.discovery.SiemensHvacDeviceDiscoveryService; +import org.openhab.core.io.net.http.HttpClientFactory; +import org.openhab.core.net.NetworkAddressService; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.osgi.service.component.annotations.Activate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link IPBridgeThingHandler} is responsible for handling commands, which are + * sent to one of the channels. It implements a KNX/IP Gateway, that either acts a a + * conduit for other {@link DeviceThingHandler}s, or for Channels that are + * directly defined on the bridge + * + * @author Karel Goderis - Initial contribution + * @author Simon Kaufmann - Refactoring & cleanup + */ +@NonNullByDefault +public class SiemensHvacOZW672BridgeThingHandler extends SiemensHvacBridgeBaseThingHandler { + + private final Logger logger = LoggerFactory.getLogger(SiemensHvacOZW672BridgeThingHandler.class); + + @Activate + public SiemensHvacOZW672BridgeThingHandler(Bridge bridge, @Nullable NetworkAddressService networkAddressService, + @Nullable HttpClientFactory httpClientFactory, @Nullable SiemensHvacMetadataRegistry metaDataRegistry) { + super(bridge, networkAddressService, httpClientFactory, metaDataRegistry); + } + + @Override + public void initialize() { + logger.debug("Initialize() bridge"); + + super.initialize(); + updateStatus(ThingStatus.ONLINE); + + } + + @Override + public void dispose() { + super.dispose(); + /* + * if (client != null) { + * client.dispose(); + * client = null; + * } + */ + } + /* + * @Override + * protected KNXClient getClient() { + * KNXClient ret = client; + * if (ret == null) { + * return new NoOpClient(); + * } + * return ret; + * } + */ + + @Override + public Collection> getServices() { + return Collections.singleton(SiemensHvacDeviceDiscoveryService.class); + } + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacCallback.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacCallback.java new file mode 100644 index 0000000000000..3035303a2ec82 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacCallback.java @@ -0,0 +1,15 @@ +package org.openhab.binding.siemenshvac.internal.network; + +import java.net.URI; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +@NonNullByDefault +public interface SiemensHvacCallback { + /** + * Runs callback code after response completion. + */ + void execute(URI uri, int status, @Nullable Object response); + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java new file mode 100644 index 0000000000000..42bc5adbe4480 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java @@ -0,0 +1,19 @@ +package org.openhab.binding.siemenshvac.internal.network; + +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.api.Request; +import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeBaseThingHandler; + +import com.google.gson.JsonObject; + +public interface SiemensHvacConnector { + + public JsonObject DoRequest(@Nullable String req, @Nullable SiemensHvacCallback callback); + + public void WaitAllPendingRequest(); + + public void onComplete(Request request); + + public void setSiemensHvacBridgeBaseThingHandler( + @Nullable SiemensHvacBridgeBaseThingHandler hvacBridgeBaseThingHandler); +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java new file mode 100644 index 0000000000000..73a99ad6a075f --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -0,0 +1,456 @@ +package org.openhab.binding.siemenshvac.internal.network; + +import java.util.Date; +import java.util.Hashtable; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.openhab.binding.siemenshvac.internal.Metadata.RuntimeTypeAdapterFactory; +import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadata; +import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataDataPoint; +import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataMenu; +import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeBaseThingHandler; +import org.openhab.core.config.core.Configuration; +import org.openhab.core.io.net.http.HttpClientFactory; +import org.openhab.core.types.Type; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +@Component(immediate = true) +@NonNullByDefault +public class SiemensHvacConnectorImpl implements SiemensHvacConnector { + + private static final Logger logger = LoggerFactory.getLogger(SiemensHvacConnectorImpl.class); + + private @Nullable String sessionId = null; + private String baseUrl = ""; + private String userName = ""; + private String userPassword = ""; + private @Nullable Date lastUpdate; + + protected final HttpClientFactory httpClientFactory; + + protected @Nullable HttpClient httpClient; + protected @Nullable HttpClient httpClientInsecure; + + private static int startedRequest = 0; + private static int completedRequest = 0; + private Lock lockObj = new ReentrantLock(); + + private Map updateCommand; + + private @Nullable SiemensHvacBridgeBaseThingHandler hvacBridgeBaseThingHandler; + + @Activate + public SiemensHvacConnectorImpl(@Reference HttpClientFactory httpClientFactory) { + this.updateCommand = new Hashtable(); + this.httpClientFactory = httpClientFactory; + + initHttpClient(); + + } + + private void initHttpClient() { + + this.httpClient = httpClientFactory.getCommonHttpClient(); + this.httpClientInsecure = new HttpClient(new SslContextFactory.Client(true)); + this.httpClientInsecure.setRemoveIdleDestinations(true); + this.httpClientInsecure.setMaxConnectionsPerDestination(15); + try { + this.httpClientInsecure.start(); + } catch (Exception e) { + // catching exception is necessary due to the signature of HttpClient.start() + logger.warn("Failed to start insecure http client: {}", e.getMessage()); + } + + } + + @Override + public void setSiemensHvacBridgeBaseThingHandler( + @Nullable SiemensHvacBridgeBaseThingHandler hvacBridgeBaseThingHandler) { + this.hvacBridgeBaseThingHandler = hvacBridgeBaseThingHandler; + } + + public void unsetSiemensHvacBridgeBaseThingHandler(SiemensHvacBridgeBaseThingHandler hvacBridgeBaseThingHandler) { + this.hvacBridgeBaseThingHandler = null; + } + + public void setBaseUrl(String baseUrl) { + this.baseUrl = baseUrl; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public void setUserPassword(String userPassword) { + this.userPassword = userPassword; + } + + @Override + public void onComplete(@Nullable Request request) { + lockObj.lock(); + try { + completedRequest++; + } finally { + lockObj.unlock(); + } + logger.debug("unregisterCount:" + completedRequest + " " + request.getURI()); + } + + private @Nullable ContentResponse executeRequest(final Request request, @Nullable SiemensHvacCallback callback) + throws Exception { + request.timeout(60, TimeUnit.SECONDS); + + ContentResponse response = null; + + @Nullable + SiemensHvacRequestListener requestListener = null; + if (callback != null) { + requestListener = new SiemensHvacRequestListener(callback, this); + request.onResponseSuccess(requestListener); + request.onResponseFailure(requestListener); + } + + try { + if (requestListener != null) { + lockObj.lock(); + try { + startedRequest++; + } finally { + lockObj.unlock(); + } + + logger.debug("registerCount:" + startedRequest + " " + request.getQuery()); + request.send(requestListener); + } else { + response = request.send(); + } + } catch (InterruptedException | TimeoutException | ExecutionException e) { + throw new Exception("siemensHvac:Exception by executing request: " + request.getQuery() + " ; " + + e.getLocalizedMessage()); + } + return response; + } + + private void _initConfig() { + Configuration config = this.hvacBridgeBaseThingHandler.getThing().getConfiguration(); + if (config.containsKey("baseUrl")) { + baseUrl = (String) config.get("baseUrl"); + } + if (config.containsKey("userName")) { + userName = (String) config.get("userName"); + } + if (config.containsKey("userPassword")) { + userPassword = (String) config.get("userPassword"); + } + } + + private void _doAuth() { + logger.debug("siemensHvac:doAuth()"); + + _initConfig(); + String baseUri = baseUrl; + String uri = "api/auth/login.json?user=" + userName + "&pwd=" + userPassword; + final Request request = httpClientInsecure.newRequest(baseUri + uri); + request.method(HttpMethod.GET); + + logger.debug("siemensHvac:doAuth:connect()"); + + try { + ContentResponse response = executeRequest(request, null); + int statusCode = response.getStatus(); + + logger.debug("siemensHvac:doAuth:Endresponse:()" + statusCode); + + if (statusCode == HttpStatus.OK_200) { + String result = response.getContentAsString(); + + if (result != null) { + logger.debug("siemensHvac:doAuth:decodeResponse:()" + result); + JsonObject resultObj = getGson().fromJson(result, JsonObject.class); + + if (resultObj.has("Result")) { + JsonElement resultVal = resultObj.get("Result"); + JsonObject resultObj2 = resultVal.getAsJsonObject(); + + if (resultObj2.has("Success")) { + boolean successVal = resultObj2.get("Success").getAsBoolean(); + + if (successVal) { + + if (resultObj.has("SessionId")) { + sessionId = resultObj.get("SessionId").getAsString(); + logger.debug("Have new SessionId : " + sessionId); + } + + } + + } + } + + logger.debug("siemensHvac:doAuth:decodeResponse:()"); + + } + + if (sessionId == null) { + logger.debug("Session request auth was unsucessfull in _doAuth()"); + } + } + + logger.debug("siemensHvac:doAuth:connect()"); + + } catch (Exception ex) { + logger.debug("siemensHvac:doAuth:error()" + ex.getLocalizedMessage()); + } finally { + } + } + + public @Nullable String DoBasicRequest(@Nullable String uri) { + return DoBasicRequest(uri, null); + } + + public @Nullable String DoBasicRequestAsync(@Nullable String uri, @Nullable SiemensHvacCallback callback) { + return DoBasicRequest(uri, callback); + } + + public @Nullable String DoBasicRequest(@Nullable String uri, @Nullable SiemensHvacCallback callback) { + if (sessionId == null) { + _doAuth(); + } + + try { + String baseUri = baseUrl; + if (!uri.endsWith("?")) { + uri = uri + "&"; + } + uri = uri + "SessionId=" + sessionId; + uri = uri + "&user=" + userName + "&pwd=" + userPassword; + + final Request request = httpClientInsecure.newRequest(baseUri + uri); + request.method(HttpMethod.GET); + + logger.debug("siemensHvac:DoBasicRequest()" + request); + + ContentResponse response = executeRequest(request, callback); + if (callback == null) { + int statusCode = response.getStatus(); + + if (statusCode == HttpStatus.OK_200) { + String result = response.getContentAsString(); + + return result; + } + } + } catch (Exception ex) { + logger.error( + "siemensHvac:DoRequest:Exception by executing Request: " + uri + " ; " + ex.getLocalizedMessage()); + } finally { + } + + return null; + } + + @Override + public @Nullable JsonObject DoRequest(@Nullable String req, @Nullable SiemensHvacCallback callback) { + try { + String response = DoBasicRequest(req, callback); + + if (response != null) { + // logger.debug("siemensHvacDoRequest:responseSt:" + response); + + JsonObject resultObj = getGson().fromJson(response, JsonObject.class); + + if (resultObj.has("Result")) { + JsonObject subResultObj = resultObj.getAsJsonObject("Result"); + + if (subResultObj.has("Success")) { + boolean result = subResultObj.get("Success").getAsBoolean(); + if (result) { + return resultObj; + } + } + + } + + return null; + } + } catch (Exception e) { + logger.error("siemensHvac:DoRequest:Exception by executing jsonRequest: " + req + " ; " + + e.getLocalizedMessage()); + } + + return null; + } + + @Override + public void WaitAllPendingRequest() { + logger.debug("WaitAllPendingRequest:start"); + try { + logger.debug("WaitAllPendingRequest:start Initial Sleep"); + Thread.sleep(1000); + logger.debug("WaitAllPendingRequest:end Initial Sleep"); + + logger.debug("WaitAllPendingRequest:start Wait Request"); + boolean allRequestDone = false; + + while (!allRequestDone) { + int idx = 0; + + allRequestDone = true; + while (idx < 5 && allRequestDone) { + logger.debug("WaitAllPendingRequest:waitAllRequestDone " + idx + " " + startedRequest + "/" + + completedRequest); + if (startedRequest != completedRequest) { + allRequestDone = false; + } + Thread.sleep(1000); + idx++; + } + } + + logger.debug("WaitAllPendingRequest:end Wait"); + + } catch (InterruptedException ex) { + logger.debug("WaitAllPendingRequest:interrupted in WaitAllRequest"); + } + + logger.debug("WaitAllPendingRequest:end Wait"); + logger.debug("WaitAllPendingRequest:end WaitAllPendingRequest"); + } + + public static Gson getGson() { + GsonBuilder builder = new GsonBuilder(); + Gson gson = builder.setPrettyPrinting().create(); + return gson; + } + + public static Gson getGsonWithAdapter() { + RuntimeTypeAdapterFactory adapter = RuntimeTypeAdapterFactory + .of(SiemensHvacMetadata.class); + adapter.registerSubtype(SiemensHvacMetadataMenu.class); + adapter.registerSubtype(SiemensHvacMetadataDataPoint.class); + + Gson gson = new GsonBuilder().setPrettyPrinting().registerTypeAdapterFactory(adapter).create(); + return gson; + } + + /* + * + * + * public void WriteDp(String name, Type dp) { + * siemensHvacGenericBindingProvider provider = hvacBinding.getProvider(); + * siemensHvacBindingConfig bindingConfig = (siemensHvacBindingConfig) provider.getBindingConfigs().get(name); + * registry.VerifyBindingConfig(bindingConfig); + * int dpt = bindingConfig.getDptId(); + * String type = bindingConfig.getDptType(); + * + * String valAct = _readDpInternal(name, dpt, type); + * String valActEnum = valAct; + * String valActLabel = valAct; + * + * + * if (type.equals("Enumeration")) { + * String[] values = valAct.split(":"); + * valActEnum = values[0]; + * valActLabel = values[1]; + * } + * + * + * // Exit there if new value is the same as old value + * if (valAct != null && valUpdate.equals(valAct)) { + * return; + * } + * if (valActEnum != null && valUpdateEnum.equals(valActEnum)) { + * return; + * } + * + * siemensHvacBindingConfig config = (siemensHvacBindingConfig) provider.getBindingConfigs().get(name); + * registry.VerifyBindingConfig(bindingConfig); + * String dpType = config.getDptType(); + * + * String request = "api/menutree/write_datapoint.json?Id=" + dpt + "&Value=" + valUpdate + "&Type=" + dpType; + * + * JSONObject result = DoRequest(request); + * logger.debug("siemensHvac:WriteDP(response) = " + result); + * + * ReadDp(name, dpt, type); + * } + * + * + * + */ + + public void AddDpUpdate(String itemName, Type dp) { + synchronized (updateCommand) { + updateCommand.put(itemName, dp); + lastUpdate = new java.util.Date(); + } + } + + /* + * @Override + * public void run() { + * logger.debug("siemensHvac:sender thread start"); + * + * // as long as no interrupt is requested, continue running + * while (!interrupted) { + * try { + * Thread.sleep(2000); + * Date currentDate = new java.util.Date(); + * + * logger.debug("siemensHvac:sender thread alive:" + currentDate); + * + * if (lastUpdate == null) { + * continue; + * } + * + * long ms = currentDate.getTime() - lastUpdate.getTime(); + * if (ms < 3000) { + * continue; + * } + * + * synchronized (updateCommand) { + * if (updateCommand.isEmpty()) { + * continue; + * } + * + * logger.debug("siemensHvac:sender thread updateCommand"); + * + * for (String key : updateCommand.keySet()) { + * Type dp = updateCommand.get(key); + * WriteDp(key, dp); + * + * } + * + * updateCommand.clear(); + * } + * + * } catch (Exception e) { + * logger.error("siemensHvac:Error occured will sending update values", e); + * } + * } + * + * } + */ +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java new file mode 100644 index 0000000000000..15707c374ab46 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java @@ -0,0 +1,101 @@ +package org.openhab.binding.siemenshvac.internal.network; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jetty.client.api.Response; +import org.eclipse.jetty.client.api.Response.CompleteListener; +import org.eclipse.jetty.client.api.Response.ContentListener; +import org.eclipse.jetty.client.api.Response.FailureListener; +import org.eclipse.jetty.client.api.Response.SuccessListener; +import org.eclipse.jetty.client.api.Result; +import org.eclipse.jetty.client.util.BufferingResponseListener; +import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataRegistryImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; + +public class SiemensHvacRequestListener extends BufferingResponseListener + implements SuccessListener, FailureListener, ContentListener, CompleteListener { + + private static final Logger logger = LoggerFactory.getLogger(SiemensHvacMetadataRegistryImpl.class); + private SiemensHvacConnector hvacConnector; + + /** + * Callback to execute on complete response + */ + private final SiemensHvacCallback callback; + + /** + * Constructor + * + * @param callback Callback which execute method has to be called. + */ + public SiemensHvacRequestListener(SiemensHvacCallback callback, SiemensHvacConnector hvacConnector) { + this.callback = callback; + this.hvacConnector = hvacConnector; + } + + @Override + public void onSuccess(@NonNullByDefault({}) Response response) { + // logger.debug("{} response: {}", response.getRequest().getURI(), response.getStatus()); + } + + @Override + public void onFailure(@NonNullByDefault({}) Response response, @NonNullByDefault({}) Throwable failure) { + logger.debug("response failed: {} {}", response.getRequest().getURI(), failure.getLocalizedMessage(), failure); + } + + @Override + public void onComplete(@NonNullByDefault({}) Result result) { + hvacConnector.onComplete(result.getRequest()); + + try { + String content = getContentAsString(); + logger.trace("response complete: {}", content); + + if (result.getResponse().getStatus() != 200) { + logger.debug("bad gateway !!!"); + return; + } + + if (content != null) { + JsonObject resultObj = null; + try { + Gson gson = SiemensHvacConnectorImpl.getGson(); + resultObj = gson.fromJson(content, JsonObject.class); + } catch (Exception ex) { + logger.debug("error:" + ex.toString()); + } + + if (resultObj.has("Result")) { + JsonObject subResultObj = resultObj.getAsJsonObject("Result"); + + if (subResultObj.has("Success")) { + boolean resultVal = subResultObj.get("Success").getAsBoolean(); + + if (resultVal) { + + callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), resultObj); + return; + } else { + logger.debug("error : " + subResultObj); + } + } else { + logger.debug("error"); + } + + } else { + logger.debug("error"); + } + + return; + } + + callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), content); + } catch (Exception ex) { + logger.debug("error"); + } + } + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacConnector.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacConnector.java deleted file mode 100644 index d9e6f25281159..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacConnector.java +++ /dev/null @@ -1,481 +0,0 @@ -package org.openhab.binding.siemenshvac.internal; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.SocketTimeoutException; -import java.net.URL; -import java.util.Date; - -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class siemensHvacConnector extends Thread { - - private static final Logger logger = LoggerFactory.getLogger(siemensHvacConnector.class); - - private String sessionId = null; - private String baseUrl = ""; - private String userName = ""; - private String userPassword = ""; - private Date lastUpdate; - - // private Map updateCommand; - - private boolean interrupted = false; - - public siemensHvacConnector(String host, String userName, String userPassword) { - this.baseUrl = "http://" + host + "/"; - this.userName = userName; - this.userPassword = userPassword; - - readDpTree(); - // this.updateCommand = new Hashtable(); - start(); - } - - private void readDpTree() { - logger.debug("siemensHvac:listDP():"); - readDpTree(null, 0, 1); - } - - private void readDpTree(String parentId, int level, int maxLevel) { - logger.debug("siemensHvac:listDP():" + parentId); - - if (level >= maxLevel) { - return; - } - - try { - String request = "api/menutree/list.json?"; - if (parentId != null) { - request = request + "Id=" + parentId; - } - - JSONObject result = DoRequest(request); - - if (result != null) { - if (result.containsKey("MenuItems")) { - JSONArray menuItems = (JSONArray) result.get("MenuItems"); - - for (Object item : menuItems) { - - JSONObject jItem = (JSONObject) item; - String Id = jItem.get("Id").toString().trim(); - JSONObject Text = (JSONObject) jItem.get("Text"); - - String longLabel = Text.get("Long").toString().trim(); - String shortLabel = Text.get("Short").toString().trim(); - String catId = Text.get("CatId").toString().trim(); - String groupdId = Text.get("GroupId").toString().trim(); - String textId = Text.get("Id").toString().trim(); - - System.out.println( - String.format("Id : %s ShortLabel: %s LongLabel : %s", Id, shortLabel, longLabel)); - - readDpTree(Id, level + 1, maxLevel); - - } - } - if (result.containsKey("DatapointItems")) { - JSONArray dataPtItems = (JSONArray) result.get("DatapointItems"); - - for (Object item : dataPtItems) { - - JSONObject dItem = (JSONObject) item; - String Id = dItem.get("Id").toString().trim(); - String Address = dItem.get("Address").toString().trim(); - String DpSubKey = dItem.get("DpSubKey").toString().trim(); - String WriteAccess = dItem.get("WriteAccess").toString().trim(); - - JSONObject Text = (JSONObject) dItem.get("Text"); - - String longLabel = Text.get("Long").toString().trim(); - String shortLabel = Text.get("Short").toString().trim(); - String catId = Text.get("CatId").toString().trim(); - String groupdId = Text.get("GroupId").toString().trim(); - String textId = Text.get("Id").toString().trim(); - - System.out.println(String.format("Id : %s Address: %s ShortLabel: %s LongLabel : %s", Id, - Address, shortLabel, longLabel)); - } - } - } - - } catch (Exception e) { - logger.error("siemensHvac:ReadDp:Error during dp reading: " + e.getLocalizedMessage()); - // Reset sessionId so we redone _auth on error - sessionId = null; - } - - } - - private void doAuth() { - - HttpURLConnection connection = null; - logger.debug("siemensHvac:doAuth()"); - - String request = "api/auth/login.json?user=" + userName + "&pwd=" + userPassword; - StringBuilder response = new StringBuilder(); - BufferedReader in = null; - - try { - URL url = new URL(baseUrl + request); - logger.debug("siemensHvac:doAuth:openCnx()"); - connection = (HttpURLConnection) url.openConnection(); - - int responseCode = -1; - if (connection != null) { - connection.setConnectTimeout(60000); - connection.setReadTimeout(60000); - - try { - logger.debug("siemensHvac:doAuth:connect()"); - connection.connect(); - responseCode = connection.getResponseCode(); - } catch (SocketTimeoutException e) { - logger.error("siemensHvac:doAuth:" + e.getMessage() + " : " + request); - return; - } - - logger.debug("siemensHvac:doAuth:response()" + responseCode); - if (responseCode == HttpURLConnection.HTTP_OK) { - String inputLine = null; - in = new BufferedReader(new InputStreamReader(connection.getInputStream())); - - while ((inputLine = in.readLine()) != null) { - response.append(inputLine); - logger.debug("siemensHvac:doAuthaddline:()" + inputLine); - } - - in.close(); - } else { - response = null; - } - logger.debug("siemensHvac:doAuth:Endresponse:()" + responseCode); - } - - if (response != null) { - String st = response.toString(); - logger.debug("siemensHvac:doAuth:decodeResponse:()" + st); - JSONObject result = JSONResponseHandler.toJSONObject(st); - logger.debug("siemensHvac:doAuth:afterJson"); - if (result.containsKey("Result")) { - JSONObject subResult = (JSONObject) result.get("Result"); - if (subResult.containsKey("Success")) { - if (subResult.get("Success").equals("true")) { - if (result.containsKey("SessionId")) { - sessionId = result.get("SessionId").toString(); - } - } - - } - - } - } - } catch (MalformedURLException e) { - logger.error("siemensHvac:MalformedURLException by executing jsonRequest: " + request + " ; " - + e.getLocalizedMessage()); - } catch (IOException e) { - logger.error( - "siemensHvac:IOException by executing jsonRequest: " + request + " ; " + e.getLocalizedMessage()); - } catch (Exception e) { - logger.error("siemensHvac:Error during auth: " + e.getLocalizedMessage()); - } finally { - if (connection != null) { - connection.disconnect(); - } - } - - } - - public void ReadAllDp() { - /* - * siemensHvacGenericBindingProvider provider = hvacBinding.getProvider(); - * logger.debug("siemensHvac:readAllDP():begin"); - * - * Iterator it = provider.getBindingConfigs().values().iterator(); - * - * while (it.hasNext()) { - * siemensHvacBindingConfig config = (siemensHvacBindingConfig) it.next(); - * String name = config.getName(); - * String type = config.getType(); - * String dp = config.getDpt(); - * - * ReadDp(name, dp, type); - * } - * logger.debug("siemensHvac:readAllDP():end"); - */ - } - - public String ReadDp(String name, String dp, String type) { - String value = _readDpInternal(name, dp, type); - - logger.debug("siemensHvac:ReadDP:" + name + ":" + dp + ":" + value); - - if (value == null || value.equals("----") || value.equals("")) { - return null; - } - - if (type.equals("Numeric")) { - // hvacBinding.getEventPublisher().postUpdate(name, new DecimalType(value)); - } else if (type.equals("Enumeration")) { - String valueEnum = value; - if (type.equals("Enumeration")) { - String[] values = value.split(":"); - valueEnum = values[0]; - value = values[1]; - } - - // hvacBinding.getEventPublisher().postUpdate(name, new StringType(valueEnum)); - } else if (type.equals("Text")) { - // hvacBinding.getEventPublisher().postUpdate(name, new StringType(value)); - } else { - // hvacBinding.getEventPublisher().postUpdate(name, new StringType(value)); - } - - return value; - } - - /* - * public void WriteDp(String name, Type dp) { - * if (sessionId == null) { - * _doAuth(); - * } - * - * - * siemensHvacGenericBindingProvider provider = hvacBinding.getProvider(); - * - * siemensHvacBindingConfig bindingConfig = (siemensHvacBindingConfig) provider.getBindingConfigs().get(name); - * String dpt = bindingConfig.getDpt(); - * String type = bindingConfig.getType(); - * - * String valAct = _readDpInternal(name, dpt, type); - * String valActEnum = valAct; - * - * if (type.equals("Enumeration")) { - * String[] values = valAct.split(":"); - * valActEnum = values[0]; - * valAct = values[1]; - * } - * String val = ""; - * if (dp instanceof PercentType) { - * PercentType pct = (PercentType) dp; - * val = pct.toString(); - * } else if (dp instanceof DecimalType) { - * DecimalType bdc = (DecimalType) dp; - * val = bdc.toString(); - * } else if (dp instanceof StringType) { - * StringType bdc = (StringType) dp; - * val = bdc.toString(); - * } - * - * if (valAct != null && val.equals(valAct)) { - * return; - * } - * if (valActEnum != null && val.equals(valActEnum)) { - * return; - * } - * - * siemensHvacBindingConfig config = (siemensHvacBindingConfig) provider.getBindingConfigs().get(name); - * String dpType = config.getType(); - * - * String request = "api/menutree/write_datapoint.json?SessionId=" + sessionId + "&Id=" + dpt + "&Value=" + val - * + "&Type=" + dpType; - * - * String response = DoRequest(request); - * logger.debug("siemensHvac:WriteDP(response) = " + response); - * - * ReadDp(name, dpt, type); - * - * } - */ - public JSONObject DoRequest(String request) { - HttpURLConnection connection = null; - StringBuilder response = new StringBuilder(); - BufferedReader in = null; - - if (sessionId == null) { - doAuth(); - } - - request = request + "&SessionId=" + sessionId; - - logger.debug("siemensHvac:ReadDp:DoRequest():" + request); - - try { - URL url = new URL(baseUrl + request); - - connection = (HttpURLConnection) url.openConnection(); - - int responseCode = -1; - if (connection != null) { - connection.setConnectTimeout(6000); - connection.setReadTimeout(6000); - - try { - connection.connect(); - responseCode = connection.getResponseCode(); - } catch (SocketTimeoutException e) { - logger.error("siemensHvac:DoRequest:" + e.getMessage() + " : " + request); - return null; - } - - if (responseCode == HttpURLConnection.HTTP_OK) { - String inputLine = null; - in = new BufferedReader(new InputStreamReader(connection.getInputStream())); - - while ((inputLine = in.readLine()) != null) { - response.append(inputLine); - } - - in.close(); - } else { - response = null; - } - - logger.debug("siemensHvac:ReadDp:response:" + response); - - if (response != null) { - - JSONObject result = JSONResponseHandler.toJSONObject(response.toString()); - if (result.containsKey("Result")) { - JSONObject subResult = (JSONObject) result.get("Result"); - if (subResult.containsKey("Success")) { - if (subResult.get("Success").equals("true")) { - return result; - } - } - } - return null; - } - - } - } catch (MalformedURLException e) { - logger.error("siemensHvac:DoRequest:MalformedURLException by executing jsonRequest: " + request + " ; " - + e.getLocalizedMessage()); - } catch (IOException e) { - logger.error("siemensHvac:DoRequest:IOException by executing jsonRequest: " + request + " ; " - + e.getLocalizedMessage()); - } catch (Exception e) { - logger.error("siemensHvac:DoRequest:Exception by executing jsonRequest: " + request + " ; " - + e.getLocalizedMessage()); - } finally { - if (connection != null) { - connection.disconnect(); - } - } - - return null; - } - - private String _readDpInternal(String name, String dp, String type) { - logger.debug("siemensHvac:readDP():" + name); - /* - * try { - * - * JSONObject subResult = DoRequest("api/menutree/read_datapoint.json?Id=" + dp); - * - * logger.debug("siemensHvac:ReadDp:response:" + response); - * if (response != null) { - * JSONObject result = JSONResponseHandler.toJSONObject(response); - * if (result.containsKey("Result")) { - * JSONObject subResult = (JSONObject) result.get("Result"); - * if (subResult.containsKey("Success")) { - * if (subResult.get("Success").equals("true")) { - * if (result.containsKey("Data")) { - * subResult = (JSONObject) result.get("Data"); - * - * // {"Value":"Automatique","Type":"Enumeration","EnumValue":"1","Unit":""} - * String typer = ""; - * String value = ""; - * String enumValue = ""; - * - * if (subResult.containsKey("Type")) { - * typer = subResult.get("Type").toString().trim(); - * } - * if (subResult.containsKey("Value")) { - * value = subResult.get("Value").toString().trim(); - * } - * if (subResult.containsKey("EnumValue")) { - * enumValue = subResult.get("EnumValue").toString().trim(); - * } - * - * if (typer.equals("Enumeration")) { - * return "" + enumValue + ":" + value; - * } else { - * return value; - * } - * } - * } else { - * sessionId = null; - * } - * } - * } - * } - * } catch (Exception e) { - * logger.error("siemensHvac:ReadDp:Error during dp reading: " + name + " ; " + e.getLocalizedMessage()); - * // Reset sessionId so we redone _auth on error - * sessionId = null; - * } - */ - return ""; - } - - /* - * public void AddDpUpdate(String itemName, Type dp) { - * synchronized (updateCommand) { - * updateCommand.put(itemName, dp); - * lastUpdate = new java.util.Date(); - * } - * } - */ - - @Override - public void run() { - logger.debug("siemensHvac:sender thread start"); - - // as long as no interrupt is requested, continue running - while (!interrupted) { - try { - Thread.sleep(2000); - Date currentDate = new java.util.Date(); - - logger.debug("siemensHvac:sender thread alive:" + currentDate); - - if (lastUpdate == null) { - continue; - } - - long ms = currentDate.getTime() - lastUpdate.getTime(); - if (ms < 3000) { - continue; - } - - /* - * synchronized (updateCommand) { - * if (updateCommand.isEmpty()) { - * continue; - * } - * - * logger.debug("siemensHvac:sender thread updateCommand"); - * - * for (String key : updateCommand.keySet()) { - * Type dp = updateCommand.get(key); - * WriteDp(key, dp); - * - * } - * - * updateCommand.clear(); - * } - */ - } catch (Exception e) { - logger.error("siemensHvac:Error occured will sending update values", e); - } - } - } -} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacDiscoveryService.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacDiscoveryService.java deleted file mode 100644 index fa24632960e00..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacDiscoveryService.java +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright (c) 2014-2016 by the respective copyright holders. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.openhab.binding.siemenshvac.internal; - -import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService; -import org.openhab.binding.siemenshvac.handler.SiemensHvacHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The {@link HueBridgeServiceTracker} tracks for hue lights which are connected - * to a paired hue bridge. The default search time for hue is 60 seconds. - * - * @author Kai Kreuzer - Initial contribution - * @author Andre Fuechsel - changed search timeout, changed discovery result creation to support generic thing types - * @author Thomas Höfer - Added representation - */ -public class siemensHvacDiscoveryService extends AbstractDiscoveryService { - - private final Logger logger = LoggerFactory.getLogger(siemensHvacDiscoveryService.class); - - private final static int SEARCH_TIME = 60; - private final static String MODEL_ID = "modelId"; - - // @formatter:off - - private SiemensHvacHandler hvacHandler; - - public siemensHvacDiscoveryService(SiemensHvacHandler hvacHandler) { - super(SEARCH_TIME); - this.hvacHandler = hvacHandler; - } - - public void activate() { - //hvacHandler.registerLightStatusListener(this); - } - - @Override - public void deactivate() { - //removeOlderResults(new Date().getTime()); - //hueBridgeHandler.unregisterLightStatusListener(this); - } - - /* - @Override - public Set getSupportedThingTypes() { - return HueLightHandler.SUPPORTED_THING_TYPES; - } -*/ - - @Override - public void startScan() { - /* - List lights = hueBridgeHandler.getFullLights(); - if (lights != null) { - for (FullLight l : lights) { - onLightAddedInternal(l); - } - } - // search for unpaired lights - hueBridgeHandler.startSearch(); - */ - } - - @Override - protected synchronized void stopScan() { - super.stopScan(); - removeOlderResults(getTimestampOfLastScan()); - } - -/* - private void onLightAddedInternal(FullLight light) { - ThingUID thingUID = getThingUID(light); - ThingTypeUID thingTypeUID = getThingTypeUID(light); - - String modelId = light.getModelID().replaceAll(HueLightHandler.NORMALIZE_ID_REGEX, "_"); - - if (thingUID != null && thingTypeUID != null) { - ThingUID bridgeUID = hueBridgeHandler.getThing().getUID(); - Map properties = new HashMap<>(1); - properties.put(LIGHT_ID, light.getId()); - properties.put(MODEL_ID, modelId); - - DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID) - .withProperties(properties).withBridge(bridgeUID).withLabel(light.getName()).build(); - - thingDiscovered(discoveryResult); - } else { - logger.debug("discovered unsupported light of type '{}' and model '{}' with id {}", light.getType(), - modelId, light.getId()); - } - } - - - @Override - public void onLightRemoved(HueBridge bridge, FullLight light) { - ThingUID thingUID = getThingUID(light); - - if (thingUID != null) { - thingRemoved(thingUID); - } - } - - @Override - public void onLightStateChanged(HueBridge bridge, FullLight light) { - // nothing to do - } - - private ThingUID getThingUID(FullLight light) { - ThingUID bridgeUID = hueBridgeHandler.getThing().getUID(); - ThingTypeUID thingTypeUID = getThingTypeUID(light); - - if (thingTypeUID != null && getSupportedThingTypes().contains(thingTypeUID)) { - return new ThingUID(thingTypeUID, bridgeUID, light.getId()); - } else { - return null; - } - } - - private ThingTypeUID getThingTypeUID(FullLight light) { - String thingTypeId = TYPE_TO_ZIGBEE_ID_MAP - .get(light.getType().replaceAll(HueLightHandler.NORMALIZE_ID_REGEX, "_").toLowerCase()); - return thingTypeId != null ? new ThingTypeUID(BINDING_ID, thingTypeId) : null; - } - - */ -} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacGenericBindingProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacGenericBindingProvider.java deleted file mode 100644 index e72e712b773d3..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/siemensHvacGenericBindingProvider.java +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Copyright (c) 2010-2015, openHAB.org and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -/* - * package org.openhab.binding.siemenshvac.internal; - * - * - * import java.util.Map; - * - * import org.openhab.binding.siemenshvac.siemensHvacBindingProvider; - * import org.openhab.core.binding.BindingConfig; - * import org.openhab.core.items.Item; - * import org.openhab.core.library.items.DimmerItem; - * import org.openhab.core.library.items.SwitchItem; - * import org.openhab.model.item.binding.AbstractGenericBindingProvider; - * import org.openhab.model.item.binding.BindingConfigParseException; - * import org.slf4j.Logger; - * import org.slf4j.LoggerFactory; - * - * - * public class siemensHvacGenericBindingProvider extends - * AbstractGenericBindingProvider implements siemensHvacBindingProvider { - * private static final Logger logger = LoggerFactory - * .getLogger(siemensHvacGenericBindingProvider.class); - * - * - * public String getBindingType() { - * logger.debug("siemensHvac:hvac getBindingType()!"); - * return "siemenshvac"; - * } - * - * - * @Override - * public void validateItemType(Item item, String bindingConfig) - * throws BindingConfigParseException { - * // if (!(item instanceof SwitchItem || item instanceof DimmerItem)) { - * // throw new BindingConfigParseException("item '" + item.getName() - * // + "' is of type '" + item.getClass().getSimpleName() - * // + - * // "', only Switch- and DimmerItems are allowed - please check your *.items configuration"); - * // } - * } - * - * - * @Override - * public void processBindingConfiguration(String context, Item item, - * String bindingConfig) throws BindingConfigParseException { - * logger.debug("siemensHvac:hvac processBindingConfiguration()!"); - * super.processBindingConfiguration(context, item, bindingConfig); - * siemensHvacBindingConfig config = new siemensHvacBindingConfig( - * item.getName(), bindingConfig); - * - * // parse bindingconfig here ... - * - * addBindingConfig(item, config); - * } - * - * public Map getBindingConfigs() { - * return bindingConfigs; - * } - * - * - * class siemensHvacBindingConfig implements BindingConfig { - * // put member fields here which holds the parsed values - * private String name; - * private String bindingConfig; - * private String dpt; - * private String type; - * - * public siemensHvacBindingConfig(String name, String bindingConfig) { - * this.name = name; - * this.bindingConfig = bindingConfig; - * - * String[] bindingValues = bindingConfig.split(";"); - * - * for (String bindingValue : bindingValues) { - * String[] bindingParts = bindingValue.split(":"); - * - * String bindingKey = bindingParts[0]; - * String bindingVal = bindingParts[1]; - * - * if (bindingKey.equals("dpt")) - * this.dpt = bindingVal; - * else if (bindingKey.equals("type")) - * this.type = bindingVal; - * } - * } - * - * public String getDpt() { - * return dpt; - * } - * - * public String getName() { - * return name; - * } - * - * public String getType() { - * return type; - * } - * - * } - * - * } - */ \ No newline at end of file diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java new file mode 100644 index 0000000000000..2e34f8bb64c6e --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2010-2020 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.siemenshvac.internal.type; + +import java.util.Locale; + +import org.openhab.core.thing.type.ChannelGroupType; +import org.openhab.core.thing.type.ChannelGroupTypeProvider; +import org.openhab.core.thing.type.ChannelGroupTypeUID; + +/** + * Extends the ChannelGroupTypeProvider to manually add a ChannelGroupType. + * + * @author Laurent Arnal - Initial contribution + */ +public interface SiemensHvacChannelGroupTypeProvider extends ChannelGroupTypeProvider { + + /** + * Adds the ChannelGroupType to this provider. + */ + public void addChannelGroupType(ChannelGroupType channelGroupType); + + /** + * Use this method to lookup a ChannelGroupType which was generated by the + * homematic binding. Other than {@link #getChannelGroupType(ChannelGroupTypeUID, Locale)} + * of this provider, it will return also those {@link ChannelGroupType}s + * which are excluded by {@link HomematicThingTypeExcluder} + * + * @param channelGroupTypeUID + * e.g. homematic:HM-WDS40-TH-I-2_0 + * @return ChannelGroupType that was added to HomematicChannelGroupTypeProvider, identified + * by its config-description-uri
+ * null if no ChannelGroupType with the given UID was added + * before + */ + public ChannelGroupType getInternalChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID); +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java new file mode 100644 index 0000000000000..cb2b17abd0245 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2010-2020 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.siemenshvac.internal.type; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.thing.type.ChannelGroupType; +import org.openhab.core.thing.type.ChannelGroupTypeProvider; +import org.openhab.core.thing.type.ChannelGroupTypeUID; +import org.openhab.core.thing.type.ChannelTypeRegistry; +import org.osgi.service.component.annotations.Component; + +/** + * Provides all ChannelGroupTypes from all Homematic bridges. + * + * @author Laurent Arnal - Initial contribution + */ +@Component(service = { SiemensHvacChannelGroupTypeProvider.class, ChannelGroupTypeProvider.class }) +public class SiemensHvacChannelGroupTypeProviderImpl implements SiemensHvacChannelGroupTypeProvider { + + private final Map channelGroupTypesByUID = new HashMap<>(); + + // + + @Override + public ChannelGroupType getInternalChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID) { + return channelGroupTypesByUID.get(channelGroupTypeUID); + } + + @Override + public void addChannelGroupType(ChannelGroupType channelGroupType) { + channelGroupTypesByUID.put(channelGroupType.getUID(), channelGroupType); + } + + @Override + public ChannelGroupType getChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID, @Nullable Locale locale) { + return channelGroupTypesByUID.get(channelGroupTypeUID); + } + + /** + * + * @see ChannelTypeRegistry#getChannelGroupTypes(Locale) + * + */ + @Override + public Collection getChannelGroupTypes(@Nullable Locale locale) { + Collection result = new ArrayList<>(); + for (ChannelGroupTypeUID uid : channelGroupTypesByUID.keySet()) { + result.add(channelGroupTypesByUID.get(uid)); + } + return result; + + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java new file mode 100644 index 0000000000000..b29940868b560 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2010-2020 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.siemenshvac.internal.type; + +import java.util.Locale; + +import org.openhab.core.thing.type.ChannelType; +import org.openhab.core.thing.type.ChannelTypeProvider; +import org.openhab.core.thing.type.ChannelTypeUID; + +/** + * Extends the ChannelTypeProvider to manually add a ChannelType. + * + * @author Laurent Arnal - Initial contribution + */ +public interface SiemensHvacChannelTypeProvider extends ChannelTypeProvider { + + /** + * Adds the ChannelType to this provider. + */ + public void addChannelType(ChannelType channelType); + + /** + * Use this method to lookup a ChannelType which was generated by the + * homematic binding. Other than {@link #getChannelType(ChannelTypeUID, Locale)} + * of this provider, it will return also those {@link ChannelType}s + * which are excluded by {@link HomematicThingTypeExcluder} + * + * @param channelTypeUID + * e.g. homematic:HM-WDS40-TH-I-2_0_RSSI_DEVICE + * @return ChannelType that was added to HomematicChannelTypeProvider, identified + * by its config-description-uri
+ * null if no ChannelType with the given UID was added + * before + */ + public ChannelType getInternalChannelType(ChannelTypeUID channelTypeUID); +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java new file mode 100644 index 0000000000000..118a58e547452 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2010-2020 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.siemenshvac.internal.type; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.thing.type.ChannelType; +import org.openhab.core.thing.type.ChannelTypeProvider; +import org.openhab.core.thing.type.ChannelTypeRegistry; +import org.openhab.core.thing.type.ChannelTypeUID; +import org.osgi.service.component.annotations.Component; + +/** + * Provides all ChannelTypes from SiemensHvac bridges. + * + * @author Laurent Arnal - Initial contribution + */ +@Component(service = { SiemensHvacChannelTypeProvider.class, ChannelTypeProvider.class }) +public class SiemensHvacChannelTypeProviderImpl implements SiemensHvacChannelTypeProvider { + private final Map channelTypesByUID = new HashMap<>(); + + public SiemensHvacChannelTypeProviderImpl() { + } + + @Override + public void addChannelType(ChannelType channelType) { + channelTypesByUID.put(channelType.getUID(), channelType); + } + + @Override + public Collection getChannelTypes(@Nullable Locale locale) { + Collection result = new ArrayList<>(); + + for (ChannelTypeUID uid : channelTypesByUID.keySet()) { + result.add(channelTypesByUID.get(uid)); + } + return result; + + } + + /** + * @see ChannelTypeRegistry#getChannelType(ChannelTypeUID, Locale) + */ + @Nullable + @Override + public ChannelType getChannelType(ChannelTypeUID channelTypeUID, @Nullable Locale locale) { + return channelTypesByUID.get(channelTypeUID); + } + + @Override + public ChannelType getInternalChannelType(@Nullable ChannelTypeUID channelTypeUID) { + return channelTypesByUID.get(channelTypeUID); + } + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java new file mode 100644 index 0000000000000..7770245cfbe64 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2010-2020 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.siemenshvac.internal.type; + +import java.net.URI; +import java.util.Locale; + +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.config.core.ConfigDescription; +import org.openhab.core.config.core.ConfigDescriptionProvider; + +/** + * Extends the ConfigDescriptionProvider to manually add a ConfigDescription. + * + * @author Laurent Arnal - Initial contribution + */ +public interface SiemensHvacConfigDescriptionProvider extends ConfigDescriptionProvider { + + /** + * Adds the ConfigDescription to this provider. + */ + public void addConfigDescription(ConfigDescription configDescription); + + /** + * Provides a {@link ConfigDescription} for the given URI. + * + * @param uri uri of the config description + * @param locale locale + * + * @return config description or null if no config description could be found + */ + @Override + @Nullable + ConfigDescription getConfigDescription(URI uri, @Nullable Locale locale); + + /** + * Use this method to lookup a ConfigDescription which was generated by the + * homematic binding. Other than {@link #getConfigDescription(URI, Locale)} + * of this provider, it will return also those {@link ConfigDescription}s + * which are excluded by {@link HomematicThingTypeExcluder} + * + * @param URI config-description-uri + * e.g. thing-type:homematic:HM-WDS40-TH-I-2 + * @return ConfigDescription that was added to HomematicConfigDescriptionProvider, + * identified by its config-description-uri
+ * null if no ConfigDescription with the given URI was added + * before + */ + public ConfigDescription getInternalConfigDescription(URI uri); +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProviderImpl.java new file mode 100644 index 0000000000000..f1032c763b9f5 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProviderImpl.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2010-2020 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.siemenshvac.internal.type; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.config.core.ConfigDescription; +import org.openhab.core.config.core.ConfigDescriptionProvider; +import org.osgi.service.component.annotations.Component; + +/** + * + * @author Laurent Arnal - Initial contribution + */ +@Component(service = { SiemensHvacConfigDescriptionProvider.class, ConfigDescriptionProvider.class }) +public class SiemensHvacConfigDescriptionProviderImpl implements SiemensHvacConfigDescriptionProvider { + private Map configDescriptionsByURI = new HashMap<>(); + + @Override + public Collection getConfigDescriptions(Locale locale) { + Collection result = new ArrayList<>(); + for (URI configDescriptionURI : configDescriptionsByURI.keySet()) { + result.add(configDescriptionsByURI.get(configDescriptionURI)); + } + return result; + } + + @Override + public ConfigDescription getConfigDescription(URI uri, @Nullable Locale locale) { + return configDescriptionsByURI.get(uri); + } + + @Override + public ConfigDescription getInternalConfigDescription(URI uri) { + return configDescriptionsByURI.get(uri); + } + + @Override + public void addConfigDescription(ConfigDescription configDescription) { + configDescriptionsByURI.put(configDescription.getUID(), configDescription); + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java new file mode 100644 index 0000000000000..0f631653854a6 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2010-2020 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.siemenshvac.internal.type; + +import java.util.Locale; + +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.binding.ThingTypeProvider; +import org.openhab.core.thing.type.ThingType; + +/** + * Extends the ThingTypeProvider to manually add a ThingType. + * + * @author Laurent Arnal - Initial contribution + */ +public interface SiemensHvacThingTypeProvider extends ThingTypeProvider { + + /** + * Adds the ThingType to this provider. + */ + public void addThingType(ThingType thingType); + + /** + * Use this method to lookup a ThingType which was generated by the + * homematic binding. Other than {@link #getThingType(ThingTypeUID, Locale)} + * of this provider, it will return also those {@link ThingType}s which are + * excluded by {@link HomematicThingTypeExcluder} + * + * @param thingTypeUID + * e.g. homematic:HM-Sec-SC + * @return ThingType that was added to HomematicThingTypeProvider, identified + * by its thingTypeUID
+ * null if no ThingType with the given thingTypeUID was added + * before + */ + public ThingType getInternalThingType(ThingTypeUID thingTypeUID); +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProviderImpl.java new file mode 100644 index 0000000000000..2904f60f49293 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProviderImpl.java @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2010-2020 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.siemenshvac.internal.type; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.binding.ThingTypeProvider; +import org.openhab.core.thing.type.ThingType; +import org.osgi.service.component.annotations.Component; + +/** + * Provides all ThingTypes from SiemensHvac bridges. + * + * @author Laurent Arnal - Initial contribution + */ +@Component(service = { SiemensHvacThingTypeProvider.class, ThingTypeProvider.class }, immediate = true) +public class SiemensHvacThingTypeProviderImpl implements SiemensHvacThingTypeProvider { + + private Map thingTypesByUID = new HashMap<>(); + + public SiemensHvacThingTypeProviderImpl() { + + } + + @Override + public void addThingType(ThingType thingType) { + thingTypesByUID.put(thingType.getUID(), thingType); + + } + + @Override + public ThingType getInternalThingType(ThingTypeUID thingTypeUID) { + return thingTypesByUID.get(thingTypeUID); + + } + + @Override + public Collection getThingTypes(@Nullable Locale locale) { + Map copy = new HashMap<>(thingTypesByUID); + return copy.values(); + + } + + @Override + public ThingType getThingType(ThingTypeUID thingTypeUID, Locale locale) { + return thingTypesByUID.get(thingTypeUID); + } + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java new file mode 100644 index 0000000000000..4bb00a7083d24 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2010-2020 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.siemenshvac.internal.type; + +import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataDataPoint; +import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataDevice; +import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataMenu; +import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.type.ChannelGroupTypeUID; +import org.openhab.core.thing.type.ChannelTypeUID; + +/** + * Utility class for generating some UIDs. + * + * @author Laurent Arnal - Initial contribution + */ +public class UidUtils { + + public static String sanetizeId(String st) { + st = st.replace(" ", "_"); + st = st.replace("é", "e"); + st = st.replace("è", "e"); + st = st.replace("ê", "e"); + + st = st.replace("î", "i"); + st = st.replace("ô", "o"); + st = st.replace("à", "a"); + st = st.replace("/", "_"); + st = st.replace(".", "_"); + st = st.replace("(", "_"); + st = st.replace(")", "_"); + st = st.replace("°", "_"); + st = st.replace("É", "E"); + + return st; + } + + /** + * Generates the ThingTypeUID for the given device. If it's a Homegear device, add a prefix because a Homegear + * device has more datapoints. + */ + public static ThingTypeUID generateThingTypeUID(SiemensHvacMetadataDevice device) { + String type = sanetizeId(device.getType()); + return new ThingTypeUID(SiemensHvacBindingConstants.BINDING_ID, type); + } + + public static ThingTypeUID generateThingTypeUID(String name) { + return new ThingTypeUID(SiemensHvacBindingConstants.BINDING_ID, name); + } + + /** + * Generates the ChannelTypeUID for the given datapoint with deviceType, channelNumber and datapointName. + */ + public static ChannelTypeUID generateChannelTypeUID(SiemensHvacMetadataDataPoint dpt) { + + String shortDesc = sanetizeId(dpt.getShortDesc()); + return new ChannelTypeUID(SiemensHvacBindingConstants.BINDING_ID, + String.format("%s_%s_%s", dpt.getDptType(), dpt.getDptId(), shortDesc)); + } + + /** + * Generates the ThingUID for the given device in the given bridge. + */ + public static ThingUID generateThingUID(Bridge bridge) { + ThingTypeUID thingTypeUID = generateThingTypeUID(""); + return new ThingUID(thingTypeUID, bridge.getUID(), ""); + } + + /** + * Generates the ChannelUID for the given datapoint with channelNumber and datapointName. + */ + public static ChannelUID generateChannelUID(ThingUID thingUID) { + return new ChannelUID(thingUID, ""); + // String.valueOf(dp.getChannel().getNumber()), dp.getName()); + } + + /** + * Generates the ChannelTypeUID for the given datapoint with deviceType and channelNumber. + */ + public static ChannelGroupTypeUID generateChannelGroupTypeUID(SiemensHvacMetadataMenu menu) { + return new ChannelGroupTypeUID(SiemensHvacBindingConstants.BINDING_ID, String.format("%s", menu.getMenuId())); + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/binding/binding.xml new file mode 100644 index 0000000000000..1c6b91c584fd4 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/binding/binding.xml @@ -0,0 +1,9 @@ + + + + SiemensHvac Binding + This is the binding for SiemensHvac. + + diff --git a/bundles/org.openhab.binding.siemenshvac/ESH-INF/i18n/siemenshvac_xx_XX.properties b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_xx_XX.properties similarity index 53% rename from bundles/org.openhab.binding.siemenshvac/ESH-INF/i18n/siemenshvac_xx_XX.properties rename to bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_xx_XX.properties index 32a0379fec109..9f2240460a27d 100644 --- a/bundles/org.openhab.binding.siemenshvac/ESH-INF/i18n/siemenshvac_xx_XX.properties +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_xx_XX.properties @@ -1,3 +1,5 @@ +# FIXME: please substitute the xx_XX with a proper locale, ie. de_DE +# FIXME: please do not add the file to the repo if you add or change no content # binding binding.siemenshvac.name = binding.siemenshvac.description = @@ -6,6 +8,10 @@ binding.siemenshvac.description = thing-type.siemenshvac.sample.label = thing-type.siemenshvac.sample.description = +# thing type config description +thing-type.config.siemenshvac.sample.config1.label = +thing-type.config.siemenshvac.sample.config1.description = + # channel types channel-type.siemenshvac.sample-channel.label = -channel-type.siemenshvac.sample-channel.description = \ No newline at end of file +channel-type.siemenshvac.sample-channel.description = diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml new file mode 100644 index 0000000000000..f9ed10993f7d3 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml @@ -0,0 +1,26 @@ + + + + + + + This is a OZW672 IP interface + + + + + Network address of the Siemens Hvac IP gateway + network-address + + + Port number of the Siemens Hvac gateway + false + + 3671 + + + + + From 77c268d4c2478f82294587f921fbefd9ce973f36 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 8 Jan 2023 11:58:28 +0100 Subject: [PATCH 003/214] better menu handing better config support update headers Signed-off-by: Laurent ARNAL --- .../Metadata/RuntimeTypeAdapterFactory.java | 28 +- .../Metadata/SiemensHvacMetadata.java | 22 +- .../SiemensHvacMetadataDataPoint.java | 36 +- .../Metadata/SiemensHvacMetadataDevice.java | 12 + .../Metadata/SiemensHvacMetadataMenu.java | 13 +- .../SiemensHvacMetadataPointChild.java | 12 + .../Metadata/SiemensHvacMetadataRegistry.java | 8 +- .../SiemensHvacMetadataRegistryImpl.java | 382 +++++++++++------- .../config/SiemensHvacConfiguration.java | 26 -- .../SiemensHvacBindingConstants.java | 6 +- .../SiemenesHvacDiscoveryParticipant.java | 2 +- .../SiemensHvacDeviceDiscoveryService.java | 54 +-- .../internal/handler/SiemensHvacHandler.java | 16 + .../handler/SiemensHvacHandlerImpl.java | 17 +- .../SiemensHvacOZW672BridgeThingHandler.java | 4 +- .../network/SiemensHvacConnector.java | 2 + .../network/SiemensHvacConnectorImpl.java | 125 +----- .../network/SiemensHvacRequestListener.java | 70 ++-- .../SiemensHvacChannelTypeProviderImpl.java | 2 +- .../SiemensHvacConfigDescriptionProvider.java | 2 +- ...mensHvacConfigDescriptionProviderImpl.java | 2 +- .../type/SiemensHvacThingTypeProvider.java | 2 +- .../siemenshvac/internal/type/UidUtils.java | 151 ++++++- .../main/resources/OH-INF/thing/ozw672.xml | 20 +- 24 files changed, 619 insertions(+), 395 deletions(-) delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/config/SiemensHvacConfiguration.java diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java index caf478b14a218..8b70cf68390ef 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java @@ -1,3 +1,15 @@ +/** + * Copyright (c) 2010-2021 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 + */ /* * Copyright (C) 2011 Google Inc. * @@ -30,6 +42,9 @@ import java.util.LinkedHashMap; import java.util.Map; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @@ -78,7 +93,7 @@ *

* Without additional type information, the serialized JSON is ambiguous. Is * the bottom shape in this drawing a rectangle or a diamond? - * + * *

  *    {@code
  *   {
@@ -99,7 +114,7 @@
  * This class addresses this problem by adding type information to the
  * serialized JSON and honoring that type information when the JSON is
  * deserialized:
- * 
+ *
  * 
  *    {@code
  *   {
@@ -126,7 +141,7 @@
  * Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field
  * name to the {@link #of} factory method. If you don't supply an explicit type
  * field name, {@code "type"} will be used.
- * 
+ *
  * 
  * {
  *     @code
@@ -156,7 +171,7 @@
  * 
* * Like {@code GsonBuilder}, this API supports chaining: - * + * *
  * {
  *     @code
@@ -165,6 +180,11 @@
  * }
  * 
*/ +/** + * + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory { private final Class baseType; private final String typeFieldName; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java index 544460e1b9d78..8191cb2601e69 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java @@ -1,8 +1,20 @@ +/** + * Copyright (c) 2010-2021 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.siemenshvac.internal.Metadata; public class SiemensHvacMetadata { private int Id = -1; - private int menuId = -1; + private int subId = -1; private int groupId = -1; private int catId = -1; private String shortDesc = null; @@ -20,12 +32,12 @@ public void setId(int Id) { this.Id = Id; } - public int getMenuId() { - return menuId; + public int getSubId() { + return subId; } - public void setMenuId(int menuId) { - this.menuId = menuId; + public void setSubId(int subId) { + this.subId = subId; } public int getGroupId() { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java index 292278dcb2a35..a375e356ed565 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java @@ -1,3 +1,15 @@ +/** + * Copyright (c) 2010-2021 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.siemenshvac.internal.Metadata; import java.util.ArrayList; @@ -9,7 +21,7 @@ import com.google.gson.JsonObject; public class SiemensHvacMetadataDataPoint extends SiemensHvacMetadata { - private int dptId = -1; + private String dptId = "-1"; private String dptType = null; private String dptUnit = null; private String min = null; @@ -18,6 +30,7 @@ public class SiemensHvacMetadataDataPoint extends SiemensHvacMetadata { private String fieldWitdh = null; private String decimalDigits = null; private Boolean detailsResolved = false; + private Boolean unresolvableDetails = false; private String dialogType = null; private String maxLength = null; private String address = null; @@ -41,11 +54,11 @@ public void setChild(List child) { this.child = child; } - public int getDptId() { + public String getDptId() { return dptId; } - public void setDptId(int dptId) { + public void setDptId(String dptId) { this.dptId = dptId; } @@ -150,8 +163,25 @@ public void resolveDptDetails(JsonObject result) { return; } + JsonObject subResultObj = result.getAsJsonObject("Result"); JsonObject desc = result.getAsJsonObject("Description"); + if (subResultObj.has("Success")) { + boolean resultVal = subResultObj.get("Success").getAsBoolean(); + JsonObject error = subResultObj.getAsJsonObject("Error"); + String errorMsg = ""; + if (error != null) { + errorMsg = error.get("Txt").getAsString(); + } + + if (errorMsg.equals("datatype not supported")) { + detailsResolved = true; + unresolvableDetails = true; + return; + } + + } + if (desc != null) { this.dptType = desc.get("Type").getAsString(); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java index ad686f96d0209..36111de9984ce 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java @@ -1,3 +1,15 @@ +/** + * Copyright (c) 2010-2021 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.siemenshvac.internal.Metadata; public class SiemensHvacMetadataDevice { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java index e2ee906bcb40b..22417897f3020 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java @@ -1,3 +1,15 @@ +/** + * Copyright (c) 2010-2021 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.siemenshvac.internal.Metadata; import java.util.HashMap; @@ -17,5 +29,4 @@ public void AddChild(SiemensHvacMetadata information) { public HashMap getChilds() { return this.childList; } - } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java index 76a5f469bf60c..f6fc7e6e741c8 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java @@ -1,3 +1,15 @@ +/** + * Copyright (c) 2010-2021 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.siemenshvac.internal.Metadata; public class SiemensHvacMetadataPointChild { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java index 5c729121422d6..fb4a0fd785bf7 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2020 Contributors to the openHAB project + * Copyright (c) 2010-2021 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -12,6 +12,8 @@ */ package org.openhab.binding.siemenshvac.internal.Metadata; +import java.util.ArrayList; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; @@ -34,6 +36,9 @@ public interface SiemensHvacMetadataRegistry { @Nullable SiemensHvacMetadataMenu getRoot(); + @Nullable + ArrayList getDevices(); + @Nullable SiemensHvacMetadata getDptMap(String key); @@ -42,5 +47,4 @@ public interface SiemensHvacMetadataRegistry { @Nullable SiemensHvacConnector getSiemensHvacConnector(); - } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index f59ec70c41a78..a436d4065fa5b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -1,3 +1,15 @@ +/** + * Copyright (c) 2010-2021 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.siemenshvac.internal.Metadata; import java.io.File; @@ -12,8 +24,11 @@ import java.util.Hashtable; import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; @@ -25,6 +40,7 @@ import org.openhab.binding.siemenshvac.internal.type.SiemensHvacConfigDescriptionProvider; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacThingTypeProvider; import org.openhab.binding.siemenshvac.internal.type.UidUtils; +import org.openhab.core.OpenHAB; import org.openhab.core.config.core.ConfigDescriptionBuilder; import org.openhab.core.config.core.ConfigDescriptionParameter; import org.openhab.core.config.core.ConfigDescriptionParameterGroup; @@ -69,8 +85,7 @@ public class SiemensHvacMetadataRegistryImpl implements SiemensHvacMetadataRegis private boolean interrupted = false; - private static final String CONFIG_DIR = "." + File.separator + "configurations"; - private static final String CONTENT_DIR = CONFIG_DIR + File.separator + "services"; + private static final String JSON_DIR = OpenHAB.getUserDataFolder() + File.separatorChar + "jsondb"; private @Nullable static URI configDescriptionUriChannel; @@ -139,6 +154,11 @@ protected void unsetConfigDescriptionProvider(SiemensHvacConfigDescriptionProvid return this.channelTypeProvider; } + @Override + public @Nullable ArrayList getDevices() { + return devices; + } + /** * Initializes the type generator. */ @@ -167,13 +187,47 @@ public void InitDptMap(@Nullable SiemensHvacMetadata node) { } dptMap.put("byId" + node.getId(), node); - dptMap.put("byMenu" + node.getMenuId(), node); + dptMap.put("bySubId" + node.getSubId(), node); if (node.getClass() == SiemensHvacMetadataDataPoint.class) { SiemensHvacMetadataDataPoint dpi = (SiemensHvacMetadataDataPoint) node; dptMap.put("byDptId" + dpi.getDptId(), node); } } + public void ResolveDetails() { + for (String key : dptMap.keySet()) { + if (key.indexOf("byId") < 0) { + continue; + } + + SiemensHvacMetadata node = dptMap.get(key); + if (node.getClass() == SiemensHvacMetadataDataPoint.class) { + SiemensHvacMetadataDataPoint dpi = (SiemensHvacMetadataDataPoint) node; + if (!dpi.getDetailsResolved()) { + resolveDptDetails(dpi); + } + } + } + } + + public int UnresolveCount() { + int count = 0; + for (String key : dptMap.keySet()) { + if (key.indexOf("byId") < 0) { + continue; + } + + SiemensHvacMetadata node = dptMap.get(key); + if (node.getClass() == SiemensHvacMetadataDataPoint.class) { + SiemensHvacMetadataDataPoint dpi = (SiemensHvacMetadataDataPoint) node; + if (!dpi.getDetailsResolved()) { + count++; + } + } + } + return count; + } + @Override public @Nullable SiemensHvacMetadataMenu getRoot() { return (SiemensHvacMetadataMenu) root; @@ -181,8 +235,6 @@ public void InitDptMap(@Nullable SiemensHvacMetadata node) { @Override public void ReadMeta() { - root = null; - if (root == null) { logger.debug("siemensHvac:InitDptMap():begin"); @@ -198,23 +250,34 @@ public void ReadMeta() { dptMap = new Hashtable(); InitDptMap(root); + + int unresolveCount = Integer.MAX_VALUE; + + while (unresolveCount > 0) { + ResolveDetails(); + hvacConnector.WaitAllPendingRequest(); + unresolveCount = UnresolveCount(); + + } + SaveMetaDataToCache(); - logger.debug("siemensHvac:InitDptMap():end"); - } - SiemensHvacMetadataMenu rootMenu = getRoot(); - for (SiemensHvacMetadataDevice device : devices) { - if (device.getType().indexOf("OZW672") >= 0) { - continue; + SiemensHvacMetadataMenu rootMenu = getRoot(); + for (SiemensHvacMetadataDevice device : devices) { + if (device.getType().indexOf("OZW672") >= 0) { + continue; + } + + generateThingsType(device); } - generateThingsType(device); + logger.debug("siemensHvac:InitDptMap():end"); } - logger.info("end configuration"); } private void generateThingsType(SiemensHvacMetadataDevice device) { + logger.debug("Generate thing types for device : " + device.getName() + "/" + device.getSerialNr()); if (thingTypeProvider != null) { ThingTypeUID thingTypeUID = UidUtils.generateThingTypeUID(device); ThingType tt = thingTypeProvider.getInternalThingType(thingTypeUID); @@ -224,8 +287,8 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { List groupTypes = new ArrayList<>(); int treeId = device.getTreeId(); - if (dptMap.containsKey("byMenu" + treeId)) { - SiemensHvacMetadataMenu menu = (SiemensHvacMetadataMenu) dptMap.get("byMenu" + treeId); + if (dptMap.containsKey("byId" + treeId)) { + SiemensHvacMetadataMenu menu = (SiemensHvacMetadataMenu) dptMap.get("byId" + treeId); for (SiemensHvacMetadata child : menu.getChilds().values()) { @@ -239,6 +302,10 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { if (childDt instanceof SiemensHvacMetadataDataPoint) { SiemensHvacMetadataDataPoint dataPoint = (SiemensHvacMetadataDataPoint) childDt; + if (dataPoint.getDptType() == null) { + continue; + } + ChannelTypeUID channelTypeUID = UidUtils.generateChannelTypeUID(dataPoint); ChannelType channelType = channelTypeProvider @@ -252,10 +319,11 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { Map props = new Hashtable(); props.put("dptId", "" + dpt.getDptId()); - props.put("groupdId", "" + subMenu.getMenuId()); + props.put("id", "" + dpt.getId()); + props.put("subId", "" + dpt.getSubId()); + props.put("groupdId", "" + dpt.getGroupId()); - String id = dataPoint.getDptId() + "_" - + UidUtils.sanetizeId(dataPoint.getShortDesc()); + String id = dataPoint.getId() + "_" + UidUtils.sanetizeId(dataPoint.getShortDesc()); ChannelDefinition channelDef = new ChannelDefinitionBuilder(id, channelType.getUID()).withLabel(dataPoint.getShortDesc()) .withDescription(dataPoint.getLongDesc()).withProperties(props) @@ -322,10 +390,25 @@ private ChannelType createChannelType(SiemensHvacMetadataDataPoint dpt, ChannelT stateFragment.withOptions(options); } + boolean isAdvanced = false; + if (label.contains("_Y")) { + isAdvanced = true; + } + if (label.contains("_K")) { + isAdvanced = true; + } + if (label.contains("Histo")) { + isAdvanced = true; + } + if (label.contains(" QX")) { + isAdvanced = true; + } + ChannelTypeBuilder channelTypeBuilder = ChannelTypeBuilder.state(channelTypeUID, label, itemType) .withStateDescriptionFragment(stateFragment.build()); - channelType = channelTypeBuilder.isAdvanced(false).withDescription(description).withCategory(category).build(); + channelType = channelTypeBuilder.isAdvanced(isAdvanced).withDescription(description).withCategory(category) + .build(); return channelType; @@ -359,7 +442,7 @@ private ThingType createThingType(SiemensHvacMetadataDevice device, List parms = new ArrayList<>(); List groups = new ArrayList<>(); - /* - * for (ChannelGroupType groupTp : groupTypes) { - * String groupName = groupTp.getLabel(); - * String groupLabel = groupTp.getLabel(); - * List channelDefinitions = groupTp.getChannelDefinitions(); - * groups.add(ConfigDescriptionParameterGroupBuilder.create(groupName).withLabel(groupLabel).build()); - * - * for (ChannelDefinition chanDef : channelDefinitions) { - * - * try { - * ConfigDescriptionParameterBuilder builder = ConfigDescriptionParameterBuilder - * .create(chanDef.getLabel(), Type.INTEGER); - * - * ChannelTypeUID channelTypeUID = chanDef.getChannelTypeUID(); - * ChannelType channelType = channelTypeProvider.getInternalChannelType(channelTypeUID); - * - * String chanId = chanDef.getId(); - * builder.withLabel(chanDef.getLabel()); - * - * builder.withDefault("0"); - * builder.withDescription(chanDef.getDescription()); - * - * builder.withMinimum(channelType.getState().getMinimum()); - * builder.withMaximum(channelType.getState().getMaximum()); - * builder.withStepSize(channelType.getState().getStep()); - * // builder.withUnitLabel(channelType.getState().getMaximum()); - * - * builder.withGroupName(groupName); - * parms.add(builder.build()); - * - * Map chanProps = chanDef.getProperties(); - * } catch (Exception ex) { - * logger.debug("p1"); - * } - * } - * - * } - */ - configDescriptionProvider.addConfigDescription(ConfigDescriptionBuilder.create(configDescriptionURI) .withParameters(parms).withParameterGroups(groups).build()); } public static String getItemType(SiemensHvacMetadataDataPoint dpt) { - if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_STRING)) { - return SiemensHvacBindingConstants.ITEM_TYPE_STRING; - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { - return SiemensHvacBindingConstants.ITEM_TYPE_NUMBER; - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { - return SiemensHvacBindingConstants.ITEM_TYPE_NUMBER; - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_DATE)) { - return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_TIME)) { - return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_RADIO)) { - return SiemensHvacBindingConstants.ITEM_TYPE_CONTACT; - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_SCHEDULER)) { - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_CALENDAR)) { - } else { - logger.debug("unknow type in getItemType()"); + if (dpt.getDptType() != null) { + if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_STRING)) { + return SiemensHvacBindingConstants.ITEM_TYPE_STRING; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { + return SiemensHvacBindingConstants.ITEM_TYPE_NUMBER; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { + return SiemensHvacBindingConstants.ITEM_TYPE_NUMBER; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_DATE)) { + return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_TIME)) { + return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_RADIO)) { + return SiemensHvacBindingConstants.ITEM_TYPE_CONTACT; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_SCHEDULER)) { + return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_CALENDAR)) { + return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; + } else { + logger.debug("unknow type in getItemType()"); + } } return ""; @@ -452,10 +500,45 @@ public static String getCategory(SiemensHvacMetadataDataPoint dp) { String dpDialog = dp.getDialogType(); int dpCatId = dp.getCatId(); String dpType = dp.getDptType(); + String dptUnit = dp.getDptUnit(); + + if (dptUnit != null && dptUnit.contains("°C")) { + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_PROPS_TEMP; + } else if (dpType != null) { + if (dpType.contains("DateTime")) { + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_PROPS_TIME; + } else if (dpType.contains("TimeOfDay")) { + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_PROPS_TIME; + } else if (dpType.contains("Enumeration")) { + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_SWITCH; + } else if (dpType.contains("RadioButton")) { + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_SWITCH; + } else if (dpType.contains("Numeric")) { + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_NUMBER; + } else { + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_CONTROL_HEATING; + } + } + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_CONTROL_HEATING; // String channelType = StringUtils.defaultString(dp.getChannel().getType()); - return SiemensHvacBindingConstants.CATEGORY_CHANNEL_CONTROL_HEATING; + // Number + // Text + // Switch + // Group + // Sun + // Moon + // Humidity + // Energy + // Water + // Pressure + // QualityOfService + // Flow + // Heating + // Alarm + // Vacation + } /** @@ -570,6 +653,7 @@ public void execute(URI uri, int status, @Nullable Object response) { } } }); + } catch (Exception e) { logger.error("siemensHvac:ResolveDpt:Error during dp reading: " + id + " ; " + e.getLocalizedMessage()); // Reset sessionId so we redone _auth on error @@ -583,20 +667,13 @@ public void DecodeMetaDataResult(JsonObject resultObj, SiemensHvacMetadata paren if (resultObj.has("MenuItems")) { if (parent != null) { logger.debug("Decode menuItem for :" + parent.getShortDesc()); - } else { - logger.debug("Decode menuItem for root"); } - SiemensHvacMetadata childNode; JsonArray menuItems = resultObj.getAsJsonArray("MenuItems"); for (JsonElement child : menuItems) { JsonObject menuItem = child.getAsJsonObject(); - if (menuItem == null) { - continue; - } - childNode = new SiemensHvacMetadataMenu(); childNode.setParent(parent); @@ -605,7 +682,7 @@ public void DecodeMetaDataResult(JsonObject resultObj, SiemensHvacMetadata paren itemId = menuItem.get("Id").getAsInt(); } - childNode.setMenuId(itemId); + childNode.setId(itemId); if (menuItem.has("Text")) { JsonObject descObj = menuItem.getAsJsonObject("Text"); @@ -632,7 +709,7 @@ public void DecodeMetaDataResult(JsonObject resultObj, SiemensHvacMetadata paren shortDesc = descObj.get("Short").getAsString(); } - childNode.setId(subItemId); + childNode.setSubId(subItemId); childNode.setCatId(catId); childNode.setGroupId(groupId); childNode.setShortDesc(shortDesc); @@ -642,45 +719,68 @@ public void DecodeMetaDataResult(JsonObject resultObj, SiemensHvacMetadata paren // logger.debug(String.format("siemensHvac:ResolveDpt():findMenuItem: %d, %s, %s, %s, %s", itemId, // subItemId, groupId, catId, longDesc)); - if (itemId == 931 || itemId == 932 || itemId == 992 || itemId == 1017 || itemId == 1030 - || itemId == 1036 || itemId == 1040 || itemId == 1300 || itemId == 1489 || itemId == 1505 - || itemId == 1558) { + boolean shouldReadSub = false; + + shouldReadSub = shouldReadSub || (itemId == 931); + shouldReadSub = shouldReadSub || (itemId == 932); + // shouldReadSub = shouldReadSub || (itemId == 936); + // shouldReadSub = shouldReadSub || (itemId == 947); + // shouldReadSub = shouldReadSub || (itemId == 956); + // shouldReadSub = shouldReadSub || (itemId == 965); + // shouldReadSub = shouldReadSub || (itemId == 974); + // shouldReadSub = shouldReadSub || (itemId == 992); + // shouldReadSub = shouldReadSub || (itemId == 1017); + // shouldReadSub = shouldReadSub || (itemId == 1030); + // shouldReadSub = shouldReadSub || (itemId == 1036); + // shouldReadSub = shouldReadSub || (itemId == 1040); + // shouldReadSub = shouldReadSub || (itemId == 1046); + // shouldReadSub = shouldReadSub || (itemId == 1070); + // shouldReadSub = shouldReadSub || (itemId == 1099); + // shouldReadSub = shouldReadSub || (itemId == 1260); + // shouldReadSub = shouldReadSub || (itemId == 1279); + // shouldReadSub = shouldReadSub || (itemId == 1300); + // shouldReadSub = shouldReadSub || (itemId == 1328); + // shouldReadSub = shouldReadSub || (itemId == 1489); + // shouldReadSub = shouldReadSub || (itemId == 1505); + // shouldReadSub = shouldReadSub || (itemId == 1558); + + shouldReadSub = shouldReadSub || true; + + if (shouldReadSub) { ReadMetaData(childNode, itemId); } } + } } - if (resultObj.has("DatapointItems")) { + if (resultObj.has("DatapointItems")) + + { if (parent != null) { logger.debug("Decode dp for :" + parent.getShortDesc()); - } else { - logger.debug("Decode dp for root"); } SiemensHvacMetadata childNode; JsonArray dptItems = resultObj.getAsJsonArray("DatapointItems"); + Map idMap = new Hashtable(); + for (JsonElement child : dptItems) { JsonObject dptItem = child.getAsJsonObject(); - if (dptItem == null) { - continue; - } - nbDpt++; - logger.debug("dpt1:" + nbDpt); childNode = new SiemensHvacMetadataDataPoint(); childNode.setParent(parent); - int dptId = -1; + int nodeId = -1; int dpSubKey = -1; boolean hasWriteAccess = false; String address = ""; if (dptItem.has("Id")) { - dptId = dptItem.get("Id").getAsInt(); + nodeId = dptItem.get("Id").getAsInt(); } if (dptItem.has("Address")) { address = dptItem.get("Address").getAsString(); @@ -694,11 +794,13 @@ public void DecodeMetaDataResult(JsonObject resultObj, SiemensHvacMetadata paren SiemensHvacMetadataDataPoint dptChild = (SiemensHvacMetadataDataPoint) childNode; - dptChild.setDptId(dptId); + dptChild.setId(nodeId); dptChild.setAddress(address); dptChild.setDptSubKey(dpSubKey); dptChild.setWriteAccess(hasWriteAccess); + idMap.put("" + nodeId, dptChild); + if (dptItem.has("Text")) { JsonObject descObj = dptItem.getAsJsonObject("Text"); @@ -724,7 +826,7 @@ public void DecodeMetaDataResult(JsonObject resultObj, SiemensHvacMetadata paren shortDesc = descObj.get("Short").getAsString(); } - childNode.setMenuId(subItemId); + childNode.setSubId(subItemId); childNode.setCatId(catId); childNode.setGroupId(groupId); childNode.setShortDesc(shortDesc); @@ -734,12 +836,44 @@ public void DecodeMetaDataResult(JsonObject resultObj, SiemensHvacMetadata paren // catId, groupId, subItemId, shortDesc, longDesc)); } - resolveDptDetails(dptChild); - ((SiemensHvacMetadataMenu) parent).AddChild(childNode); } + String request2 = "main.app?section=popcard&idtype=4"; + if (id != -1) { + request2 = request2 + "&id=" + id; + } + + hvacConnector.DoRequest(request2, new SiemensHvacCallback() { + + @Override + public void execute(URI uri, int status, @Nullable Object response) { + + String st = (String) response; + st = st.replace("\n", ""); + + Pattern pattern = Pattern + .compile("td class=\\\"dp_linenumber\\\".*?>(.*?)<\\/td>.+?(?=id)id=\"dp(.+?)\""); + Matcher matcher = pattern.matcher(st); + + while (matcher.find()) { + String all = matcher.group(0); + String id = matcher.group(2); + String dptId = matcher.group(1); + + if ((!StringUtils.isEmpty(id)) && (!StringUtils.isEmpty(dptId))) { + + if (idMap.containsKey(id)) { + SiemensHvacMetadataDataPoint child = idMap.get(id); + child.setDptId(dptId); + } + + } + } + } + }); + } if (resultObj.has("WidgetItems")) { // JSONArray wgItems = (JSONArray) result.get("WidgetItems"); @@ -764,42 +898,11 @@ public void DecodeMetaDataResult(JsonObject resultObj, SiemensHvacMetadata paren } - public void VerifyBindingConfig(/* siemensHvacBindingConfig bindingConfig */) { - /* - * String dptType = bindingConfig.getDptType(); - * - * int dptId = bindingConfig.getDptId(); - * int dptMenuId = bindingConfig.getDptMenuId(); - * String dptName = bindingConfig.getDptName(); - * - * - * if (entry != null && entry.getClass() == siemensMetadataDataPoint.class) { - * siemensMetadataDataPoint dpt = (siemensMetadataDataPoint) entry; - * - * resolveDptDetails(dpt); - * - * if (dptId == -1) { - * bindingConfig.setDptId(dpt.getDptId()); - * } - * if (dptName == null || dptName.equals("")) { - * bindingConfig.setDptName(entry.getLongDesc()); - * } - * if (dptMenuId == -1) { - * bindingConfig.setDptMenuId(entry.getMenuId()); - * } - * - * if (dptType == null) { - * bindingConfig.setDptType(dpt.getDptType()); - * } - * } - */ - } - public void LoadMetaDataFromCache() { File file = null; try { - file = new File(CONFIG_DIR + File.separator + "siemens.json"); + file = new File(JSON_DIR + File.separator + "siemens.json"); if (!file.exists()) { return; @@ -820,7 +923,7 @@ public void SaveMetaDataToCache() { File file = null; try { - file = new File(CONFIG_DIR + File.separator + "siemens.json"); + file = new File(JSON_DIR + File.separator + "siemens.json"); if (!file.exists()) { file.getParentFile().mkdirs(); @@ -840,25 +943,18 @@ public void SaveMetaDataToCache() { } } - public static int nbDptRq = 0; - public static int nbDptRs = 0; - public void resolveDptDetails(SiemensHvacMetadataDataPoint dpt) { if (dpt.getDetailsResolved()) { return; } - String request = "api/menutree/datapoint_desc.json?Id=" + dpt.getDptId(); - nbDptRq++; - logger.debug("dpt21:" + nbDptRq); + String request = "api/menutree/datapoint_desc.json?Id=" + dpt.getId(); hvacConnector.DoRequest(request, new SiemensHvacCallback() { @Override public void execute(URI uri, int status, @Nullable Object response) { if (response instanceof JsonObject) { dpt.resolveDptDetails((JsonObject) response); - nbDptRs++; - logger.debug("dpt22:" + nbDptRs); } else { logger.debug("errror"); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/config/SiemensHvacConfiguration.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/config/SiemensHvacConfiguration.java deleted file mode 100644 index cfb58da199b47..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/config/SiemensHvacConfiguration.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) 2010-2020 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.siemenshvac.internal.config; - -/** - * The {@link SiemensHvacConfiguration} class contains fields mapping thing configuration parameters. - * - * @author Laurent ARNAL - Initial contribution - */ -public class SiemensHvacConfiguration { - - /** - * Sample configuration parameter. Replace with your own. - */ - public String config1; -} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java index 4281e38a0d49f..cc5fcb584b815 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2020 Contributors to the openHAB project + * Copyright (c) 2010-2021 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -31,7 +31,6 @@ public class SiemensHvacBindingConstants { // List of all Thing Type UIDs // Thing Type UIDs public static final ThingTypeUID THING_TYPE_OZW672 = new ThingTypeUID(BINDING_ID, "ozw672"); - public static final ThingTypeUID THING_TYPE_RVS41_813_327 = new ThingTypeUID(BINDING_ID, "RVS41_813_327"); // List of all Channel ids public static final String CHANNEL_1 = "channel1"; @@ -41,7 +40,7 @@ public class SiemensHvacBindingConstants { public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge"); - public static final String PROPERTY_VENDOR_NAME = "eQ-3 AG"; + public static final String PROPERTY_VENDOR_NAME = "Siemens"; public static final String ITEM_TYPE_SWITCH = "Switch"; public static final String ITEM_TYPE_ROLLERSHUTTER = "Rollershutter"; @@ -74,5 +73,4 @@ public class SiemensHvacBindingConstants { public static final String CATEGORY_CHANNEL_PROPS_TIME = "Time"; public static final String CATEGORY_CHANNEL_CONTROL_HEATING = "Heating"; - } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java index df0b8e5316863..dc746316580eb 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2020 Contributors to the openHAB project + * Copyright (c) 2010-2021 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java index b6328b8ed7fb3..a063197c8c076 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java @@ -1,6 +1,5 @@ - /** - * Copyright (c) 2010-2020 Contributors to the openHAB project + * Copyright (c) 2010-2021 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -13,18 +12,19 @@ */ package org.openhab.binding.siemenshvac.internal.discovery; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadata; -import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataMenu; +import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataDevice; import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataRegistry; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeBaseThingHandler; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; +import org.openhab.binding.siemenshvac.internal.type.UidUtils; import org.openhab.core.config.discovery.AbstractDiscoveryService; import org.openhab.core.config.discovery.DiscoveryResult; import org.openhab.core.config.discovery.DiscoveryResultBuilder; @@ -48,12 +48,11 @@ public class SiemensHvacDeviceDiscoveryService extends AbstractDiscoveryService private static final Logger logger = LoggerFactory.getLogger(SiemensHvacDeviceDiscoveryService.class); - public static final Set SUPPORTED_THING_TYPES = Collections - .singleton(SiemensHvacBindingConstants.THING_TYPE_RVS41_813_327); - private @Nullable SiemensHvacMetadataRegistry metadataRegistry; private @Nullable SiemensHvacBridgeBaseThingHandler siemensHvacBridgeHandler; private @Nullable SiemensHvacConnector hvacConnector; + public static final Set SUPPORTED_THING_TYPES = Collections + .singleton(SiemensHvacBindingConstants.THING_TYPE_OZW672); private static final int SEARCH_TIME = 10; @@ -79,15 +78,11 @@ protected void stopBackgroundDiscovery() { // can be overridden } - private @Nullable ThingUID getThingUID() { + private @Nullable ThingUID getThingUID(ThingTypeUID thingTypeUID, String serial) { if (siemensHvacBridgeHandler != null) { ThingUID localBridgeUID = siemensHvacBridgeHandler.getThing().getUID(); if (localBridgeUID != null) { - ThingTypeUID thingTypeUID = SiemensHvacBindingConstants.THING_TYPE_RVS41_813_327; - - if (thingTypeUID != null && getSupportedThingTypes().contains(thingTypeUID)) { - return new ThingUID(thingTypeUID, localBridgeUID, "-1"); - } + return new ThingUID(thingTypeUID, localBridgeUID, serial); } } return null; @@ -102,25 +97,38 @@ public void startScan() { if (metadataRegistry != null) { metadataRegistry.ReadMeta(); - SiemensHvacMetadataMenu rootMenu = metadataRegistry.getRoot(); - for (SiemensHvacMetadata child : rootMenu.getChilds().values()) { - if (child.getLongDesc().indexOf("OZW672") >= 0) { + // SiemensHvacMetadataMenu rootMenu = metadataRegistry.getRoot(); + // for (SiemensHvacMetadata child : rootMenu.getChilds().values()) { + ArrayList devices = metadataRegistry.getDevices(); + for (SiemensHvacMetadataDevice device : devices) { + + String name = device.getName(); + String type = device.getType(); + String addr = device.getAddr(); + String serialNr = device.getSerialNr(); + + if (name.indexOf("OZW672") >= 0) { continue; } - ThingUID thingUID = getThingUID(); + logger.debug("Find devices: " + name + "/" + type + "/" + addr + "/" + serialNr); + + String typeSn = UidUtils.sanetizeId(type); + String nameSn = UidUtils.sanetizeId(name); + ThingTypeUID thingTypeUID = new ThingTypeUID(SiemensHvacBindingConstants.BINDING_ID, typeSn); + + ThingUID thingUID = getThingUID(thingTypeUID, serialNr); ThingUID bridgeUID = siemensHvacBridgeHandler.getThing().getUID(); if (thingUID != null) { Map properties = new HashMap<>(1); - properties.put("CatId", child.getCatId()); - properties.put("GroupId", child.getGroupId()); - properties.put("ItemId", child.getMenuId()); - properties.put("LongDesc", child.getLongDesc()); - properties.put("ShortDesc", child.getShortDesc()); + properties.put("name", name); + properties.put("type", type); + properties.put("addr", addr); + properties.put("serialNr", serialNr); DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties) - .withBridge(bridgeUID).withLabel(child.getLongDesc()).build(); + .withBridge(bridgeUID).withLabel(name).build(); thingDiscovered(discoveryResult); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandler.java index 90adb9068496a..db3fc9a96f50a 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandler.java @@ -1,7 +1,23 @@ +/** + * Copyright (c) 2010-2021 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.siemenshvac.internal.handler; import org.eclipse.jdt.annotation.NonNullByDefault; +/** + * + * @author Laurent Arnal - Initial contribution + */ @NonNullByDefault public interface SiemensHvacHandler { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index cea908971d930..fcc9eb5cf100d 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2020 Contributors to the openHAB project + * Copyright (c) 2010-2021 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -23,7 +23,6 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataDataPoint; import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataRegistry; -import org.openhab.binding.siemenshvac.internal.config.SiemensHvacConfiguration; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacCallback; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelGroupTypeProvider; @@ -65,7 +64,6 @@ public class SiemensHvacHandlerImpl extends BaseThingHandler implements SiemensH private @Nullable ScheduledFuture pollingJob = null; - private @Nullable SiemensHvacConfiguration config; private @Nullable SiemensHvacThingTypeProvider thingTypeProvider; private @Nullable SiemensHvacChannelTypeProvider channelTypeProvider; private @Nullable SiemensHvacChannelGroupTypeProvider channelGroupTypeProvider; @@ -122,10 +120,6 @@ public void initialize() { } }); - config = getConfigAs(SiemensHvacConfiguration.class); - var c1 = getThing().getConfiguration(); - var c2 = getBridge().getConfiguration(); - pollingJob = scheduler.scheduleWithFixedDelay(this::pollingCode, 0, 10, TimeUnit.SECONDS); } @@ -152,13 +146,17 @@ private void pollingCode() { ChannelType tp = channelTypeProvider.getInternalChannelType(ch.getChannelTypeUID()); + if (tp == null) { + continue; + } String dptId = ch.getProperties().get("dptId"); + String Id = ch.getProperties().get("id"); String groupId = ch.getProperties().get("groupdId"); String label = ch.getLabel(); String uid = ch.getUID().getId(); String type = tp.getItemType(); - ReadDp(dptId, uid, type, false); + ReadDp(Id, uid, type, true); logger.debug("" + isLink); } } @@ -194,9 +192,6 @@ public void DecodeReadDp(JsonObject response, @Nullable String uid, @Nullable St if (type == null) { logger.debug("siemensHvac:ReadDP:null type" + dp); } - if (typer == null) { - logger.debug("siemensHvac:ReadDP:null typer" + dp); - } if (typer.equals("Numeric")) { updateState(updateKey, new DecimalType(value.getAsDouble())); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java index 4cf9d9d7d64c7..814ea2b168962 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2020 Contributors to the openHAB project + * Copyright (c) 2010-2021 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -54,7 +54,6 @@ public void initialize() { super.initialize(); updateStatus(ThingStatus.ONLINE); - } @Override @@ -82,5 +81,4 @@ public void dispose() { public Collection> getServices() { return Collections.singleton(SiemensHvacDeviceDiscoveryService.class); } - } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java index 42bc5adbe4480..76f806a8ad2a6 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java @@ -14,6 +14,8 @@ public interface SiemensHvacConnector { public void onComplete(Request request); + public void onError(Request request); + public void setSiemensHvacBridgeBaseThingHandler( @Nullable SiemensHvacBridgeBaseThingHandler hvacBridgeBaseThingHandler); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 73a99ad6a075f..db7accbdd7ea3 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -73,13 +73,13 @@ public SiemensHvacConnectorImpl(@Reference HttpClientFactory httpClientFactory) private void initHttpClient() { this.httpClient = httpClientFactory.getCommonHttpClient(); + this.httpClient.setMaxConnectionsPerDestination(15); this.httpClientInsecure = new HttpClient(new SslContextFactory.Client(true)); this.httpClientInsecure.setRemoveIdleDestinations(true); this.httpClientInsecure.setMaxConnectionsPerDestination(15); try { this.httpClientInsecure.start(); } catch (Exception e) { - // catching exception is necessary due to the signature of HttpClient.start() logger.warn("Failed to start insecure http client: {}", e.getMessage()); } @@ -115,7 +115,16 @@ public void onComplete(@Nullable Request request) { } finally { lockObj.unlock(); } - logger.debug("unregisterCount:" + completedRequest + " " + request.getURI()); + } + + @Override + public void onError(@Nullable Request request) { + lockObj.lock(); + try { + completedRequest++; + } finally { + lockObj.unlock(); + } } private @Nullable ContentResponse executeRequest(final Request request, @Nullable SiemensHvacCallback callback) @@ -141,7 +150,6 @@ public void onComplete(@Nullable Request request) { lockObj.unlock(); } - logger.debug("registerCount:" + startedRequest + " " + request.getQuery()); request.send(requestListener); } else { response = request.send(); @@ -181,13 +189,10 @@ private void _doAuth() { ContentResponse response = executeRequest(request, null); int statusCode = response.getStatus(); - logger.debug("siemensHvac:doAuth:Endresponse:()" + statusCode); - if (statusCode == HttpStatus.OK_200) { String result = response.getContentAsString(); if (result != null) { - logger.debug("siemensHvac:doAuth:decodeResponse:()" + result); JsonObject resultObj = getGson().fromJson(result, JsonObject.class); if (resultObj.has("Result")) { @@ -250,8 +255,6 @@ private void _doAuth() { final Request request = httpClientInsecure.newRequest(baseUri + uri); request.method(HttpMethod.GET); - logger.debug("siemensHvac:DoBasicRequest()" + request); - ContentResponse response = executeRequest(request, callback); if (callback == null) { int statusCode = response.getStatus(); @@ -277,7 +280,6 @@ private void _doAuth() { String response = DoBasicRequest(req, callback); if (response != null) { - // logger.debug("siemensHvacDoRequest:responseSt:" + response); JsonObject resultObj = getGson().fromJson(response, JsonObject.class); @@ -307,11 +309,7 @@ private void _doAuth() { public void WaitAllPendingRequest() { logger.debug("WaitAllPendingRequest:start"); try { - logger.debug("WaitAllPendingRequest:start Initial Sleep"); Thread.sleep(1000); - logger.debug("WaitAllPendingRequest:end Initial Sleep"); - - logger.debug("WaitAllPendingRequest:start Wait Request"); boolean allRequestDone = false; while (!allRequestDone) { @@ -319,8 +317,9 @@ public void WaitAllPendingRequest() { allRequestDone = true; while (idx < 5 && allRequestDone) { - logger.debug("WaitAllPendingRequest:waitAllRequestDone " + idx + " " + startedRequest + "/" - + completedRequest); + logger.debug("WaitAllPendingRequest:waitAllRequestDone " + idx + " : " + + (startedRequest - completedRequest) + "(" + startedRequest + "/" + completedRequest + + ")"); if (startedRequest != completedRequest) { allRequestDone = false; } @@ -328,14 +327,10 @@ public void WaitAllPendingRequest() { idx++; } } - - logger.debug("WaitAllPendingRequest:end Wait"); - } catch (InterruptedException ex) { logger.debug("WaitAllPendingRequest:interrupted in WaitAllRequest"); } - logger.debug("WaitAllPendingRequest:end Wait"); logger.debug("WaitAllPendingRequest:end WaitAllPendingRequest"); } @@ -355,102 +350,10 @@ public static Gson getGsonWithAdapter() { return gson; } - /* - * - * - * public void WriteDp(String name, Type dp) { - * siemensHvacGenericBindingProvider provider = hvacBinding.getProvider(); - * siemensHvacBindingConfig bindingConfig = (siemensHvacBindingConfig) provider.getBindingConfigs().get(name); - * registry.VerifyBindingConfig(bindingConfig); - * int dpt = bindingConfig.getDptId(); - * String type = bindingConfig.getDptType(); - * - * String valAct = _readDpInternal(name, dpt, type); - * String valActEnum = valAct; - * String valActLabel = valAct; - * - * - * if (type.equals("Enumeration")) { - * String[] values = valAct.split(":"); - * valActEnum = values[0]; - * valActLabel = values[1]; - * } - * - * - * // Exit there if new value is the same as old value - * if (valAct != null && valUpdate.equals(valAct)) { - * return; - * } - * if (valActEnum != null && valUpdateEnum.equals(valActEnum)) { - * return; - * } - * - * siemensHvacBindingConfig config = (siemensHvacBindingConfig) provider.getBindingConfigs().get(name); - * registry.VerifyBindingConfig(bindingConfig); - * String dpType = config.getDptType(); - * - * String request = "api/menutree/write_datapoint.json?Id=" + dpt + "&Value=" + valUpdate + "&Type=" + dpType; - * - * JSONObject result = DoRequest(request); - * logger.debug("siemensHvac:WriteDP(response) = " + result); - * - * ReadDp(name, dpt, type); - * } - * - * - * - */ - public void AddDpUpdate(String itemName, Type dp) { synchronized (updateCommand) { updateCommand.put(itemName, dp); lastUpdate = new java.util.Date(); } } - - /* - * @Override - * public void run() { - * logger.debug("siemensHvac:sender thread start"); - * - * // as long as no interrupt is requested, continue running - * while (!interrupted) { - * try { - * Thread.sleep(2000); - * Date currentDate = new java.util.Date(); - * - * logger.debug("siemensHvac:sender thread alive:" + currentDate); - * - * if (lastUpdate == null) { - * continue; - * } - * - * long ms = currentDate.getTime() - lastUpdate.getTime(); - * if (ms < 3000) { - * continue; - * } - * - * synchronized (updateCommand) { - * if (updateCommand.isEmpty()) { - * continue; - * } - * - * logger.debug("siemensHvac:sender thread updateCommand"); - * - * for (String key : updateCommand.keySet()) { - * Type dp = updateCommand.get(key); - * WriteDp(key, dp); - * - * } - * - * updateCommand.clear(); - * } - * - * } catch (Exception e) { - * logger.error("siemensHvac:Error occured will sending update values", e); - * } - * } - * - * } - */ } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java index 15707c374ab46..c669d0f6b2568 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java @@ -1,6 +1,5 @@ package org.openhab.binding.siemenshvac.internal.network; -import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Response.CompleteListener; import org.eclipse.jetty.client.api.Response.ContentListener; @@ -8,7 +7,6 @@ import org.eclipse.jetty.client.api.Response.SuccessListener; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.util.BufferingResponseListener; -import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataRegistryImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,7 +16,7 @@ public class SiemensHvacRequestListener extends BufferingResponseListener implements SuccessListener, FailureListener, ContentListener, CompleteListener { - private static final Logger logger = LoggerFactory.getLogger(SiemensHvacMetadataRegistryImpl.class); + private static final Logger logger = LoggerFactory.getLogger(SiemensHvacRequestListener.class); private SiemensHvacConnector hvacConnector; /** @@ -37,17 +35,17 @@ public SiemensHvacRequestListener(SiemensHvacCallback callback, SiemensHvacConne } @Override - public void onSuccess(@NonNullByDefault({}) Response response) { + public void onSuccess(Response response) { // logger.debug("{} response: {}", response.getRequest().getURI(), response.getStatus()); } @Override - public void onFailure(@NonNullByDefault({}) Response response, @NonNullByDefault({}) Throwable failure) { + public void onFailure(Response response, Throwable failure) { logger.debug("response failed: {} {}", response.getRequest().getURI(), failure.getLocalizedMessage(), failure); } @Override - public void onComplete(@NonNullByDefault({}) Result result) { + public void onComplete(Result result) { hvacConnector.onComplete(result.getRequest()); try { @@ -60,36 +58,52 @@ public void onComplete(@NonNullByDefault({}) Result result) { } if (content != null) { - JsonObject resultObj = null; - try { - Gson gson = SiemensHvacConnectorImpl.getGson(); - resultObj = gson.fromJson(content, JsonObject.class); - } catch (Exception ex) { - logger.debug("error:" + ex.toString()); - } - - if (resultObj.has("Result")) { - JsonObject subResultObj = resultObj.getAsJsonObject("Result"); - - if (subResultObj.has("Success")) { - boolean resultVal = subResultObj.get("Success").getAsBoolean(); - - if (resultVal) { + if (content.indexOf("") >= 0) { + callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), content); + } else { + JsonObject resultObj = null; + try { + Gson gson = SiemensHvacConnectorImpl.getGson(); + resultObj = gson.fromJson(content, JsonObject.class); + } catch (Exception ex) { + logger.debug("error:" + ex.toString()); + } - callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), resultObj); - return; + if (resultObj.has("Result")) { + JsonObject subResultObj = resultObj.getAsJsonObject("Result"); + + if (subResultObj.has("Success")) { + boolean resultVal = subResultObj.get("Success").getAsBoolean(); + JsonObject error = subResultObj.getAsJsonObject("Error"); + String errorMsg = ""; + if (error != null) { + errorMsg = error.get("Txt").getAsString(); + } + + if (resultVal) { + + callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), + resultObj); + return; + } else if (errorMsg.equals("datatype not supported")) { + callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), + resultObj); + } else { + logger.debug("error : " + subResultObj); + hvacConnector.onError(result.getRequest()); + } } else { - logger.debug("error : " + subResultObj); + logger.debug("error"); + hvacConnector.onError(result.getRequest()); } + } else { logger.debug("error"); + hvacConnector.onError(result.getRequest()); } - } else { - logger.debug("error"); + return; } - - return; } callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), content); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java index 118a58e547452..b9e760ec6cab1 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2020 Contributors to the openHAB project + * Copyright (c) 2010-2021 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java index 7770245cfbe64..d9bfdfc3866e4 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2020 Contributors to the openHAB project + * Copyright (c) 2010-2021 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProviderImpl.java index f1032c763b9f5..aeae4393da116 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProviderImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProviderImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2020 Contributors to the openHAB project + * Copyright (c) 2010-2021 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java index 0f631653854a6..3492f340c52bd 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2020 Contributors to the openHAB project + * Copyright (c) 2010-2021 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index 4bb00a7083d24..cb2f2938ce0ee 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2020 Contributors to the openHAB project + * Copyright (c) 2010-2021 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -31,22 +31,135 @@ public class UidUtils { public static String sanetizeId(String st) { - st = st.replace(" ", "_"); - st = st.replace("é", "e"); - st = st.replace("è", "e"); - st = st.replace("ê", "e"); - - st = st.replace("î", "i"); - st = st.replace("ô", "o"); - st = st.replace("à", "a"); - st = st.replace("/", "_"); - st = st.replace(".", "_"); - st = st.replace("(", "_"); - st = st.replace(")", "_"); - st = st.replace("°", "_"); - st = st.replace("É", "E"); - - return st; + + StringBuffer buffer = new StringBuffer(); + + for (int i = 0; i < st.length(); i++) { + char c = st.charAt(i); + + if (c == 'é') { + c = 'e'; + } else if (c == 'è') { + c = 'e'; + } else if (c == 'ê') { + c = 'e'; + } else if (c == 'ë') { + c = 'e'; + } else if (c == 'ě') { + c = 'e'; + } else if (c == 'É') { + c = 'E'; + } else if (c == 'É') { + c = 'E'; + } + + else if (c == 'î') { + c = 'i'; + } else if (c == 'ï') { + c = 'i'; + } else if (c == 'í') { + c = 'i'; + } else if (c == 'í') { + c = 'i'; + } + + else if (c == 'ô') { + c = 'o'; + } else if (c == 'ó') { + c = 'o'; + } else if (c == 'ò') { + c = 'o'; + } else if (c == 'ö') { + c = 'o'; + } + + else if (c == 'ú') { + c = 'u'; + } else if (c == 'ù') { + c = 'u'; + } else if (c == 'û') { + c = 'u'; + } else if (c == 'ü') { + c = 'u'; + } else if (c == 'ů') { + c = 'u'; + } else if (c == 'Ú') { + c = 'U'; + } + + else if (c == 'à') { + c = 'a'; + } else if (c == 'ä') { + c = 'a'; + } else if (c == 'â') { + c = 'a'; + } else if (c == 'á') { + c = 'a'; + } + + else if (c == 'ř') { + c = 'r'; + } else if (c == 'ť') { + c = 't'; + } + + else if (c == 'š') { + c = 's'; + } + + else if (c == 'ý') { + c = 'y'; + } else if (c == 'ÿ') { + c = 'y'; + } + + else if (c == 'ž') { + c = 'z'; + } + + else if (c == 'ç') { + c = 'c'; + } else if (c == 'č') { + c = 'c'; + } else if (c == 'Č') { + c = 'C'; + } + + else if (c == 'Ž') { + c = 'Z'; + } + + else if (c == '_') { + c = '_'; + } else if (c == ' ') { + c = '_'; + } else if (c == '&') { + c = '_'; + } else if (c == '/') { + c = '_'; + } else if (c == '.') { + c = '_'; + } else if (c == '(') { + c = '_'; + } else if (c == ')') { + c = '_'; + } + + else if (c == '°') { + c = '_'; + } else if (c == '\'') { + c = '_'; + } + + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' || c == '-') { + } else { + c = '_'; + } + + buffer.append(c); + } + + return buffer.toString(); } /** @@ -69,7 +182,7 @@ public static ChannelTypeUID generateChannelTypeUID(SiemensHvacMetadataDataPoint String shortDesc = sanetizeId(dpt.getShortDesc()); return new ChannelTypeUID(SiemensHvacBindingConstants.BINDING_ID, - String.format("%s_%s_%s", dpt.getDptType(), dpt.getDptId(), shortDesc)); + String.format("%s_%s_%s", dpt.getDptType(), dpt.getId(), shortDesc)); } /** @@ -92,6 +205,6 @@ public static ChannelUID generateChannelUID(ThingUID thingUID) { * Generates the ChannelTypeUID for the given datapoint with deviceType and channelNumber. */ public static ChannelGroupTypeUID generateChannelGroupTypeUID(SiemensHvacMetadataMenu menu) { - return new ChannelGroupTypeUID(SiemensHvacBindingConstants.BINDING_ID, String.format("%s", menu.getMenuId())); + return new ChannelGroupTypeUID(SiemensHvacBindingConstants.BINDING_ID, String.format("%s", menu.getId())); } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml index f9ed10993f7d3..a69e0c9d33b27 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml @@ -9,16 +9,22 @@ This is a OZW672 IP interface - - - Network address of the Siemens Hvac IP gateway + + + The URL of the Siemens Hvac IP gateway network-address - - Port number of the Siemens Hvac gateway + + UserName of the Siemens Hvac gateway false - - 3671 + + Administrator + + + UserPassword of the Siemens Hvac gateway + false + + password From 755b570add81bab23e64ca6877cc382e08d02bc4 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 8 Jan 2023 11:59:25 +0100 Subject: [PATCH 004/214] move to 3.2.0 fix CommonIOUtils Signed-off-by: Laurent ARNAL --- bundles/org.openhab.binding.siemenshvac/pom.xml | 9 +++++---- .../Metadata/RuntimeTypeAdapterFactory.java | 16 ++++------------ .../SiemensHvacMetadataRegistryImpl.java | 8 +++++--- .../siemenshvac/internal/type/UidUtils.java | 1 + 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/pom.xml b/bundles/org.openhab.binding.siemenshvac/pom.xml index 84c8fa94a1d9b..20e62f24be141 100644 --- a/bundles/org.openhab.binding.siemenshvac/pom.xml +++ b/bundles/org.openhab.binding.siemenshvac/pom.xml @@ -1,16 +1,17 @@ - - + + 4.0.0 org.openhab.addons.bundles org.openhab.addons.reactor.bundles - 3.1.0-SNAPSHOT + 3.2.0-SNAPSHOT org.openhab.binding.siemenshvac - openHAB Add-ons :: Bundles :: SiemensHvac Binding + openHAB Add-ons::Bundles::SiemensHvac Binding diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java index 8b70cf68390ef..5d2d342562d2b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java @@ -42,9 +42,6 @@ import java.util.LinkedHashMap; import java.util.Map; -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; - import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @@ -93,7 +90,7 @@ *

* Without additional type information, the serialized JSON is ambiguous. Is * the bottom shape in this drawing a rectangle or a diamond? - * + * *

  *    {@code
  *   {
@@ -114,7 +111,7 @@
  * This class addresses this problem by adding type information to the
  * serialized JSON and honoring that type information when the JSON is
  * deserialized:
- *
+ * 
  * 
  *    {@code
  *   {
@@ -141,7 +138,7 @@
  * Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field
  * name to the {@link #of} factory method. If you don't supply an explicit type
  * field name, {@code "type"} will be used.
- *
+ * 
  * 
  * {
  *     @code
@@ -171,7 +168,7 @@
  * 
* * Like {@code GsonBuilder}, this API supports chaining: - * + * *
  * {
  *     @code
@@ -180,11 +177,6 @@
  * }
  * 
*/ -/** - * - * @author Laurent Arnal - Initial contribution - */ -@NonNullByDefault public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory { private final Class baseType; private final String typeFieldName; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index a436d4065fa5b..14dec1ef54f18 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -27,7 +27,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -69,6 +68,8 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import io.micrometer.core.instrument.util.IOUtils; + @Component(immediate = true) @NonNullByDefault public class SiemensHvacMetadataRegistryImpl implements SiemensHvacMetadataRegistry { @@ -934,8 +935,9 @@ public void SaveMetaDataToCache() { String js = SiemensHvacConnectorImpl.getGsonWithAdapter().toJson(root); - IOUtils.write(js, os); - IOUtils.closeQuietly(os); + byte[] bt = js.getBytes(); + os.write(bt); + os.flush(); } catch (IOException ioe) { logger.error("Couldn't write WithingsAccount to file '{}'.", file.getAbsolutePath()); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index cb2f2938ce0ee..235f82e3b9d35 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -160,6 +160,7 @@ else if (c == '°') { } return buffer.toString(); + } /** From faa0df6642db2d0f7ad2c94b69c1cae8db91ab1b Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 8 Jan 2023 12:00:11 +0100 Subject: [PATCH 005/214] code cleanup, review nullable annotations Signed-off-by: Laurent ARNAL --- .../org.openhab.binding.siemenshvac/pom.xml | 2 +- .../src/main/feature/feature.xml | 3 +- .../Metadata/RuntimeTypeAdapterFactory.java | 79 +-- .../Metadata/SiemensHvacMetadata.java | 17 +- .../SiemensHvacMetadataDataPoint.java | 71 ++- .../Metadata/SiemensHvacMetadataDevice.java | 30 +- .../Metadata/SiemensHvacMetadataMenu.java | 7 + .../SiemensHvacMetadataPointChild.java | 17 +- .../Metadata/SiemensHvacMetadataRegistry.java | 2 +- .../SiemensHvacMetadataRegistryImpl.java | 465 ++++++++++-------- .../SiemensHvacDeviceDiscoveryService.java | 75 +-- .../factory/SiemensHvacHandlerFactory.java | 15 +- .../SiemensHvacBridgeBaseThingHandler.java | 26 +- .../handler/SiemensHvacHandlerImpl.java | 163 +++--- .../SiemensHvacOZW672BridgeThingHandler.java | 2 +- .../internal/network/SiemensHvacCallback.java | 16 +- .../network/SiemensHvacConnector.java | 19 +- .../network/SiemensHvacConnectorImpl.java | 125 +++-- .../network/SiemensHvacRequestListener.java | 42 +- .../SiemensHvacChannelGroupTypeProvider.java | 6 +- ...emensHvacChannelGroupTypeProviderImpl.java | 12 +- .../type/SiemensHvacChannelTypeProvider.java | 8 +- .../SiemensHvacChannelTypeProviderImpl.java | 10 +- .../SiemensHvacConfigDescriptionProvider.java | 3 + ...mensHvacConfigDescriptionProviderImpl.java | 12 +- .../type/SiemensHvacThingTypeProvider.java | 8 +- .../SiemensHvacThingTypeProviderImpl.java | 12 +- .../siemenshvac/internal/type/UidUtils.java | 3 +- .../main/resources/OH-INF/thing/ozw672.xml | 7 +- 29 files changed, 734 insertions(+), 523 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/pom.xml b/bundles/org.openhab.binding.siemenshvac/pom.xml index 20e62f24be141..10316b6b9e076 100644 --- a/bundles/org.openhab.binding.siemenshvac/pom.xml +++ b/bundles/org.openhab.binding.siemenshvac/pom.xml @@ -7,7 +7,7 @@ org.openhab.addons.bundles org.openhab.addons.reactor.bundles - 3.2.0-SNAPSHOT + 3.3.0-SNAPSHOT org.openhab.binding.siemenshvac diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/feature/feature.xml b/bundles/org.openhab.binding.siemenshvac/src/main/feature/feature.xml index 47cf3e8717670..537a825b6c7f5 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/feature/feature.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/feature/feature.xml @@ -18,8 +18,7 @@ openhab-runtime-base - openhab-transport-upnp - esh-io-transport-upnp + openhab-transport-upnp mvn:org.openhab.addons.bundles/org.openhab.binding.siemenshvac/${project.version} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java index 5d2d342562d2b..8e93cd34a429e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java @@ -25,14 +25,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -/* - * Copied from - * https://raw.githubusercontent.com/google/gson/master/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java - * and repackaged here with additional content from - * com.google.gson.internal.{Streams,TypeAdapters,LazilyParsedNumber} - * to avoid using the internal package. - */ package org.openhab.binding.siemenshvac.internal.Metadata; import java.io.EOFException; @@ -42,6 +34,9 @@ import java.util.LinkedHashMap; import java.util.Map; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @@ -90,7 +85,7 @@ *

* Without additional type information, the serialized JSON is ambiguous. Is * the bottom shape in this drawing a rectangle or a diamond? - * + * *

  *    {@code
  *   {
@@ -111,7 +106,7 @@
  * This class addresses this problem by adding type information to the
  * serialized JSON and honoring that type information when the JSON is
  * deserialized:
- * 
+ *
  * 
  *    {@code
  *   {
@@ -138,7 +133,7 @@
  * Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field
  * name to the {@link #of} factory method. If you don't supply an explicit type
  * field name, {@code "type"} will be used.
- * 
+ *
  * 
  * {
  *     @code
@@ -168,7 +163,7 @@
  * 
* * Like {@code GsonBuilder}, this API supports chaining: - * + * *
  * {
  *     @code
@@ -177,6 +172,11 @@
  * }
  * 
*/ +/** + * + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory { private final Class baseType; private final String typeFieldName; @@ -184,9 +184,6 @@ public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory { private final Map, String> subtypeToLabel = new LinkedHashMap, String>(); private RuntimeTypeAdapterFactory(Class baseType, String typeFieldName) { - if (typeFieldName == null || baseType == null) { - throw new NullPointerException(); - } this.baseType = baseType; this.typeFieldName = typeFieldName; } @@ -215,9 +212,6 @@ public static RuntimeTypeAdapterFactory of(Class baseType) { * have already been registered on this type adapter. */ public RuntimeTypeAdapterFactory registerSubtype(Class type, String label) { - if (type == null || label == null) { - throw new NullPointerException(); - } if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) { throw new IllegalArgumentException("types and labels must be unique"); } @@ -238,8 +232,11 @@ public RuntimeTypeAdapterFactory registerSubtype(Class type) { } @Override - public TypeAdapter create(Gson gson, TypeToken type) { - if (type.getRawType() != baseType) { + public @Nullable TypeAdapter create(@Nullable Gson gson, @Nullable TypeToken type) { + if (type == null || type.getRawType() != baseType) { + return null; + } + if (gson == null) { return null; } @@ -253,25 +250,33 @@ public TypeAdapter create(Gson gson, TypeToken type) { return new TypeAdapter() { @Override - public R read(JsonReader in) throws IOException { + public @Nullable R read(JsonReader in) throws IOException { JsonElement jsonElement = RuntimeTypeAdapterFactory.parse(in); - JsonElement labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName); - if (labelJsonElement == null) { - throw new JsonParseException("cannot deserialize " + baseType - + " because it does not define a field named " + typeFieldName); - } - String label = labelJsonElement.getAsString(); - @SuppressWarnings("unchecked") // registration requires that subtype extends T - TypeAdapter delegate = (TypeAdapter) labelToDelegate.get(label); - if (delegate == null) { - throw new JsonParseException("cannot deserialize " + baseType + " subtype named " + label - + "; did you forget to register a subtype?"); + if (jsonElement != null) { + JsonElement labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName); + if (labelJsonElement == null) { + throw new JsonParseException("cannot deserialize " + baseType + + " because it does not define a field named " + typeFieldName); + } + String label = labelJsonElement.getAsString(); + @SuppressWarnings("unchecked") // registration requires that subtype extends T + TypeAdapter delegate = (TypeAdapter) labelToDelegate.get(label); + if (delegate == null) { + throw new JsonParseException("cannot deserialize " + baseType + " subtype named " + label + + "; did you forget to register a subtype?"); + } + return delegate.fromJsonTree(jsonElement); + } else { + throw new JsonParseException("cannot deserialize " + baseType + " because jsonElement is null"); } - return delegate.fromJsonTree(jsonElement); } @Override - public void write(JsonWriter out, R value) throws IOException { + public void write(JsonWriter out, @Nullable R value) throws IOException { + if (value == null) { + return; + } + Class srcType = value.getClass(); String label = subtypeToLabel.get(srcType); @SuppressWarnings("unchecked") // registration requires that subtype extends T @@ -298,7 +303,7 @@ public void write(JsonWriter out, R value) throws IOException { /** * Takes a reader in any state and returns the next value as a JsonElement. */ - private static JsonElement parse(JsonReader reader) throws JsonParseException { + private static @Nullable JsonElement parse(JsonReader reader) throws JsonParseException { boolean isEmpty = true; try { reader.peek(); @@ -332,7 +337,7 @@ private static void write(JsonElement element, JsonWriter writer) throws IOExcep private static final TypeAdapter JSON_ELEMENT = new TypeAdapter() { @Override - public JsonElement read(JsonReader in) throws IOException { + public @Nullable JsonElement read(JsonReader in) throws IOException { switch (in.peek()) { case STRING: return new JsonPrimitive(in.nextString()); @@ -370,7 +375,7 @@ public JsonElement read(JsonReader in) throws IOException { } @Override - public void write(JsonWriter out, JsonElement value) throws IOException { + public void write(JsonWriter out, @Nullable JsonElement value) throws IOException { if (value == null || value.isJsonNull()) { out.nullValue(); } else if (value.isJsonPrimitive()) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java index 8191cb2601e69..8e6d819046808 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java @@ -12,13 +12,22 @@ */ package org.openhab.binding.siemenshvac.internal.Metadata; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault public class SiemensHvacMetadata { private int Id = -1; private int subId = -1; private int groupId = -1; private int catId = -1; - private String shortDesc = null; - private String longDesc = null; + private String shortDesc = ""; + private String longDesc = ""; + @Nullable private transient SiemensHvacMetadata parent; public SiemensHvacMetadata() { @@ -72,11 +81,11 @@ public void setLongDesc(String longDesc) { this.longDesc = longDesc; } - public SiemensHvacMetadata getParent() { + public @Nullable SiemensHvacMetadata getParent() { return parent; } - public void setParent(SiemensHvacMetadata parent) { + public void setParent(@Nullable SiemensHvacMetadata parent) { this.parent = parent; } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java index a375e356ed565..6ee9a43cf652e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java @@ -15,28 +15,40 @@ import java.util.ArrayList; import java.util.List; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import com.google.gson.JsonArray; import com.google.gson.JsonObject; +/** + * + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault public class SiemensHvacMetadataDataPoint extends SiemensHvacMetadata { private String dptId = "-1"; - private String dptType = null; - private String dptUnit = null; - private String min = null; - private String max = null; - private String resolution = null; - private String fieldWitdh = null; - private String decimalDigits = null; + private String dptType = ""; + private @Nullable String dptUnit = null; + private @Nullable String min = null; + private @Nullable String max = null; + private @Nullable String resolution = null; + private @Nullable String fieldWitdh = null; + private @Nullable String decimalDigits = null; private Boolean detailsResolved = false; + @SuppressWarnings("unused") private Boolean unresolvableDetails = false; - private String dialogType = null; - private String maxLength = null; - private String address = null; + private @Nullable String dialogType = null; + private @Nullable String maxLength = null; + private @Nullable String address = null; private int dptSubKey = -1; private boolean writeAccess = false; - private List child = null; + private List child; + + public SiemensHvacMetadataDataPoint() { + child = new ArrayList(); + } public String getDptType() { return dptType; @@ -46,7 +58,7 @@ public void setDptType(String dptType) { this.dptType = dptType; } - public List getChild() { + public @Nullable List getChild() { return child; } @@ -70,7 +82,7 @@ public void setDptSubKey(int dptSubKey) { this.dptSubKey = dptSubKey; } - public String getAddress() { + public @Nullable String getAddress() { return address; } @@ -86,7 +98,7 @@ public void setAddress(String address) { this.address = address; } - public String getDptUnit() { + public @Nullable String getDptUnit() { return dptUnit; } @@ -94,7 +106,7 @@ public void setDptUnit(String dptUnit) { this.dptUnit = dptUnit; } - public String getMaxLength() { + public @Nullable String getMaxLength() { return maxLength; } @@ -102,7 +114,7 @@ public void setMaxLength(String maxLength) { this.maxLength = maxLength; } - public String getDialogType() { + public @Nullable String getDialogType() { return dialogType; } @@ -110,7 +122,7 @@ public void setDialogType(String dialogType) { this.dialogType = dialogType; } - public String getMin() { + public @Nullable String getMin() { return min; } @@ -118,7 +130,7 @@ public void setMin(String min) { this.min = min; } - public String getMax() { + public @Nullable String getMax() { return max; } @@ -126,7 +138,7 @@ public void setMax(String max) { this.max = max; } - public String getResolution() { + public @Nullable String getResolution() { return resolution; } @@ -134,7 +146,7 @@ public void setResolution(String resolution) { this.resolution = resolution; } - public String getFieldWitdh() { + public @Nullable String getFieldWitdh() { return fieldWitdh; } @@ -142,7 +154,7 @@ public void setFieldWitdh(String fieldWitdh) { this.fieldWitdh = fieldWitdh; } - public String getDecimalDigits() { + public @Nullable String getDecimalDigits() { return decimalDigits; } @@ -159,22 +171,17 @@ public void setDetailsResolved(Boolean detailsResolved) { } public void resolveDptDetails(JsonObject result) { - if (result == null) { - return; - } - JsonObject subResultObj = result.getAsJsonObject("Result"); JsonObject desc = result.getAsJsonObject("Description"); if (subResultObj.has("Success")) { - boolean resultVal = subResultObj.get("Success").getAsBoolean(); JsonObject error = subResultObj.getAsJsonObject("Error"); String errorMsg = ""; if (error != null) { errorMsg = error.get("Txt").getAsString(); } - if (errorMsg.equals("datatype not supported")) { + if (("datatype not supported").equals(errorMsg)) { detailsResolved = true; unresolvableDetails = true; return; @@ -188,7 +195,6 @@ public void resolveDptDetails(JsonObject result) { if (dptType.equals(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { JsonArray enums = desc.getAsJsonArray("Enums"); - child = new ArrayList(); for (Object obj : enums) { JsonObject entry = (JsonObject) obj; @@ -222,23 +228,14 @@ public void resolveDptDetails(JsonObject result) { ch.setOpt1(button.get("TextOpt1").getAsString()); ch.setIsActive(button.get("IsActive").getAsString()); child.add(ch); - - String signifiance = button.get("Significance").getAsString(); } } else if (dptType.equals(SiemensHvacBindingConstants.DPT_TYPE_DATE)) { - System.out.println(""); } else if (dptType.equals(SiemensHvacBindingConstants.DPT_TYPE_TIME)) { - System.out.println(""); } else if (dptType.equals(SiemensHvacBindingConstants.DPT_TYPE_SCHEDULER)) { - System.out.println(""); } else if (dptType.equals(SiemensHvacBindingConstants.DPT_TYPE_CALENDAR)) { - System.out.println(""); } else { - System.out.println(""); } detailsResolved = true; } - } - } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java index 36111de9984ce..0ba1861a50c49 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java @@ -12,13 +12,30 @@ */ package org.openhab.binding.siemenshvac.internal.Metadata; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault public class SiemensHvacMetadataDevice { - private String name; - private String addr; - private String type; - private String serialNr; + + private String name = ""; + + private String addr = ""; + + private String type = "unknow"; + + private String serialNr = ""; + + @Nullable private String treeDate; + + @Nullable private String treeTime; + private boolean treeGenerated; private int treeId; @@ -54,7 +71,7 @@ public void setSerialNr(String serialNr) { this.serialNr = serialNr; } - public String getTreeDate() { + public @Nullable String getTreeDate() { return treeDate; } @@ -62,7 +79,7 @@ public void setTreeDate(String treeDate) { this.treeDate = treeDate; } - public String getTreeTime() { + public @Nullable String getTreeTime() { return treeTime; } @@ -85,5 +102,4 @@ public int getTreeId() { public void setTreeId(int treeId) { this.treeId = treeId; } - } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java index 22417897f3020..4de7ae4c82950 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java @@ -15,6 +15,13 @@ import java.util.HashMap; import java.util.LinkedHashMap; +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault public class SiemensHvacMetadataMenu extends SiemensHvacMetadata { private LinkedHashMap childList; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java index f6fc7e6e741c8..38f934642af6d 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java @@ -12,13 +12,20 @@ */ package org.openhab.binding.siemenshvac.internal.Metadata; +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault public class SiemensHvacMetadataPointChild { - private String text; - private String value; - private String opt0; - private String opt1; - private String isActive; + private String text = ""; + private String value = ""; + private String opt0 = ""; + private String opt1 = ""; + private String isActive = ""; public String getText() { return this.text; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java index fb4a0fd785bf7..80fa23690fbfa 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java @@ -40,7 +40,7 @@ public interface SiemensHvacMetadataRegistry { ArrayList getDevices(); @Nullable - SiemensHvacMetadata getDptMap(String key); + SiemensHvacMetadata getDptMap(@Nullable String key); @Nullable SiemensHvacChannelTypeProvider getChannelTypeProvider(); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index 14dec1ef54f18..c7cbb7cbd1570 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -27,7 +27,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; @@ -70,8 +69,12 @@ import io.micrometer.core.instrument.util.IOUtils; -@Component(immediate = true) +/** + * + * @author Laurent Arnal - Initial contribution + */ @NonNullByDefault +@Component(immediate = true) public class SiemensHvacMetadataRegistryImpl implements SiemensHvacMetadataRegistry { private static final Logger logger = LoggerFactory.getLogger(SiemensHvacMetadataRegistryImpl.class); @@ -80,16 +83,12 @@ public class SiemensHvacMetadataRegistryImpl implements SiemensHvacMetadataRegis // protected List homematicThingTypeExcluders = new CopyOnWriteArrayList<>(); // A map contains data point config read from Api and/or WebPages - private @Nullable Map dptMap = null; + private Map dptMap = new Hashtable(); private @Nullable SiemensHvacMetadata root = null; private @Nullable ArrayList devices = null; - private boolean interrupted = false; - private static final String JSON_DIR = OpenHAB.getUserDataFolder() + File.separatorChar + "jsondb"; - private @Nullable static URI configDescriptionUriChannel; - private @Nullable SiemensHvacThingTypeProvider thingTypeProvider; private @Nullable SiemensHvacChannelTypeProvider channelTypeProvider; private @Nullable SiemensHvacChannelGroupTypeProvider channelGroupTypeProvider; @@ -168,7 +167,7 @@ protected void unsetConfigDescriptionProvider(SiemensHvacConfigDescriptionProvid public void initialize() { } - public void InitDptMap(@Nullable SiemensHvacMetadata node) { + public void InitDptMap(SiemensHvacMetadata node) { if (node.getClass() == SiemensHvacMetadataMenu.class) { SiemensHvacMetadataMenu mInformation = (SiemensHvacMetadataMenu) node; @@ -178,17 +177,16 @@ public void InitDptMap(@Nullable SiemensHvacMetadata node) { } } - if (node != null) { - if (node.getLongDesc() != null) { - dptMap.put("byName" + node.getLongDesc(), node); - } - if (node.getShortDesc() != null) { - dptMap.put("byName" + node.getShortDesc(), node); - } + if (!node.getLongDesc().isEmpty()) { + dptMap.put("byName" + node.getLongDesc(), node); + } + if (!node.getShortDesc().isEmpty()) { + dptMap.put("byName" + node.getShortDesc(), node); } dptMap.put("byId" + node.getId(), node); dptMap.put("bySubId" + node.getSubId(), node); + if (node.getClass() == SiemensHvacMetadataDataPoint.class) { SiemensHvacMetadataDataPoint dpi = (SiemensHvacMetadataDataPoint) node; dptMap.put("byDptId" + dpi.getDptId(), node); @@ -202,10 +200,12 @@ public void ResolveDetails() { } SiemensHvacMetadata node = dptMap.get(key); - if (node.getClass() == SiemensHvacMetadataDataPoint.class) { - SiemensHvacMetadataDataPoint dpi = (SiemensHvacMetadataDataPoint) node; - if (!dpi.getDetailsResolved()) { - resolveDptDetails(dpi); + if (node != null) { + if (node.getClass() == SiemensHvacMetadataDataPoint.class) { + SiemensHvacMetadataDataPoint dpi = (SiemensHvacMetadataDataPoint) node; + if (!dpi.getDetailsResolved()) { + resolveDptDetails(dpi); + } } } } @@ -219,10 +219,12 @@ public int UnresolveCount() { } SiemensHvacMetadata node = dptMap.get(key); - if (node.getClass() == SiemensHvacMetadataDataPoint.class) { - SiemensHvacMetadataDataPoint dpi = (SiemensHvacMetadataDataPoint) node; - if (!dpi.getDetailsResolved()) { - count++; + if (node != null) { + if (node.getClass() == SiemensHvacMetadataDataPoint.class) { + SiemensHvacMetadataDataPoint dpi = (SiemensHvacMetadataDataPoint) node; + if (!dpi.getDetailsResolved()) { + count++; + } } } } @@ -246,42 +248,52 @@ public void ReadMeta() { if (root == null) { root = new SiemensHvacMetadataMenu(); ReadMetaData(root, -1); - hvacConnector.WaitAllPendingRequest(); + if (hvacConnector != null) { + hvacConnector.WaitAllPendingRequest(); + } } - dptMap = new Hashtable(); - InitDptMap(root); + if (root != null) { + InitDptMap(root); + } int unresolveCount = Integer.MAX_VALUE; while (unresolveCount > 0) { ResolveDetails(); - hvacConnector.WaitAllPendingRequest(); + if (hvacConnector != null) { + hvacConnector.WaitAllPendingRequest(); + } unresolveCount = UnresolveCount(); } SaveMetaDataToCache(); - SiemensHvacMetadataMenu rootMenu = getRoot(); - for (SiemensHvacMetadataDevice device : devices) { - if (device.getType().indexOf("OZW672") >= 0) { - continue; - } + getRoot(); + if (devices != null) { + for (SiemensHvacMetadataDevice device : devices) { + if (device.getType().indexOf("OZW672") >= 0) { + continue; + } - generateThingsType(device); + generateThingsType(device); + } } logger.debug("siemensHvac:InitDptMap():end"); } - } private void generateThingsType(SiemensHvacMetadataDevice device) { - logger.debug("Generate thing types for device : " + device.getName() + "/" + device.getSerialNr()); + logger.debug("Generate thing types for device : {} / {}", device.getName(), device.getSerialNr()); if (thingTypeProvider != null) { ThingTypeUID thingTypeUID = UidUtils.generateThingTypeUID(device); - ThingType tt = thingTypeProvider.getInternalThingType(thingTypeUID); + ThingType tt = null; + + if (thingTypeProvider != null) { + tt = thingTypeProvider.getInternalThingType(thingTypeUID); + } if (tt == null) { @@ -291,70 +303,89 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { if (dptMap.containsKey("byId" + treeId)) { SiemensHvacMetadataMenu menu = (SiemensHvacMetadataMenu) dptMap.get("byId" + treeId); - for (SiemensHvacMetadata child : menu.getChilds().values()) { + if (menu != null) { + var childs = menu.getChilds().values(); + for (SiemensHvacMetadata child : childs) { - if (child instanceof SiemensHvacMetadataMenu) { - SiemensHvacMetadataMenu subMenu = (SiemensHvacMetadataMenu) child; + if (child instanceof SiemensHvacMetadataMenu) { + SiemensHvacMetadataMenu subMenu = (SiemensHvacMetadataMenu) child; - List channelDefinitions = new ArrayList<>(); + List channelDefinitions = new ArrayList<>(); - for (SiemensHvacMetadata childDt : subMenu.getChilds().values()) { + for (SiemensHvacMetadata childDt : subMenu.getChilds().values()) { - if (childDt instanceof SiemensHvacMetadataDataPoint) { - SiemensHvacMetadataDataPoint dataPoint = (SiemensHvacMetadataDataPoint) childDt; + if (childDt instanceof SiemensHvacMetadataDataPoint) { + SiemensHvacMetadataDataPoint dataPoint = (SiemensHvacMetadataDataPoint) childDt; - if (dataPoint.getDptType() == null) { - continue; - } + if (dataPoint.getDptType().isEmpty()) { + continue; + } - ChannelTypeUID channelTypeUID = UidUtils.generateChannelTypeUID(dataPoint); + ChannelTypeUID channelTypeUID = UidUtils.generateChannelTypeUID(dataPoint); - ChannelType channelType = channelTypeProvider - .getInternalChannelType(channelTypeUID); - if (channelType == null) { - channelType = createChannelType(dataPoint, channelTypeUID); - channelTypeProvider.addChannelType(channelType); - } + ChannelType channelType = null; + if (channelTypeProvider != null) { + + channelType = channelTypeProvider.getInternalChannelType(channelTypeUID); + if (channelType == null && channelTypeProvider != null) { + channelType = createChannelType(dataPoint, channelTypeUID); + if (channelTypeProvider != null) { + channelTypeProvider.addChannelType(channelType); + } + } + } + + SiemensHvacMetadataDataPoint dpt = ((SiemensHvacMetadataDataPoint) childDt); - SiemensHvacMetadataDataPoint dpt = ((SiemensHvacMetadataDataPoint) childDt); + Map props = new Hashtable(); + props.put("dptId", "" + dpt.getDptId()); + props.put("id", "" + dpt.getId()); + props.put("subId", "" + dpt.getSubId()); + props.put("groupdId", "" + dpt.getGroupId()); - Map props = new Hashtable(); - props.put("dptId", "" + dpt.getDptId()); - props.put("id", "" + dpt.getId()); - props.put("subId", "" + dpt.getSubId()); - props.put("groupdId", "" + dpt.getGroupId()); + String id = dataPoint.getId() + "_" + + UidUtils.sanetizeId(dataPoint.getShortDesc()); - String id = dataPoint.getId() + "_" + UidUtils.sanetizeId(dataPoint.getShortDesc()); - ChannelDefinition channelDef = new ChannelDefinitionBuilder(id, - channelType.getUID()).withLabel(dataPoint.getShortDesc()) - .withDescription(dataPoint.getLongDesc()).withProperties(props) - .build(); + if (channelType != null) { + ChannelDefinition channelDef = new ChannelDefinitionBuilder(id, + channelType.getUID()).withLabel(dataPoint.getShortDesc()) + .withDescription(dataPoint.getLongDesc()) + .withProperties(props).build(); - channelDefinitions.add(channelDef); + channelDefinitions.add(channelDef); + } + } } - } - // generate group - ChannelGroupTypeUID groupTypeUID = UidUtils.generateChannelGroupTypeUID(subMenu); - ChannelGroupType groupType = channelGroupTypeProvider - .getInternalChannelGroupType(groupTypeUID); - - if (groupType == null) { - String groupLabel = subMenu.getShortDesc(); - groupType = ChannelGroupTypeBuilder.instance(groupTypeUID, groupLabel) - .withChannelDefinitions(channelDefinitions).withCategory("") - .withDescription(menu.getLongDesc()).build(); - channelGroupTypeProvider.addChannelGroupType(groupType); - groupTypes.add(groupType); - } + // generate group + ChannelGroupTypeUID groupTypeUID = UidUtils.generateChannelGroupTypeUID(subMenu); + ChannelGroupType groupType = null; + + if (channelGroupTypeProvider != null) { + groupType = channelGroupTypeProvider.getInternalChannelGroupType(groupTypeUID); + + if (groupType == null) { + String groupLabel = subMenu.getShortDesc(); + groupType = ChannelGroupTypeBuilder.instance(groupTypeUID, groupLabel) + .withChannelDefinitions(channelDefinitions).withCategory("") + .withDescription(menu.getLongDesc()).build(); + if (channelGroupTypeProvider != null) { + channelGroupTypeProvider.addChannelGroupType(groupType); + } + groupTypes.add(groupType); + } + } + } } } } tt = createThingType(device, groupTypes); - thingTypeProvider.addThingType(tt); + if (thingTypeProvider != null) { + thingTypeProvider.addThingType(tt); + } } } } @@ -371,9 +402,12 @@ private ChannelType createChannelType(SiemensHvacMetadataDataPoint dpt, ChannelT List options = new ArrayList(); if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { - for (SiemensHvacMetadataPointChild opt : dpt.getChild()) { - StateOption stOpt = new StateOption(opt.getValue(), opt.getText()); - options.add(stOpt); + List childs = dpt.getChild(); + if (childs != null) { + for (SiemensHvacMetadataPointChild opt : childs) { + StateOption stOpt = new StateOption(opt.getValue(), opt.getText()); + options.add(stOpt); + } } } @@ -387,7 +421,7 @@ private ChannelType createChannelType(SiemensHvacMetadataDataPoint dpt, ChannelT stateFragment.withPattern(getStatePattern(dpt)).withReadOnly(dpt.getWriteAccess() == false); } - if (options != null && !options.isEmpty()) { + if (!options.isEmpty()) { stateFragment.withOptions(options); } @@ -412,7 +446,6 @@ private ChannelType createChannelType(SiemensHvacMetadataDataPoint dpt, ChannelT .build(); return channelType; - } /** @@ -431,7 +464,8 @@ private ThingType createThingType(SiemensHvacMetadataDevice device, List parms = new ArrayList<>(); List groups = new ArrayList<>(); - configDescriptionProvider.addConfigDescription(ConfigDescriptionBuilder.create(configDescriptionURI) - .withParameters(parms).withParameterGroups(groups).build()); + if (configDescriptionProvider != null) { + configDescriptionProvider.addConfigDescription(ConfigDescriptionBuilder.create(configDescriptionURI) + .withParameters(parms).withParameterGroups(groups).build()); + } } public static String getItemType(SiemensHvacMetadataDataPoint dpt) { - if (dpt.getDptType() != null) { - if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_STRING)) { - return SiemensHvacBindingConstants.ITEM_TYPE_STRING; - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { - return SiemensHvacBindingConstants.ITEM_TYPE_NUMBER; - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { - return SiemensHvacBindingConstants.ITEM_TYPE_NUMBER; - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_DATE)) { - return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_TIME)) { - return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_RADIO)) { - return SiemensHvacBindingConstants.ITEM_TYPE_CONTACT; - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_SCHEDULER)) { - return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_CALENDAR)) { - return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; - } else { - logger.debug("unknow type in getItemType()"); + if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_STRING)) { + return SiemensHvacBindingConstants.ITEM_TYPE_STRING; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { + return SiemensHvacBindingConstants.ITEM_TYPE_NUMBER; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { + return SiemensHvacBindingConstants.ITEM_TYPE_NUMBER; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_DATE)) { + return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_TIME)) { + return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_RADIO)) { + return SiemensHvacBindingConstants.ITEM_TYPE_CONTACT; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_SCHEDULER)) { + return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_CALENDAR)) { + return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; + } else { + logger.debug("unknow type in getItemType()"); - } } return ""; - } /** * Determines the category for the given Datapoint. */ public static String getCategory(SiemensHvacMetadataDataPoint dp) { - String dpDialog = dp.getDialogType(); - int dpCatId = dp.getCatId(); String dpType = dp.getDptType(); String dptUnit = dp.getDptUnit(); - if (dptUnit != null && dptUnit.contains("°C")) { + if (dptUnit == null) { + return ""; + } else if (dptUnit.contains("°C")) { return SiemensHvacBindingConstants.CATEGORY_CHANNEL_PROPS_TEMP; - } else if (dpType != null) { - if (dpType.contains("DateTime")) { - return SiemensHvacBindingConstants.CATEGORY_CHANNEL_PROPS_TIME; - } else if (dpType.contains("TimeOfDay")) { - return SiemensHvacBindingConstants.CATEGORY_CHANNEL_PROPS_TIME; - } else if (dpType.contains("Enumeration")) { - return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_SWITCH; - } else if (dpType.contains("RadioButton")) { - return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_SWITCH; - } else if (dpType.contains("Numeric")) { - return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_NUMBER; - } else { - return SiemensHvacBindingConstants.CATEGORY_CHANNEL_CONTROL_HEATING; - } + } else if (dpType.contains("DateTime")) { + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_PROPS_TIME; + } else if (dpType.contains("TimeOfDay")) { + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_PROPS_TIME; + } else if (dpType.contains("Enumeration")) { + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_SWITCH; + } else if (dpType.contains("RadioButton")) { + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_SWITCH; + } else if (dpType.contains("Numeric")) { + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_NUMBER; + } else { + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_CONTROL_HEATING; } - - return SiemensHvacBindingConstants.CATEGORY_CHANNEL_CONTROL_HEATING; - // String channelType = StringUtils.defaultString(dp.getChannel().getType()); - - // Number - // Text - // Switch - // Group - // Sun - // Moon - // Humidity - // Energy - // Water - // Pressure - // QualityOfService - // Flow - // Heating - // Alarm - // Vacation - } /** @@ -552,7 +563,7 @@ public static String getStatePattern(SiemensHvacMetadataDataPoint dpt) { return "%d %%"; } - if (unit != null && unit != "") { + if (unit != null && !unit.isEmpty()) { if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { return String.format("%s %s", "%d", "%unit%"); } @@ -566,8 +577,18 @@ public void ReadDeviceList() { devices = new ArrayList(); String request = "api/devicelist/list.json?"; - JsonObject response = hvacConnector.DoRequest(request, null); - JsonArray devicesList = response.getAsJsonArray("Devices"); + JsonObject response = null; + if (hvacConnector != null) { + response = hvacConnector.DoRequest(request, null); + } + JsonArray devicesList = null; + if (response != null) { + devicesList = response.getAsJsonArray("Devices"); + } + + if (devicesList == null) { + return; + } for (JsonElement device : devicesList) { @@ -618,56 +639,61 @@ public void ReadDeviceList() { deviceObj.setTreeGenerated(TreeGenerated); String request2 = "api/menutree/device_root.json?TreeName=Web&SerialNumber=" + SerialNr; - JsonObject response2 = hvacConnector.DoRequest(request2, null); - - if (response2.has("TreeItem")) { - JsonObject tree = response2.getAsJsonObject("TreeItem"); - if (tree.has("Id")) { - int treeId = tree.get("Id").getAsInt(); - deviceObj.setTreeId(treeId); + if (hvacConnector != null) { + JsonObject response2 = hvacConnector.DoRequest(request2, null); + + if (response2 != null && response2.has("TreeItem")) { + JsonObject tree = response2.getAsJsonObject("TreeItem"); + if (tree.has("Id")) { + int treeId = tree.get("Id").getAsInt(); + deviceObj.setTreeId(treeId); + } } } - devices.add(deviceObj); + if (devices != null) { + devices.add(deviceObj); + } } } catch (Exception e) { - logger.error("siemensHvac:ResolveDpt:Error during dp reading: " + e.getLocalizedMessage()); + logger.error("siemensHvac:ResolveDpt:Error during dp reading: {}", e.getLocalizedMessage()); // Reset sessionId so we redone _auth on error } - } - public void ReadMetaData(SiemensHvacMetadata parent, int id) { + public void ReadMetaData(@Nullable SiemensHvacMetadata parent, int id) { try { String request = "api/menutree/list.json?"; if (id != -1) { request = request + "&Id=" + id; } - hvacConnector.DoRequest(request, new SiemensHvacCallback() { + if (hvacConnector != null) { + hvacConnector.DoRequest(request, new SiemensHvacCallback() { - @Override - public void execute(URI uri, int status, @Nullable Object response) { - if (response instanceof JsonObject) { - DecodeMetaDataResult((JsonObject) response, parent, id); + @Override + public void execute(URI uri, int status, @Nullable Object response) { + if (response instanceof JsonObject) { + DecodeMetaDataResult((JsonObject) response, parent, id); + } } - } - }); + }); + } } catch (Exception e) { - logger.error("siemensHvac:ResolveDpt:Error during dp reading: " + id + " ; " + e.getLocalizedMessage()); + logger.error("siemensHvac:ResolveDpt:Error during dp reading: {} ; {}", id, e.getLocalizedMessage()); // Reset sessionId so we redone _auth on error } - } + @SuppressWarnings("unused") private static int nbDpt = 0; - public void DecodeMetaDataResult(JsonObject resultObj, SiemensHvacMetadata parent, int id) { + public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMetadata parent, int id) { if (resultObj.has("MenuItems")) { if (parent != null) { - logger.debug("Decode menuItem for :" + parent.getShortDesc()); + logger.debug("Decode menuItem for : {}", parent.getShortDesc()); } SiemensHvacMetadata childNode; JsonArray menuItems = resultObj.getAsJsonArray("MenuItems"); @@ -715,7 +741,9 @@ public void DecodeMetaDataResult(JsonObject resultObj, SiemensHvacMetadata paren childNode.setGroupId(groupId); childNode.setShortDesc(shortDesc); childNode.setLongDesc(longDesc); - ((SiemensHvacMetadataMenu) parent).AddChild(childNode); + if (parent != null) { + ((SiemensHvacMetadataMenu) parent).AddChild(childNode); + } // logger.debug(String.format("siemensHvac:ResolveDpt():findMenuItem: %d, %s, %s, %s, %s", itemId, // subItemId, groupId, catId, longDesc)); @@ -759,7 +787,7 @@ public void DecodeMetaDataResult(JsonObject resultObj, SiemensHvacMetadata paren { if (parent != null) { - logger.debug("Decode dp for :" + parent.getShortDesc()); + logger.debug("Decode dp for : {}", parent.getShortDesc()); } SiemensHvacMetadata childNode; @@ -837,7 +865,9 @@ public void DecodeMetaDataResult(JsonObject resultObj, SiemensHvacMetadata paren // catId, groupId, subItemId, shortDesc, longDesc)); } - ((SiemensHvacMetadataMenu) parent).AddChild(childNode); + if (parent != null) { + ((SiemensHvacMetadataMenu) parent).AddChild(childNode); + } } @@ -846,44 +876,52 @@ public void DecodeMetaDataResult(JsonObject resultObj, SiemensHvacMetadata paren request2 = request2 + "&id=" + id; } - hvacConnector.DoRequest(request2, new SiemensHvacCallback() { + if (hvacConnector != null) { + hvacConnector.DoRequest(request2, new SiemensHvacCallback() { - @Override - public void execute(URI uri, int status, @Nullable Object response) { + @Override + public void execute(URI uri, int status, @Nullable Object response) { - String st = (String) response; - st = st.replace("\n", ""); + if (response != null) { + String st = (String) response; + st = st.replace("\n", ""); - Pattern pattern = Pattern - .compile("td class=\\\"dp_linenumber\\\".*?>(.*?)<\\/td>.+?(?=id)id=\"dp(.+?)\""); - Matcher matcher = pattern.matcher(st); + Pattern pattern = Pattern + .compile("td class=\\\"dp_linenumber\\\".*?>(.*?)<\\/td>.+?(?=id)id=\"dp(.+?)\""); + Matcher matcher = pattern.matcher(st); - while (matcher.find()) { - String all = matcher.group(0); - String id = matcher.group(2); - String dptId = matcher.group(1); + while (matcher.find()) { + String id = matcher.group(2); + String dptId = matcher.group(1); - if ((!StringUtils.isEmpty(id)) && (!StringUtils.isEmpty(dptId))) { + if (id != null && dptId != null && !id.isEmpty() && !dptId.isEmpty()) { - if (idMap.containsKey(id)) { - SiemensHvacMetadataDataPoint child = idMap.get(id); - child.setDptId(dptId); - } + if (idMap.containsKey(id)) { + SiemensHvacMetadataDataPoint child = idMap.get(id); + if (child != null) { + child.setDptId(dptId); + } + } + } + } } } - } - }); + }); + } } if (resultObj.has("WidgetItems")) { // JSONArray wgItems = (JSONArray) result.get("WidgetItems"); } - } @Override - public @Nullable SiemensHvacMetadata getDptMap(String key) { + public @Nullable SiemensHvacMetadata getDptMap(@Nullable String key) { + + if (key == null) { + return null; + } if (dptMap.containsKey("byMenu" + key)) { return dptMap.get("byMenu" + key); @@ -896,7 +934,6 @@ public void execute(URI uri, int status, @Nullable Object response) { } return null; - } public void LoadMetaDataFromCache() { @@ -917,7 +954,6 @@ public void LoadMetaDataFromCache() { logger.error("Couldn't write WithingsAccount to file '{}'.", file.getAbsolutePath()); } - } public void SaveMetaDataToCache() { @@ -931,13 +967,13 @@ public void SaveMetaDataToCache() { file.createNewFile(); } - FileOutputStream os = new FileOutputStream(file); - - String js = SiemensHvacConnectorImpl.getGsonWithAdapter().toJson(root); + try (FileOutputStream os = new FileOutputStream(file)) { + String js = SiemensHvacConnectorImpl.getGsonWithAdapter().toJson(root); - byte[] bt = js.getBytes(); - os.write(bt); - os.flush(); + byte[] bt = js.getBytes(); + os.write(bt); + os.flush(); + } } catch (IOException ioe) { logger.error("Couldn't write WithingsAccount to file '{}'.", file.getAbsolutePath()); @@ -951,17 +987,18 @@ public void resolveDptDetails(SiemensHvacMetadataDataPoint dpt) { } String request = "api/menutree/datapoint_desc.json?Id=" + dpt.getId(); - hvacConnector.DoRequest(request, new SiemensHvacCallback() { - - @Override - public void execute(URI uri, int status, @Nullable Object response) { - if (response instanceof JsonObject) { - dpt.resolveDptDetails((JsonObject) response); - } else { - logger.debug("errror"); + if (hvacConnector != null) { + hvacConnector.DoRequest(request, new SiemensHvacCallback() { + + @Override + public void execute(URI uri, int status, @Nullable Object response) { + if (response instanceof JsonObject) { + dpt.resolveDptDetails((JsonObject) response); + } else { + logger.debug("errror"); + } } - } - }); + }); + } } - } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java index a063197c8c076..39b22d2cb4a32 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java @@ -18,12 +18,12 @@ import java.util.Map; import java.util.Set; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataDevice; import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataRegistry; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeBaseThingHandler; -import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; import org.openhab.binding.siemenshvac.internal.type.UidUtils; import org.openhab.core.config.discovery.AbstractDiscoveryService; import org.openhab.core.config.discovery.DiscoveryResult; @@ -43,6 +43,7 @@ * @author Laurent Arnal - Initial contribution */ // @Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.siemenshvac") +@NonNullByDefault public class SiemensHvacDeviceDiscoveryService extends AbstractDiscoveryService implements DiscoveryService, ThingHandlerService { @@ -50,7 +51,6 @@ public class SiemensHvacDeviceDiscoveryService extends AbstractDiscoveryService private @Nullable SiemensHvacMetadataRegistry metadataRegistry; private @Nullable SiemensHvacBridgeBaseThingHandler siemensHvacBridgeHandler; - private @Nullable SiemensHvacConnector hvacConnector; public static final Set SUPPORTED_THING_TYPES = Collections .singleton(SiemensHvacBindingConstants.THING_TYPE_OZW672); @@ -61,7 +61,7 @@ public SiemensHvacDeviceDiscoveryService() { } @Reference - public void setSiemensHvacMetadataRegistry(SiemensHvacMetadataRegistry metadataRegistry) { + public void setSiemensHvacMetadataRegistry(@Nullable SiemensHvacMetadataRegistry metadataRegistry) { this.metadataRegistry = metadataRegistry; } @@ -81,9 +81,7 @@ protected void stopBackgroundDiscovery() { private @Nullable ThingUID getThingUID(ThingTypeUID thingTypeUID, String serial) { if (siemensHvacBridgeHandler != null) { ThingUID localBridgeUID = siemensHvacBridgeHandler.getThing().getUID(); - if (localBridgeUID != null) { - return new ThingUID(thingTypeUID, localBridgeUID, serial); - } + return new ThingUID(thingTypeUID, localBridgeUID, serial); } return null; } @@ -92,50 +90,56 @@ protected void stopBackgroundDiscovery() { public void startScan() { logger.debug("call startScan()"); - final SiemensHvacBridgeBaseThingHandler handler = siemensHvacBridgeHandler; - if (metadataRegistry != null) { metadataRegistry.ReadMeta(); // SiemensHvacMetadataMenu rootMenu = metadataRegistry.getRoot(); // for (SiemensHvacMetadata child : rootMenu.getChilds().values()) { - ArrayList devices = metadataRegistry.getDevices(); - for (SiemensHvacMetadataDevice device : devices) { - - String name = device.getName(); - String type = device.getType(); - String addr = device.getAddr(); - String serialNr = device.getSerialNr(); + if (metadataRegistry != null) { + ArrayList devices = metadataRegistry.getDevices(); - if (name.indexOf("OZW672") >= 0) { - continue; + if (devices == null) { + return; } - logger.debug("Find devices: " + name + "/" + type + "/" + addr + "/" + serialNr); + for (SiemensHvacMetadataDevice device : devices) { - String typeSn = UidUtils.sanetizeId(type); - String nameSn = UidUtils.sanetizeId(name); - ThingTypeUID thingTypeUID = new ThingTypeUID(SiemensHvacBindingConstants.BINDING_ID, typeSn); + String name = device.getName(); + String type = device.getType(); + String addr = device.getAddr(); + String serialNr = device.getSerialNr(); - ThingUID thingUID = getThingUID(thingTypeUID, serialNr); - ThingUID bridgeUID = siemensHvacBridgeHandler.getThing().getUID(); + if (name.indexOf("OZW672") >= 0) { + continue; + } - if (thingUID != null) { - Map properties = new HashMap<>(1); - properties.put("name", name); - properties.put("type", type); - properties.put("addr", addr); - properties.put("serialNr", serialNr); + logger.debug("Find devices: {} / {} / {} / {}", name, type, addr, serialNr); - DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties) - .withBridge(bridgeUID).withLabel(name).build(); + String typeSn = UidUtils.sanetizeId(type); + ThingTypeUID thingTypeUID = new ThingTypeUID(SiemensHvacBindingConstants.BINDING_ID, typeSn); - thingDiscovered(discoveryResult); - } + ThingUID thingUID = getThingUID(thingTypeUID, serialNr); + + if (siemensHvacBridgeHandler != null) { + ThingUID bridgeUID = siemensHvacBridgeHandler.getThing().getUID(); + + if (thingUID != null) { + Map properties = new HashMap<>(1); + properties.put("name", name); + properties.put("type", type); + properties.put("addr", addr); + properties.put("serialNr", serialNr); + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID) + .withProperties(properties).withBridge(bridgeUID).withLabel(name).build(); + + thingDiscovered(discoveryResult); + } + } + + } } } - } @Override @@ -149,7 +153,6 @@ public void setThingHandler(@Nullable ThingHandler handler) { siemensHvacBridgeHandler = (SiemensHvacBridgeBaseThingHandler) handler; // bridgeUID = handler.getThing().getUID(); } - } @Override @@ -163,7 +166,6 @@ public void activate() { if (handler != null) { handler.registerDiscoveryListener(this); } - } @Override @@ -176,5 +178,4 @@ public void deactivate() { * } */ } - } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java index 2b563692ed4a3..26673d5d00e0a 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2020 Contributors to the openHAB project + * Copyright (c) 2010-2021 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -44,16 +44,17 @@ @Component(service = ThingHandlerFactory.class, configurationPid = "binding.siemenshvac") public class SiemensHvacHandlerFactory extends BaseThingHandlerFactory { + @SuppressWarnings("unused") private final Logger logger = LoggerFactory.getLogger(SiemensHvacHandlerFactory.class); private @Nullable NetworkAddressService networkAddressService; private @Nullable HttpClientFactory httpClientFactory; - private @Nullable SiemensHvacMetadataRegistry metaDataRegistry; + private SiemensHvacMetadataRegistry metaDataRegistry; // @Activate - public SiemensHvacHandlerFactory(final @Reference HttpClientFactory httpClientFactory, + public SiemensHvacHandlerFactory(@Nullable final @Reference HttpClientFactory httpClientFactory, final @Reference SiemensHvacMetadataRegistry metaDataRegistry) { this.httpClientFactory = httpClientFactory; this.metaDataRegistry = metaDataRegistry; @@ -75,14 +76,15 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { return super.createThing(thingTypeUID, configuration, thingUID, bridgeUID); } throw new IllegalArgumentException("The thing type " + thingTypeUID + " is not supported by the KNX binding."); - } @Override protected @Nullable ThingHandler createHandler(Thing thing) { if (thing.getThingTypeUID().equals(SiemensHvacBindingConstants.THING_TYPE_OZW672)) { - return new SiemensHvacOZW672BridgeThingHandler((Bridge) thing, networkAddressService, httpClientFactory, - metaDataRegistry); + if (networkAddressService != null) { + return new SiemensHvacOZW672BridgeThingHandler((Bridge) thing, networkAddressService, httpClientFactory, + metaDataRegistry); + } } else if (SiemensHvacBindingConstants.BINDING_ID.equals(thing.getThingTypeUID().getBindingId())) { SiemensHvacHandlerImpl handler = new SiemensHvacHandlerImpl(thing); handler.setChannelTypeProvider(metaDataRegistry.getChannelTypeProvider()); @@ -110,5 +112,4 @@ protected void setNetworkAddressService(NetworkAddressService networkAddressServ protected void unsetNetworkAddressService(NetworkAddressService networkAddressService) { this.networkAddressService = null; } - } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java index f87a8442bfad9..fd01e49149501 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2020 Contributors to the openHAB project + * Copyright (c) 2010-2021 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -16,6 +16,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataRegistry; import org.openhab.binding.siemenshvac.internal.discovery.SiemensHvacDeviceDiscoveryService; +import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.net.NetworkAddressService; import org.openhab.core.thing.Bridge; @@ -39,17 +40,23 @@ public abstract class SiemensHvacBridgeBaseThingHandler extends BaseBridgeHandle // private final ScheduledExecutorService backgroundScheduler = Executors.newSingleThreadScheduledExecutor(); private @Nullable SiemensHvacDeviceDiscoveryService discoveryService; - private @Nullable final NetworkAddressService networkAddressService; - private @Nullable final HttpClientFactory httpClientFactory; - private @Nullable final SiemensHvacMetadataRegistry metaDataRegistry; + @SuppressWarnings("unused") + private final @Nullable NetworkAddressService networkAddressService; + private final @Nullable HttpClientFactory httpClientFactory; + private final SiemensHvacMetadataRegistry metaDataRegistry; + private @Nullable SiemensHvacConnector connector; public SiemensHvacBridgeBaseThingHandler(Bridge bridge, @Nullable NetworkAddressService networkAddressService, - @Nullable HttpClientFactory httpClientFactory, @Nullable SiemensHvacMetadataRegistry metaDataRegistry) { + @Nullable HttpClientFactory httpClientFactory, SiemensHvacMetadataRegistry metaDataRegistry) { super(bridge); this.networkAddressService = networkAddressService; this.httpClientFactory = httpClientFactory; this.metaDataRegistry = metaDataRegistry; - this.metaDataRegistry.getSiemensHvacConnector().setSiemensHvacBridgeBaseThingHandler(this); + + connector = this.metaDataRegistry.getSiemensHvacConnector(); + if (connector != null) { + connector.setSiemensHvacBridgeBaseThingHandler(this); + } } @Override @@ -69,11 +76,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { @Override public void initialize() { - - if (metaDataRegistry != null) { - metaDataRegistry.ReadMeta(); - } - + metaDataRegistry.ReadMeta(); } @Override @@ -109,5 +112,4 @@ public boolean unregisterDiscoveryListener() { public @Nullable HttpClientFactory getHttpClientFactory() { return this.httpClientFactory; } - } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index fcc9eb5cf100d..422111cbba892 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -25,10 +25,7 @@ import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataRegistry; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacCallback; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; -import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelGroupTypeProvider; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelTypeProvider; -import org.openhab.binding.siemenshvac.internal.type.SiemensHvacConfigDescriptionProvider; -import org.openhab.binding.siemenshvac.internal.type.SiemensHvacThingTypeProvider; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.PercentType; @@ -38,6 +35,7 @@ import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.thing.binding.ThingHandlerCallback; import org.openhab.core.thing.type.ChannelType; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; @@ -64,10 +62,7 @@ public class SiemensHvacHandlerImpl extends BaseThingHandler implements SiemensH private @Nullable ScheduledFuture pollingJob = null; - private @Nullable SiemensHvacThingTypeProvider thingTypeProvider; private @Nullable SiemensHvacChannelTypeProvider channelTypeProvider; - private @Nullable SiemensHvacChannelGroupTypeProvider channelGroupTypeProvider; - private @Nullable SiemensHvacConfigDescriptionProvider configDescriptionProvider; private @Nullable SiemensHvacConnector hvacConnector; private @Nullable SiemensHvacMetadataRegistry metaDataRegistry; @@ -125,43 +120,57 @@ public void initialize() { @Override public void dispose() { - pollingJob.cancel(true); + if (pollingJob != null) { + pollingJob.cancel(true); + } } private void pollingCode() { var chList = this.getThing().getChannels(); for (Channel ch : chList) { - logger.debug(ch.getDescription()); + logger.debug("{}", ch.getDescription()); + + ThingHandlerCallback cb = this.getCallback(); + boolean isLink = false; - boolean isLink = this.getCallback().isChannelLinked(ch.getUID()); + if (cb != null) { + isLink = cb.isChannelLinked(ch.getUID()); + } if (!isLink) { continue; } - if (channelTypeProvider == null) { - return; - } + ChannelType tp = null; - ChannelType tp = channelTypeProvider.getInternalChannelType(ch.getChannelTypeUID()); + if (channelTypeProvider != null) { + tp = channelTypeProvider.getInternalChannelType(ch.getChannelTypeUID()); + } if (tp == null) { continue; } - String dptId = ch.getProperties().get("dptId"); + String Id = ch.getProperties().get("id"); - String groupId = ch.getProperties().get("groupdId"); - String label = ch.getLabel(); String uid = ch.getUID().getId(); String type = tp.getItemType(); + if (Id == null) { + logger.debug("poolingCode : Id is null {} ", ch); + continue; + } + if (type == null) { + logger.debug("poolingCode : type is null {} ", ch); + continue; + } ReadDp(Id, uid, type, true); - logger.debug("" + isLink); + logger.debug("{}", isLink); } } - public void DecodeReadDp(JsonObject response, @Nullable String uid, @Nullable String dp, @Nullable String type) { + public void DecodeReadDp(@Nullable JsonObject response, @Nullable String uid, @Nullable String dp, + @Nullable String type) { if (response != null && response.has("Data")) { JsonObject subResult = (JsonObject) response.get("Data"); @@ -169,8 +178,6 @@ public void DecodeReadDp(JsonObject response, @Nullable String uid, @Nullable St String typer = ""; JsonElement value = null; JsonElement enumValue = null; - String result = ""; - String unit = ""; if (subResult.has("Type")) { typer = subResult.get("Type").getAsString().trim(); @@ -181,27 +188,26 @@ public void DecodeReadDp(JsonObject response, @Nullable String uid, @Nullable St if (subResult.has("EnumValue")) { enumValue = subResult.get("EnumValue"); } - if (subResult.has("Unit")) { - unit = subResult.get("Unit").toString().trim(); - } if (value == null) { return; } if (type == null) { - logger.debug("siemensHvac:ReadDP:null type" + dp); + logger.debug("siemensHvac:ReadDP:null type {}", dp); } - if (typer.equals("Numeric")) { + if (("Numeric").equals(typer)) { updateState(updateKey, new DecimalType(value.getAsDouble())); - } else if (typer.equals("Enumeration")) { - updateState(updateKey, new DecimalType(enumValue.getAsInt())); - } else if (typer.equals("Text")) { + } else if (("Enumeration").equals(typer)) { + if (enumValue != null) { + updateState(updateKey, new DecimalType(enumValue.getAsInt())); + } + } else if (("Text").equals(typer)) { updateState(updateKey, new StringType(value.getAsString())); - } else if (typer.equals("RadioButton")) { + } else if (("RadioButton").equals(typer)) { updateState(updateKey, new StringType(value.getAsString())); - } else if (typer.equals("DayOfTime") || typer.equals("DateTime")) { + } else if (("DayOfTime").equals(typer) || ("DateTime").equals(typer)) { try { SimpleDateFormat dtf = new SimpleDateFormat("EEEE, d. MMMM yyyy hh:mm"); // first example ZonedDateTime zdt = dtf.parse(value.getAsString()).toInstant().atZone(ZoneId.systemDefault()); @@ -216,47 +222,50 @@ public void DecodeReadDp(JsonObject response, @Nullable String uid, @Nullable St } } - private void ReadDp(@Nullable String dp, String uid, @Nullable String type, boolean async) { - if (dp.equals("-1")) { + private void ReadDp(String dp, String uid, String type, boolean async) { + if (("-1").equals(dp)) { return; } - try { + try + + { String request = "api/menutree/read_datapoint.json?Id=" + dp; // logger.debug("siemensHvac:ReadDp:DoRequest():" + request); if (async) { - hvacConnector.DoRequest(request, new SiemensHvacCallback() { - - @Override - public void execute(java.net.URI uri, int status, @Nullable Object response) { - if (response instanceof JsonObject) { - DecodeReadDp((JsonObject) response, uid, dp, type); + if (hvacConnector != null) { + hvacConnector.DoRequest(request, new SiemensHvacCallback() { + + @Override + public void execute(java.net.URI uri, int status, @Nullable Object response) { + if (response instanceof JsonObject) { + DecodeReadDp((JsonObject) response, uid, dp, type); + } } - } - - }); + }); + } } else { - JsonObject js = hvacConnector.DoRequest(request, null); - DecodeReadDp(js, uid, dp, type); + if (hvacConnector != null) { + JsonObject js = hvacConnector.DoRequest(request, null); + DecodeReadDp(js, uid, dp, type); + } } - } catch (Exception e) { - logger.error("siemensHvac:ReadDp:Error during dp reading: " + dp + " ; " + e.getLocalizedMessage()); + logger.error("siemensHvac:ReadDp:Error during dp reading: {} ; {}", dp, e.getLocalizedMessage()); // Reset sessionId so we redone _auth on error } } - private void WriteDp(@Nullable String dp, Type dpVal, @Nullable String type) { - if (dp.equals("-1")) { + private void WriteDp(String dp, Type dpVal, String type) { + if (("-1").equals(dp)) { return; } try { String valUpdate = "0"; String valUpdateEnum = ""; - String valUpdateLabel = ""; if (dpVal instanceof PercentType) { PercentType pct = (PercentType) dpVal; @@ -268,36 +277,41 @@ private void WriteDp(@Nullable String dp, Type dpVal, @Nullable String type) { StringType bdc = (StringType) dpVal; valUpdate = bdc.toString(); - if (type.equals("Enumeration")) { + if (("Enumeration").equals(type)) { String[] valuesUpdateDp = valUpdate.split(":"); valUpdateEnum = valuesUpdateDp[0]; - valUpdateLabel = valuesUpdateDp[1]; // For enumeration, we always update using the raw value valUpdate = valUpdateEnum; } } - SiemensHvacMetadataDataPoint md = (SiemensHvacMetadataDataPoint) metaDataRegistry.getDptMap(dp); - String dptType = md.getDptType(); + SiemensHvacMetadataDataPoint md = null; + String dptType = null; - String request = "api/menutree/write_datapoint.json?Id=" + dp + "&Value=" + valUpdate + "&Type=" + dptType; + if (metaDataRegistry != null) { + md = (SiemensHvacMetadataDataPoint) metaDataRegistry.getDptMap(dp); + } + if (md != null) { + dptType = md.getDptType(); + } - // logger.debug("siemensHvac:ReadDp:DoRequest():" + request); + String request = "api/menutree/write_datapoint.json?Id=" + dp + "&Value=" + valUpdate + "&Type=" + dptType; - hvacConnector.DoRequest(request, new SiemensHvacCallback() { + if (hvacConnector != null) { + hvacConnector.DoRequest(request, new SiemensHvacCallback() { - @Override - public void execute(java.net.URI uri, int status, @Nullable Object response) { - if (response instanceof JsonObject) { - logger.debug("p1"); + @Override + public void execute(java.net.URI uri, int status, @Nullable Object response) { + if (response instanceof JsonObject) { + logger.debug("p1"); + } } - } - - }); + }); + } } catch (Exception e) { - logger.error("siemensHvac:ReadDp:Error during dp reading: " + dp + " ; " + e.getLocalizedMessage()); + logger.error("siemensHvac:ReadDp:Error during dp reading: {} ; {}", dp, e.getLocalizedMessage()); // Reset sessionId so we redone _auth on error } } @@ -310,18 +324,27 @@ public void handleCommand(ChannelUID channelUID, Command command) { } else { Channel channel = getThing().getChannel(channelUID); + if (channel == null) { + return; + } + + ChannelType tp = null; + if (channelTypeProvider != null) { + tp = channelTypeProvider.getInternalChannelType(channel.getChannelTypeUID()); + } - ChannelType tp = channelTypeProvider.getInternalChannelType(channel.getChannelTypeUID()); + if (tp == null) { + return; + } String dptId = channel.getProperties().get("dptId"); - String groupId = channel.getProperties().get("groupdId"); - String label = channel.getLabel(); String uid = channel.getUID().getId(); String type = tp.getItemType(); - WriteDp(dptId, command, type); - ReadDp(dptId, uid, type, false); + if (dptId != null && type != null) { + WriteDp(dptId, command, type); + ReadDp(dptId, uid, type, false); + } } } - } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java index 814ea2b168962..fe92780176a47 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java @@ -44,7 +44,7 @@ public class SiemensHvacOZW672BridgeThingHandler extends SiemensHvacBridgeBaseTh @Activate public SiemensHvacOZW672BridgeThingHandler(Bridge bridge, @Nullable NetworkAddressService networkAddressService, - @Nullable HttpClientFactory httpClientFactory, @Nullable SiemensHvacMetadataRegistry metaDataRegistry) { + @Nullable HttpClientFactory httpClientFactory, SiemensHvacMetadataRegistry metaDataRegistry) { super(bridge, networkAddressService, httpClientFactory, metaDataRegistry); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacCallback.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacCallback.java index 3035303a2ec82..43435425b0192 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacCallback.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacCallback.java @@ -1,3 +1,15 @@ +/** + * Copyright (c) 2010-2021 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.siemenshvac.internal.network; import java.net.URI; @@ -5,11 +17,13 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +/** + * @author Laurent Arnal - Initial contribution + */ @NonNullByDefault public interface SiemensHvacCallback { /** * Runs callback code after response completion. */ void execute(URI uri, int status, @Nullable Object response); - } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java index 76f806a8ad2a6..bfd878d346d72 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java @@ -1,14 +1,31 @@ +/** + * Copyright (c) 2010-2021 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.siemenshvac.internal.network; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.api.Request; import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeBaseThingHandler; import com.google.gson.JsonObject; +/** + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault public interface SiemensHvacConnector { - public JsonObject DoRequest(@Nullable String req, @Nullable SiemensHvacCallback callback); + public @Nullable JsonObject DoRequest(String req, @Nullable SiemensHvacCallback callback); public void WaitAllPendingRequest(); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index db7accbdd7ea3..9d9873fe888a0 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -1,3 +1,15 @@ +/** + * Copyright (c) 2010-2021 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.siemenshvac.internal.network; import java.util.Date; @@ -36,8 +48,12 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; -@Component(immediate = true) +/** + * + * @author Laurent Arnal - Initial contribution + */ @NonNullByDefault +@Component(immediate = true) public class SiemensHvacConnectorImpl implements SiemensHvacConnector { private static final Logger logger = LoggerFactory.getLogger(SiemensHvacConnectorImpl.class); @@ -46,12 +62,13 @@ public class SiemensHvacConnectorImpl implements SiemensHvacConnector { private String baseUrl = ""; private String userName = ""; private String userPassword = ""; + @SuppressWarnings("unused") private @Nullable Date lastUpdate; protected final HttpClientFactory httpClientFactory; - protected @Nullable HttpClient httpClient; - protected @Nullable HttpClient httpClientInsecure; + protected HttpClient httpClient; + protected HttpClient httpClientInsecure; private static int startedRequest = 0; private static int completedRequest = 0; @@ -66,12 +83,6 @@ public SiemensHvacConnectorImpl(@Reference HttpClientFactory httpClientFactory) this.updateCommand = new Hashtable(); this.httpClientFactory = httpClientFactory; - initHttpClient(); - - } - - private void initHttpClient() { - this.httpClient = httpClientFactory.getCommonHttpClient(); this.httpClient.setMaxConnectionsPerDestination(15); this.httpClientInsecure = new HttpClient(new SslContextFactory.Client(true)); @@ -82,7 +93,6 @@ private void initHttpClient() { } catch (Exception e) { logger.warn("Failed to start insecure http client: {}", e.getMessage()); } - } @Override @@ -161,8 +171,16 @@ public void onError(@Nullable Request request) { return response; } - private void _initConfig() { - Configuration config = this.hvacBridgeBaseThingHandler.getThing().getConfiguration(); + private void _initConfig() throws Exception { + + Configuration config = null; + if (hvacBridgeBaseThingHandler != null) { + config = hvacBridgeBaseThingHandler.getThing().getConfiguration(); + } else { + throw new Exception( + "siemensHvac:Exception unable to get config because hvacBridgeBaseThingHandler is null"); + } + if (config.containsKey("baseUrl")) { baseUrl = (String) config.get("baseUrl"); } @@ -174,7 +192,7 @@ private void _initConfig() { } } - private void _doAuth() { + private void _doAuth() throws Exception { logger.debug("siemensHvac:doAuth()"); _initConfig(); @@ -187,76 +205,80 @@ private void _doAuth() { try { ContentResponse response = executeRequest(request, null); - int statusCode = response.getStatus(); + if (response != null) { + int statusCode = response.getStatus(); + + if (statusCode == HttpStatus.OK_200) { + String result = response.getContentAsString(); - if (statusCode == HttpStatus.OK_200) { - String result = response.getContentAsString(); + if (result != null) { + JsonObject resultObj = getGson().fromJson(result, JsonObject.class); - if (result != null) { - JsonObject resultObj = getGson().fromJson(result, JsonObject.class); + if (resultObj != null && resultObj.has("Result")) { + JsonElement resultVal = resultObj.get("Result"); + JsonObject resultObj2 = resultVal.getAsJsonObject(); - if (resultObj.has("Result")) { - JsonElement resultVal = resultObj.get("Result"); - JsonObject resultObj2 = resultVal.getAsJsonObject(); + if (resultObj2.has("Success")) { + boolean successVal = resultObj2.get("Success").getAsBoolean(); - if (resultObj2.has("Success")) { - boolean successVal = resultObj2.get("Success").getAsBoolean(); + if (successVal) { - if (successVal) { + if (resultObj.has("SessionId")) { + sessionId = resultObj.get("SessionId").getAsString(); + logger.debug("Have new SessionId : {} ", sessionId); + } - if (resultObj.has("SessionId")) { - sessionId = resultObj.get("SessionId").getAsString(); - logger.debug("Have new SessionId : " + sessionId); } } - } - } - logger.debug("siemensHvac:doAuth:decodeResponse:()"); + logger.debug("siemensHvac:doAuth:decodeResponse:()"); - } + } - if (sessionId == null) { - logger.debug("Session request auth was unsucessfull in _doAuth()"); + if (sessionId == null) { + logger.debug("Session request auth was unsucessfull in _doAuth()"); + } } } logger.debug("siemensHvac:doAuth:connect()"); } catch (Exception ex) { - logger.debug("siemensHvac:doAuth:error()" + ex.getLocalizedMessage()); + logger.debug("siemensHvac:doAuth:error() {}", ex.getLocalizedMessage()); } finally { } } - public @Nullable String DoBasicRequest(@Nullable String uri) { + public @Nullable String DoBasicRequest(String uri) throws Exception { return DoBasicRequest(uri, null); } - public @Nullable String DoBasicRequestAsync(@Nullable String uri, @Nullable SiemensHvacCallback callback) { + public @Nullable String DoBasicRequestAsync(String uri, @Nullable SiemensHvacCallback callback) throws Exception { return DoBasicRequest(uri, callback); } - public @Nullable String DoBasicRequest(@Nullable String uri, @Nullable SiemensHvacCallback callback) { + public @Nullable String DoBasicRequest(String uri, @Nullable SiemensHvacCallback callback) throws Exception { if (sessionId == null) { _doAuth(); } try { String baseUri = baseUrl; - if (!uri.endsWith("?")) { - uri = uri + "&"; + + String mUri = uri; + if (!mUri.endsWith("?")) { + mUri = mUri + "&"; } - uri = uri + "SessionId=" + sessionId; - uri = uri + "&user=" + userName + "&pwd=" + userPassword; + mUri = mUri + "SessionId=" + sessionId; + mUri = mUri + "&user=" + userName + "&pwd=" + userPassword; - final Request request = httpClientInsecure.newRequest(baseUri + uri); + final Request request = httpClientInsecure.newRequest(baseUri + mUri); request.method(HttpMethod.GET); ContentResponse response = executeRequest(request, callback); - if (callback == null) { + if (callback == null && response != null) { int statusCode = response.getStatus(); if (statusCode == HttpStatus.OK_200) { @@ -266,8 +288,8 @@ private void _doAuth() { } } } catch (Exception ex) { - logger.error( - "siemensHvac:DoRequest:Exception by executing Request: " + uri + " ; " + ex.getLocalizedMessage()); + logger.error("siemensHvac:DoRequest:Exception by executing Request: {} ; {} ", uri, + ex.getLocalizedMessage()); } finally { } @@ -275,7 +297,7 @@ private void _doAuth() { } @Override - public @Nullable JsonObject DoRequest(@Nullable String req, @Nullable SiemensHvacCallback callback) { + public @Nullable JsonObject DoRequest(String req, @Nullable SiemensHvacCallback callback) { try { String response = DoBasicRequest(req, callback); @@ -283,7 +305,7 @@ private void _doAuth() { JsonObject resultObj = getGson().fromJson(response, JsonObject.class); - if (resultObj.has("Result")) { + if (resultObj != null && resultObj.has("Result")) { JsonObject subResultObj = resultObj.getAsJsonObject("Result"); if (subResultObj.has("Success")) { @@ -298,8 +320,8 @@ private void _doAuth() { return null; } } catch (Exception e) { - logger.error("siemensHvac:DoRequest:Exception by executing jsonRequest: " + req + " ; " - + e.getLocalizedMessage()); + logger.error("siemensHvac:DoRequest:Exception by executing jsonRequest: {} ; {} ", req, + e.getLocalizedMessage()); } return null; @@ -317,9 +339,8 @@ public void WaitAllPendingRequest() { allRequestDone = true; while (idx < 5 && allRequestDone) { - logger.debug("WaitAllPendingRequest:waitAllRequestDone " + idx + " : " - + (startedRequest - completedRequest) + "(" + startedRequest + "/" + completedRequest - + ")"); + logger.debug("WaitAllPendingRequest:waitAllRequestDone {} : {} ({}/{})", idx, + (startedRequest - completedRequest), startedRequest, completedRequest); if (startedRequest != completedRequest) { allRequestDone = false; } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java index c669d0f6b2568..6205c699175fd 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java @@ -1,5 +1,19 @@ +/** + * Copyright (c) 2010-2021 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.siemenshvac.internal.network; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Response.CompleteListener; import org.eclipse.jetty.client.api.Response.ContentListener; @@ -13,6 +27,10 @@ import com.google.gson.Gson; import com.google.gson.JsonObject; +/** + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault public class SiemensHvacRequestListener extends BufferingResponseListener implements SuccessListener, FailureListener, ContentListener, CompleteListener { @@ -35,17 +53,24 @@ public SiemensHvacRequestListener(SiemensHvacCallback callback, SiemensHvacConne } @Override - public void onSuccess(Response response) { + public void onSuccess(@Nullable Response response) { // logger.debug("{} response: {}", response.getRequest().getURI(), response.getStatus()); } @Override - public void onFailure(Response response, Throwable failure) { - logger.debug("response failed: {} {}", response.getRequest().getURI(), failure.getLocalizedMessage(), failure); + public void onFailure(@Nullable Response response, @Nullable Throwable failure) { + if (response != null && failure != null) { + logger.debug("response failed: {} {}", response.getRequest().getURI(), failure.getLocalizedMessage(), + failure); + } } @Override - public void onComplete(Result result) { + public void onComplete(@Nullable Result result) { + if (result == null) { + return; + } + hvacConnector.onComplete(result.getRequest()); try { @@ -66,10 +91,10 @@ public void onComplete(Result result) { Gson gson = SiemensHvacConnectorImpl.getGson(); resultObj = gson.fromJson(content, JsonObject.class); } catch (Exception ex) { - logger.debug("error:" + ex.toString()); + logger.debug("error: {}", ex.toString()); } - if (resultObj.has("Result")) { + if (resultObj != null && resultObj.has("Result")) { JsonObject subResultObj = resultObj.getAsJsonObject("Result"); if (subResultObj.has("Success")) { @@ -85,11 +110,11 @@ public void onComplete(Result result) { callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), resultObj); return; - } else if (errorMsg.equals("datatype not supported")) { + } else if (("datatype not supported").equals(errorMsg)) { callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), resultObj); } else { - logger.debug("error : " + subResultObj); + logger.debug("error : {}", subResultObj); hvacConnector.onError(result.getRequest()); } } else { @@ -111,5 +136,4 @@ public void onComplete(Result result) { logger.debug("error"); } } - } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java index 2e34f8bb64c6e..42fc94a3e5672 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2020 Contributors to the openHAB project + * Copyright (c) 2010-2021 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -14,6 +14,8 @@ import java.util.Locale; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.thing.type.ChannelGroupType; import org.openhab.core.thing.type.ChannelGroupTypeProvider; import org.openhab.core.thing.type.ChannelGroupTypeUID; @@ -23,6 +25,7 @@ * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault public interface SiemensHvacChannelGroupTypeProvider extends ChannelGroupTypeProvider { /** @@ -43,5 +46,6 @@ public interface SiemensHvacChannelGroupTypeProvider extends ChannelGroupTypePro * null if no ChannelGroupType with the given UID was added * before */ + @Nullable public ChannelGroupType getInternalChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java index cb2b17abd0245..3a98c2b6bb152 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2020 Contributors to the openHAB project + * Copyright (c) 2010-2021 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -18,6 +18,7 @@ import java.util.Locale; import java.util.Map; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.thing.type.ChannelGroupType; import org.openhab.core.thing.type.ChannelGroupTypeProvider; @@ -30,6 +31,7 @@ * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault @Component(service = { SiemensHvacChannelGroupTypeProvider.class, ChannelGroupTypeProvider.class }) public class SiemensHvacChannelGroupTypeProviderImpl implements SiemensHvacChannelGroupTypeProvider { @@ -38,6 +40,7 @@ public class SiemensHvacChannelGroupTypeProviderImpl implements SiemensHvacChann // @Override + @Nullable public ChannelGroupType getInternalChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID) { return channelGroupTypesByUID.get(channelGroupTypeUID); } @@ -47,6 +50,7 @@ public void addChannelGroupType(ChannelGroupType channelGroupType) { channelGroupTypesByUID.put(channelGroupType.getUID(), channelGroupType); } + @Nullable @Override public ChannelGroupType getChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID, @Nullable Locale locale) { return channelGroupTypesByUID.get(channelGroupTypeUID); @@ -61,9 +65,11 @@ public ChannelGroupType getChannelGroupType(ChannelGroupTypeUID channelGroupType public Collection getChannelGroupTypes(@Nullable Locale locale) { Collection result = new ArrayList<>(); for (ChannelGroupTypeUID uid : channelGroupTypesByUID.keySet()) { - result.add(channelGroupTypesByUID.get(uid)); + ChannelGroupType groupType = channelGroupTypesByUID.get(uid); + if (groupType != null) { + result.add(groupType); + } } return result; - } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java index b29940868b560..4aa91a16c35e3 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2020 Contributors to the openHAB project + * Copyright (c) 2010-2021 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -14,6 +14,8 @@ import java.util.Locale; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.thing.type.ChannelType; import org.openhab.core.thing.type.ChannelTypeProvider; import org.openhab.core.thing.type.ChannelTypeUID; @@ -23,6 +25,7 @@ * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault public interface SiemensHvacChannelTypeProvider extends ChannelTypeProvider { /** @@ -43,5 +46,6 @@ public interface SiemensHvacChannelTypeProvider extends ChannelTypeProvider { * null if no ChannelType with the given UID was added * before */ - public ChannelType getInternalChannelType(ChannelTypeUID channelTypeUID); + @Nullable + public ChannelType getInternalChannelType(@Nullable ChannelTypeUID channelTypeUID); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java index b9e760ec6cab1..4a9fd02ec40fd 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java @@ -18,6 +18,7 @@ import java.util.Locale; import java.util.Map; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.thing.type.ChannelType; import org.openhab.core.thing.type.ChannelTypeProvider; @@ -30,6 +31,7 @@ * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault @Component(service = { SiemensHvacChannelTypeProvider.class, ChannelTypeProvider.class }) public class SiemensHvacChannelTypeProviderImpl implements SiemensHvacChannelTypeProvider { private final Map channelTypesByUID = new HashMap<>(); @@ -47,10 +49,12 @@ public Collection getChannelTypes(@Nullable Locale locale) { Collection result = new ArrayList<>(); for (ChannelTypeUID uid : channelTypesByUID.keySet()) { - result.add(channelTypesByUID.get(uid)); + ChannelType tp = channelTypesByUID.get(uid); + if (tp != null) { + result.add(tp); + } } return result; - } /** @@ -62,9 +66,9 @@ public ChannelType getChannelType(ChannelTypeUID channelTypeUID, @Nullable Local return channelTypesByUID.get(channelTypeUID); } + @Nullable @Override public ChannelType getInternalChannelType(@Nullable ChannelTypeUID channelTypeUID) { return channelTypesByUID.get(channelTypeUID); } - } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java index d9bfdfc3866e4..85034d8cfeafb 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java @@ -15,6 +15,7 @@ import java.net.URI; import java.util.Locale; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.config.core.ConfigDescription; import org.openhab.core.config.core.ConfigDescriptionProvider; @@ -24,6 +25,7 @@ * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault public interface SiemensHvacConfigDescriptionProvider extends ConfigDescriptionProvider { /** @@ -56,5 +58,6 @@ public interface SiemensHvacConfigDescriptionProvider extends ConfigDescriptionP * null if no ConfigDescription with the given URI was added * before */ + @Nullable public ConfigDescription getInternalConfigDescription(URI uri); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProviderImpl.java index aeae4393da116..b06cc93bce837 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProviderImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProviderImpl.java @@ -19,6 +19,7 @@ import java.util.Locale; import java.util.Map; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.config.core.ConfigDescription; import org.openhab.core.config.core.ConfigDescriptionProvider; @@ -28,24 +29,29 @@ * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault @Component(service = { SiemensHvacConfigDescriptionProvider.class, ConfigDescriptionProvider.class }) public class SiemensHvacConfigDescriptionProviderImpl implements SiemensHvacConfigDescriptionProvider { private Map configDescriptionsByURI = new HashMap<>(); @Override - public Collection getConfigDescriptions(Locale locale) { + public Collection getConfigDescriptions(@Nullable Locale locale) { Collection result = new ArrayList<>(); for (URI configDescriptionURI : configDescriptionsByURI.keySet()) { - result.add(configDescriptionsByURI.get(configDescriptionURI)); + ConfigDescription desc = configDescriptionsByURI.get(configDescriptionURI); + if (desc != null) { + result.add(desc); + } } return result; } @Override - public ConfigDescription getConfigDescription(URI uri, @Nullable Locale locale) { + public @Nullable ConfigDescription getConfigDescription(URI uri, @Nullable Locale locale) { return configDescriptionsByURI.get(uri); } + @Nullable @Override public ConfigDescription getInternalConfigDescription(URI uri) { return configDescriptionsByURI.get(uri); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java index 3492f340c52bd..44714cf847c2d 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java @@ -14,6 +14,8 @@ import java.util.Locale; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.binding.ThingTypeProvider; import org.openhab.core.thing.type.ThingType; @@ -23,6 +25,7 @@ * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault public interface SiemensHvacThingTypeProvider extends ThingTypeProvider { /** @@ -32,9 +35,9 @@ public interface SiemensHvacThingTypeProvider extends ThingTypeProvider { /** * Use this method to lookup a ThingType which was generated by the - * homematic binding. Other than {@link #getThingType(ThingTypeUID, Locale)} + * binding. Other than {@link #getThingType(ThingTypeUID, Locale)} * of this provider, it will return also those {@link ThingType}s which are - * excluded by {@link HomematicThingTypeExcluder} + * excluded by {@link ThingTypeExcluder} * * @param thingTypeUID * e.g. homematic:HM-Sec-SC @@ -43,5 +46,6 @@ public interface SiemensHvacThingTypeProvider extends ThingTypeProvider { * null if no ThingType with the given thingTypeUID was added * before */ + @Nullable public ThingType getInternalThingType(ThingTypeUID thingTypeUID); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProviderImpl.java index 2904f60f49293..3bd99c5e93b9b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProviderImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProviderImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2020 Contributors to the openHAB project + * Copyright (c) 2010-2021 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -17,6 +17,7 @@ import java.util.Locale; import java.util.Map; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.binding.ThingTypeProvider; @@ -28,37 +29,34 @@ * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault @Component(service = { SiemensHvacThingTypeProvider.class, ThingTypeProvider.class }, immediate = true) public class SiemensHvacThingTypeProviderImpl implements SiemensHvacThingTypeProvider { private Map thingTypesByUID = new HashMap<>(); public SiemensHvacThingTypeProviderImpl() { - } @Override public void addThingType(ThingType thingType) { thingTypesByUID.put(thingType.getUID(), thingType); - } + @Nullable @Override public ThingType getInternalThingType(ThingTypeUID thingTypeUID) { return thingTypesByUID.get(thingTypeUID); - } @Override public Collection getThingTypes(@Nullable Locale locale) { Map copy = new HashMap<>(thingTypesByUID); return copy.values(); - } @Override - public ThingType getThingType(ThingTypeUID thingTypeUID, Locale locale) { + public @Nullable ThingType getThingType(ThingTypeUID thingTypeUID, @Nullable Locale locale) { return thingTypesByUID.get(thingTypeUID); } - } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index 235f82e3b9d35..9b0336814b5a8 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.siemenshvac.internal.type; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataDataPoint; import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataDevice; import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataMenu; @@ -28,6 +29,7 @@ * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault public class UidUtils { public static String sanetizeId(String st) { @@ -160,7 +162,6 @@ else if (c == '°') { } return buffer.toString(); - } /** diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml index a69e0c9d33b27..ddb89cb49140f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml @@ -1,5 +1,6 @@ - @@ -16,13 +17,13 @@ UserName of the Siemens Hvac gateway - false + false Administrator UserPassword of the Siemens Hvac gateway - false + false password From 780d5b0de959b42ed4e54b509508c80ae241db20 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 8 Jan 2023 12:00:50 +0100 Subject: [PATCH 006/214] some discovery fixes Signed-off-by: Laurent ARNAL --- .../Metadata/RuntimeTypeAdapterFactory.java | 14 ++++++++++++++ .../SiemenesHvacDiscoveryParticipant.java | 14 +++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java index 8e93cd34a429e..13c485483b0d7 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java @@ -25,6 +25,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/* + * Copied from + * https://raw.githubusercontent.com/google/gson/master/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java + * and repackaged here with additional content from + * com.google.gson.internal.{Streams,TypeAdapters,LazilyParsedNumber} + * to avoid using the internal package. + */ package org.openhab.binding.siemenshvac.internal.Metadata; import java.io.EOFException; @@ -184,6 +192,9 @@ public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory { private final Map, String> subtypeToLabel = new LinkedHashMap, String>(); private RuntimeTypeAdapterFactory(Class baseType, String typeFieldName) { + if (typeFieldName == null || baseType == null) { + throw new NullPointerException(); + } this.baseType = baseType; this.typeFieldName = typeFieldName; } @@ -212,6 +223,9 @@ public static RuntimeTypeAdapterFactory of(Class baseType) { * have already been registered on this type adapter. */ public RuntimeTypeAdapterFactory registerSubtype(Class type, String label) { + if (type == null || label == null) { + throw new NullPointerException(); + } if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) { throw new IllegalArgumentException("types and labels must be unique"); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java index dc746316580eb..3eadd4d5aeff6 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java @@ -31,7 +31,9 @@ import org.openhab.core.config.discovery.upnp.internal.UpnpDiscoveryService; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; +import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Modified; /** * The {@link SiemenesHvacDiscoveryParticipant} is responsible for discovering new and @@ -40,9 +42,19 @@ * @author Laurent Arnal - Initial contribution */ @NonNullByDefault -@Component(service = UpnpDiscoveryParticipant.class) +@Component(service = UpnpDiscoveryParticipant.class, configurationPid = "discovery.siemensHvac") public class SiemenesHvacDiscoveryParticipant implements UpnpDiscoveryParticipant { + @Activate + public void activate(@Nullable Map configProperties) { + + } + + @Modified + public void modified(@Nullable Map configProperties) { + + } + @Override public Set getSupportedThingTypeUIDs() { return Collections.singleton(SiemensHvacBindingConstants.THING_TYPE_BRIDGE); From 874d983cf0648bcbc52b65344bc8e45b0eade841 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sat, 14 Jan 2023 10:00:39 +0100 Subject: [PATCH 007/214] fix spotless violations Signed-off-by: Laurent ARNAL --- .../internal/discovery/SiemenesHvacDiscoveryParticipant.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java index 3eadd4d5aeff6..a0ae1a62fe3cf 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java @@ -47,12 +47,10 @@ public class SiemenesHvacDiscoveryParticipant implements UpnpDiscoveryParticipan @Activate public void activate(@Nullable Map configProperties) { - } @Modified public void modified(@Nullable Map configProperties) { - } @Override From bdf2d7b679ff3d0d559e91246b94e1006d593137 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sat, 14 Jan 2023 10:33:00 +0100 Subject: [PATCH 008/214] review warnings Signed-off-by: Laurent ARNAL --- .../Metadata/RuntimeTypeAdapterFactory.java | 8 +---- .../Metadata/SiemensHvacMetadata.java | 2 +- .../SiemensHvacMetadataDataPoint.java | 2 +- .../Metadata/SiemensHvacMetadataDevice.java | 2 +- .../Metadata/SiemensHvacMetadataMenu.java | 2 +- .../SiemensHvacMetadataPointChild.java | 2 +- .../Metadata/SiemensHvacMetadataRegistry.java | 2 +- .../SiemensHvacMetadataRegistryImpl.java | 22 +++++++----- .../SiemensHvacBindingConstants.java | 2 +- .../SiemenesHvacDiscoveryParticipant.java | 2 +- .../SiemensHvacDeviceDiscoveryService.java | 2 +- .../factory/SiemensHvacHandlerFactory.java | 2 +- .../SiemensHvacBridgeBaseThingHandler.java | 2 +- .../internal/handler/SiemensHvacHandler.java | 2 +- .../handler/SiemensHvacHandlerImpl.java | 2 +- .../SiemensHvacOZW672BridgeThingHandler.java | 2 +- .../internal/network/SiemensHvacCallback.java | 2 +- .../network/SiemensHvacConnector.java | 2 +- .../network/SiemensHvacConnectorImpl.java | 7 ++-- .../network/SiemensHvacRequestListener.java | 2 +- .../SiemensHvacChannelGroupTypeProvider.java | 2 +- ...emensHvacChannelGroupTypeProviderImpl.java | 2 +- .../type/SiemensHvacChannelTypeProvider.java | 2 +- .../SiemensHvacChannelTypeProviderImpl.java | 2 +- .../SiemensHvacConfigDescriptionProvider.java | 2 +- ...mensHvacConfigDescriptionProviderImpl.java | 2 +- .../internal/type/SiemensHvacException.java | 35 +++++++++++++++++++ .../type/SiemensHvacThingTypeProvider.java | 2 +- .../SiemensHvacThingTypeProviderImpl.java | 2 +- .../siemenshvac/internal/type/UidUtils.java | 2 +- 30 files changed, 80 insertions(+), 44 deletions(-) create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacException.java diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java index 13c485483b0d7..18ec8edce8f3d 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2021 Contributors to the openHAB project + * Copyright (c) 2010-2023 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -192,9 +192,6 @@ public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory { private final Map, String> subtypeToLabel = new LinkedHashMap, String>(); private RuntimeTypeAdapterFactory(Class baseType, String typeFieldName) { - if (typeFieldName == null || baseType == null) { - throw new NullPointerException(); - } this.baseType = baseType; this.typeFieldName = typeFieldName; } @@ -223,9 +220,6 @@ public static RuntimeTypeAdapterFactory of(Class baseType) { * have already been registered on this type adapter. */ public RuntimeTypeAdapterFactory registerSubtype(Class type, String label) { - if (type == null || label == null) { - throw new NullPointerException(); - } if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) { throw new IllegalArgumentException("types and labels must be unique"); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java index 8e6d819046808..a2170d9685790 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2021 Contributors to the openHAB project + * Copyright (c) 2010-2023 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java index 6ee9a43cf652e..57040186d4b48 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2021 Contributors to the openHAB project + * Copyright (c) 2010-2023 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java index 0ba1861a50c49..a5799d1afbb29 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2021 Contributors to the openHAB project + * Copyright (c) 2010-2023 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java index 4de7ae4c82950..749d3df69d591 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2021 Contributors to the openHAB project + * Copyright (c) 2010-2023 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java index 38f934642af6d..139321e54e7c1 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2021 Contributors to the openHAB project + * Copyright (c) 2010-2023 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java index 80fa23690fbfa..5ae702e1e0657 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2021 Contributors to the openHAB project + * Copyright (c) 2010-2023 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index c7cbb7cbd1570..6810671d0d09d 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2021 Contributors to the openHAB project + * Copyright (c) 2010-2023 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -36,6 +36,7 @@ import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelGroupTypeProvider; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelTypeProvider; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacConfigDescriptionProvider; +import org.openhab.binding.siemenshvac.internal.type.SiemensHvacException; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacThingTypeProvider; import org.openhab.binding.siemenshvac.internal.type.UidUtils; import org.openhab.core.OpenHAB; @@ -167,7 +168,11 @@ protected void unsetConfigDescriptionProvider(SiemensHvacConfigDescriptionProvid public void initialize() { } - public void InitDptMap(SiemensHvacMetadata node) { + public void InitDptMap(@Nullable SiemensHvacMetadata node) { + + if (node == null) { + return; + } if (node.getClass() == SiemensHvacMetadataMenu.class) { SiemensHvacMetadataMenu mInformation = (SiemensHvacMetadataMenu) node; @@ -285,7 +290,7 @@ public void ReadMeta() { } } - private void generateThingsType(SiemensHvacMetadataDevice device) { + private void generateThingsType(SiemensHvacMetadataDevice device) throws Exception { logger.debug("Generate thing types for device : {} / {}", device.getName(), device.getSerialNr()); if (thingTypeProvider != null) { ThingTypeUID thingTypeUID = UidUtils.generateThingTypeUID(device); @@ -349,8 +354,8 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { if (channelType != null) { ChannelDefinition channelDef = new ChannelDefinitionBuilder(id, channelType.getUID()).withLabel(dataPoint.getShortDesc()) - .withDescription(dataPoint.getLongDesc()) - .withProperties(props).build(); + .withDescription(dataPoint.getLongDesc()).withProperties(props) + .build(); channelDefinitions.add(channelDef); } @@ -451,7 +456,8 @@ private ChannelType createChannelType(SiemensHvacMetadataDataPoint dpt, ChannelT /** * Creates the ThingType for the given device. */ - private ThingType createThingType(SiemensHvacMetadataDevice device, List groupTypes) { + private ThingType createThingType(SiemensHvacMetadataDevice device, List groupTypes) + throws Exception { String name = device.getName(); String description = device.getName(); @@ -481,13 +487,13 @@ private ThingType createThingType(SiemensHvacMetadataDevice device, List Date: Sat, 14 Jan 2023 11:41:41 +0100 Subject: [PATCH 009/214] fix pmd errors and exceptions Signed-off-by: Laurent ARNAL --- .../org.openhab.binding.siemenshvac/pom.xml | 2 +- .../SiemensHvacMetadataRegistryImpl.java | 22 ++--- .../siemenshvac/internal/type/UidUtils.java | 83 ++++++------------- 3 files changed, 34 insertions(+), 73 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/pom.xml b/bundles/org.openhab.binding.siemenshvac/pom.xml index 10316b6b9e076..a4e7eb9a2165e 100644 --- a/bundles/org.openhab.binding.siemenshvac/pom.xml +++ b/bundles/org.openhab.binding.siemenshvac/pom.xml @@ -7,7 +7,7 @@ org.openhab.addons.bundles org.openhab.addons.reactor.bundles - 3.3.0-SNAPSHOT + 4.0.0-SNAPSHOT org.openhab.binding.siemenshvac diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index 6810671d0d09d..8aae75bd5c723 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -18,7 +18,6 @@ import java.io.IOException; import java.math.BigDecimal; import java.net.URI; -import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; @@ -36,7 +35,6 @@ import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelGroupTypeProvider; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelTypeProvider; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacConfigDescriptionProvider; -import org.openhab.binding.siemenshvac.internal.type.SiemensHvacException; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacThingTypeProvider; import org.openhab.binding.siemenshvac.internal.type.UidUtils; import org.openhab.core.OpenHAB; @@ -290,7 +288,7 @@ public void ReadMeta() { } } - private void generateThingsType(SiemensHvacMetadataDevice device) throws Exception { + private void generateThingsType(SiemensHvacMetadataDevice device) { logger.debug("Generate thing types for device : {} / {}", device.getName(), device.getSerialNr()); if (thingTypeProvider != null) { ThingTypeUID thingTypeUID = UidUtils.generateThingTypeUID(device); @@ -354,8 +352,8 @@ private void generateThingsType(SiemensHvacMetadataDevice device) throws Excepti if (channelType != null) { ChannelDefinition channelDef = new ChannelDefinitionBuilder(id, channelType.getUID()).withLabel(dataPoint.getShortDesc()) - .withDescription(dataPoint.getLongDesc()).withProperties(props) - .build(); + .withDescription(dataPoint.getLongDesc()) + .withProperties(props).build(); channelDefinitions.add(channelDef); } @@ -456,8 +454,7 @@ private ChannelType createChannelType(SiemensHvacMetadataDataPoint dpt, ChannelT /** * Creates the ThingType for the given device. */ - private ThingType createThingType(SiemensHvacMetadataDevice device, List groupTypes) - throws Exception { + private ThingType createThingType(SiemensHvacMetadataDevice device, List groupTypes) { String name = device.getName(); String description = device.getName(); @@ -487,14 +484,9 @@ private ThingType createThingType(SiemensHvacMetadataDevice device, List groupTypes, diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index 53f20f0bf9000..17ef432ce4cb6 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -38,99 +38,68 @@ public static String sanetizeId(String st) { for (int i = 0; i < st.length(); i++) { char c = st.charAt(i); + if (c == 130) { - if (c == 'é') { + } else if (c == 131) { c = 'e'; - } else if (c == 'è') { + } else if (c == 136) { c = 'e'; - } else if (c == 'ê') { + } else if (c == 137) { c = 'e'; - } else if (c == 'ë') { - c = 'e'; - } else if (c == 'ě') { - c = 'e'; - } else if (c == 'É') { + } else if (c == 144) { c = 'E'; - } else if (c == 'É') { + } else if (c == 212) { c = 'E'; } - else if (c == 'î') { + else if (c == 140) { c = 'i'; - } else if (c == 'ï') { + } else if (c == 139) { c = 'i'; - } else if (c == 'í') { + } else if (c == 161) { c = 'i'; - } else if (c == 'í') { + } else if (c == 141) { c = 'i'; } - else if (c == 'ô') { + else if (c == 147) { c = 'o'; - } else if (c == 'ó') { + } else if (c == 162) { c = 'o'; - } else if (c == 'ò') { + } else if (c == 149) { c = 'o'; - } else if (c == 'ö') { + } else if (c == 148) { c = 'o'; } - else if (c == 'ú') { + else if (c == 163) { c = 'u'; - } else if (c == 'ù') { + } else if (c == 151) { c = 'u'; - } else if (c == 'û') { + } else if (c == 150) { c = 'u'; - } else if (c == 'ü') { + } else if (c == 129) { c = 'u'; - } else if (c == 'ů') { - c = 'u'; - } else if (c == 'Ú') { + } else if (c == 233) { c = 'U'; } - else if (c == 'à') { + else if (c == 133) { c = 'a'; - } else if (c == 'ä') { + } else if (c == 132) { c = 'a'; - } else if (c == 'â') { + } else if (c == 131) { c = 'a'; - } else if (c == 'á') { + } else if (c == 160) { c = 'a'; } - else if (c == 'ř') { - c = 'r'; - } else if (c == 'ť') { - c = 't'; - } - - else if (c == 'š') { - c = 's'; - } - - else if (c == 'ý') { - c = 'y'; - } else if (c == 'ÿ') { - c = 'y'; - } - - else if (c == 'ž') { - c = 'z'; - } - - else if (c == 'ç') { + else if (c == 135) { c = 'c'; - } else if (c == 'č') { - c = 'c'; - } else if (c == 'Č') { + } else if (c == 128) { c = 'C'; } - else if (c == 'Ž') { - c = 'Z'; - } - else if (c == '_') { c = '_'; } else if (c == ' ') { @@ -147,7 +116,7 @@ else if (c == '_') { c = '_'; } - else if (c == '°') { + else if (c == 248) { c = '_'; } else if (c == '\'') { c = '_'; From 70dc554f2d324dab90f4a91aa97e7bad9ee8bf4d Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sat, 14 Jan 2023 15:00:13 +0100 Subject: [PATCH 010/214] review warnings Signed-off-by: Laurent ARNAL --- .../SiemensHvacMetadataRegistryImpl.java | 91 ++++++++++--------- .../SiemenesHvacDiscoveryParticipant.java | 3 +- .../SiemensHvacDeviceDiscoveryService.java | 69 +++++++------- .../SiemensHvacBridgeBaseThingHandler.java | 20 ++-- .../handler/SiemensHvacHandlerImpl.java | 35 ++++--- .../network/SiemensHvacConnector.java | 3 +- .../network/SiemensHvacConnectorImpl.java | 8 +- .../SiemensHvacChannelGroupTypeProvider.java | 4 +- ...emensHvacChannelGroupTypeProviderImpl.java | 3 +- .../type/SiemensHvacChannelTypeProvider.java | 5 +- .../SiemensHvacChannelTypeProviderImpl.java | 3 +- .../type/SiemensHvacThingTypeProvider.java | 4 +- 12 files changed, 128 insertions(+), 120 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index 8aae75bd5c723..5fbab8a3bfa8b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -52,6 +52,7 @@ import org.openhab.core.thing.type.ChannelType; import org.openhab.core.thing.type.ChannelTypeBuilder; import org.openhab.core.thing.type.ChannelTypeUID; +import org.openhab.core.thing.type.StateChannelTypeBuilder; import org.openhab.core.thing.type.ThingType; import org.openhab.core.thing.type.ThingTypeBuilder; import org.openhab.core.types.StateDescriptionFragmentBuilder; @@ -241,6 +242,8 @@ public int UnresolveCount() { @Override public void ReadMeta() { + ArrayList lcDevices = devices; + SiemensHvacConnector lcHvacConnector = hvacConnector; if (root == null) { logger.debug("siemensHvac:InitDptMap():begin"); @@ -251,8 +254,8 @@ public void ReadMeta() { if (root == null) { root = new SiemensHvacMetadataMenu(); ReadMetaData(root, -1); - if (hvacConnector != null) { - hvacConnector.WaitAllPendingRequest(); + if (lcHvacConnector != null) { + lcHvacConnector.WaitAllPendingRequest(); } } @@ -264,8 +267,8 @@ public void ReadMeta() { while (unresolveCount > 0) { ResolveDetails(); - if (hvacConnector != null) { - hvacConnector.WaitAllPendingRequest(); + if (lcHvacConnector != null) { + lcHvacConnector.WaitAllPendingRequest(); } unresolveCount = UnresolveCount(); @@ -274,8 +277,8 @@ public void ReadMeta() { SaveMetaDataToCache(); getRoot(); - if (devices != null) { - for (SiemensHvacMetadataDevice device : devices) { + if (lcDevices != null) { + for (SiemensHvacMetadataDevice device : lcDevices) { if (device.getType().indexOf("OZW672") >= 0) { continue; } @@ -289,14 +292,15 @@ public void ReadMeta() { } private void generateThingsType(SiemensHvacMetadataDevice device) { + SiemensHvacThingTypeProvider lcThingTypeProvider = thingTypeProvider; + SiemensHvacChannelTypeProvider lcChannelTypeProvider = channelTypeProvider; + SiemensHvacChannelGroupTypeProvider lcChannelGroupTypeProvider = channelGroupTypeProvider; logger.debug("Generate thing types for device : {} / {}", device.getName(), device.getSerialNr()); - if (thingTypeProvider != null) { + if (lcThingTypeProvider != null) { ThingTypeUID thingTypeUID = UidUtils.generateThingTypeUID(device); ThingType tt = null; - if (thingTypeProvider != null) { - tt = thingTypeProvider.getInternalThingType(thingTypeUID); - } + tt = lcThingTypeProvider.getInternalThingType(thingTypeUID); if (tt == null) { @@ -327,14 +331,12 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { ChannelTypeUID channelTypeUID = UidUtils.generateChannelTypeUID(dataPoint); ChannelType channelType = null; - if (channelTypeProvider != null) { + if (lcChannelTypeProvider != null) { - channelType = channelTypeProvider.getInternalChannelType(channelTypeUID); - if (channelType == null && channelTypeProvider != null) { + channelType = lcChannelTypeProvider.getInternalChannelType(channelTypeUID); + if (channelType == null) { channelType = createChannelType(dataPoint, channelTypeUID); - if (channelTypeProvider != null) { - channelTypeProvider.addChannelType(channelType); - } + lcChannelTypeProvider.addChannelType(channelType); } } @@ -364,17 +366,15 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { ChannelGroupTypeUID groupTypeUID = UidUtils.generateChannelGroupTypeUID(subMenu); ChannelGroupType groupType = null; - if (channelGroupTypeProvider != null) { - groupType = channelGroupTypeProvider.getInternalChannelGroupType(groupTypeUID); + if (lcChannelGroupTypeProvider != null) { + groupType = lcChannelGroupTypeProvider.getInternalChannelGroupType(groupTypeUID); if (groupType == null) { String groupLabel = subMenu.getShortDesc(); groupType = ChannelGroupTypeBuilder.instance(groupTypeUID, groupLabel) .withChannelDefinitions(channelDefinitions).withCategory("") .withDescription(menu.getLongDesc()).build(); - if (channelGroupTypeProvider != null) { - channelGroupTypeProvider.addChannelGroupType(groupType); - } + lcChannelGroupTypeProvider.addChannelGroupType(groupType); groupTypes.add(groupType); } } @@ -386,9 +386,7 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { } tt = createThingType(device, groupTypes); - if (thingTypeProvider != null) { - thingTypeProvider.addThingType(tt); - } + lcThingTypeProvider.addThingType(tt); } } } @@ -442,7 +440,7 @@ private ChannelType createChannelType(SiemensHvacMetadataDataPoint dpt, ChannelT isAdvanced = true; } - ChannelTypeBuilder channelTypeBuilder = ChannelTypeBuilder.state(channelTypeUID, label, itemType) + final StateChannelTypeBuilder channelTypeBuilder = ChannelTypeBuilder.state(channelTypeUID, label, itemType) .withStateDescriptionFragment(stateFragment.build()); channelType = channelTypeBuilder.isAdvanced(isAdvanced).withDescription(description).withCategory(category) @@ -455,6 +453,7 @@ private ChannelType createChannelType(SiemensHvacMetadataDataPoint dpt, ChannelT * Creates the ThingType for the given device. */ private ThingType createThingType(SiemensHvacMetadataDevice device, List groupTypes) { + SiemensHvacConfigDescriptionProvider lcConfigDescriptionProvider = configDescriptionProvider; String name = device.getName(); String description = device.getName(); @@ -467,8 +466,8 @@ private ThingType createThingType(SiemensHvacMetadataDevice device, List groupTypes, URI configDescriptionURI) { + SiemensHvacConfigDescriptionProvider lcConfigDescriptionProvider = configDescriptionProvider; List parms = new ArrayList<>(); List groups = new ArrayList<>(); - if (configDescriptionProvider != null) { - configDescriptionProvider.addConfigDescription(ConfigDescriptionBuilder.create(configDescriptionURI) + if (lcConfigDescriptionProvider != null) { + lcConfigDescriptionProvider.addConfigDescription(ConfigDescriptionBuilder.create(configDescriptionURI) .withParameters(parms).withParameterGroups(groups).build()); } } @@ -572,12 +572,16 @@ public static String getStatePattern(SiemensHvacMetadataDataPoint dpt) { public void ReadDeviceList() { try { - devices = new ArrayList(); + SiemensHvacConnector lcHvacConnector = hvacConnector; + ArrayList lcDevices = devices; + + lcDevices = new ArrayList(); + devices = lcDevices; String request = "api/devicelist/list.json?"; JsonObject response = null; - if (hvacConnector != null) { - response = hvacConnector.DoRequest(request, null); + if (lcHvacConnector != null) { + response = lcHvacConnector.DoRequest(request, null); } JsonArray devicesList = null; if (response != null) { @@ -637,8 +641,8 @@ public void ReadDeviceList() { deviceObj.setTreeGenerated(TreeGenerated); String request2 = "api/menutree/device_root.json?TreeName=Web&SerialNumber=" + SerialNr; - if (hvacConnector != null) { - JsonObject response2 = hvacConnector.DoRequest(request2, null); + if (lcHvacConnector != null) { + JsonObject response2 = lcHvacConnector.DoRequest(request2, null); if (response2 != null && response2.has("TreeItem")) { JsonObject tree = response2.getAsJsonObject("TreeItem"); @@ -649,9 +653,7 @@ public void ReadDeviceList() { } } - if (devices != null) { - devices.add(deviceObj); - } + lcDevices.add(deviceObj); } } catch (Exception e) { @@ -662,13 +664,14 @@ public void ReadDeviceList() { public void ReadMetaData(@Nullable SiemensHvacMetadata parent, int id) { try { + SiemensHvacConnector lcHvacConnector = hvacConnector; String request = "api/menutree/list.json?"; if (id != -1) { request = request + "&Id=" + id; } - if (hvacConnector != null) { - hvacConnector.DoRequest(request, new SiemensHvacCallback() { + if (lcHvacConnector != null) { + lcHvacConnector.DoRequest(request, new SiemensHvacCallback() { @Override public void execute(URI uri, int status, @Nullable Object response) { @@ -689,6 +692,7 @@ public void execute(URI uri, int status, @Nullable Object response) { private static int nbDpt = 0; public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMetadata parent, int id) { + SiemensHvacConnector lcHvacConnector = hvacConnector; if (resultObj.has("MenuItems")) { if (parent != null) { logger.debug("Decode menuItem for : {}", parent.getShortDesc()); @@ -874,8 +878,8 @@ public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMeta request2 = request2 + "&id=" + id; } - if (hvacConnector != null) { - hvacConnector.DoRequest(request2, new SiemensHvacCallback() { + if (lcHvacConnector != null) { + lcHvacConnector.DoRequest(request2, new SiemensHvacCallback() { @Override public void execute(URI uri, int status, @Nullable Object response) { @@ -980,13 +984,14 @@ public void SaveMetaDataToCache() { } public void resolveDptDetails(SiemensHvacMetadataDataPoint dpt) { + SiemensHvacConnector lcHvacConnector = hvacConnector; if (dpt.getDetailsResolved()) { return; } String request = "api/menutree/datapoint_desc.json?Id=" + dpt.getId(); - if (hvacConnector != null) { - hvacConnector.DoRequest(request, new SiemensHvacCallback() { + if (lcHvacConnector != null) { + lcHvacConnector.DoRequest(request, new SiemensHvacCallback() { @Override public void execute(URI uri, int status, @Nullable Object response) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java index 96ccb71709ad5..d8adf67bf3b19 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java @@ -28,7 +28,6 @@ import org.openhab.core.config.discovery.DiscoveryResult; import org.openhab.core.config.discovery.DiscoveryResultBuilder; import org.openhab.core.config.discovery.upnp.UpnpDiscoveryParticipant; -import org.openhab.core.config.discovery.upnp.internal.UpnpDiscoveryService; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; import org.osgi.service.component.annotations.Activate; @@ -37,7 +36,7 @@ /** * The {@link SiemenesHvacDiscoveryParticipant} is responsible for discovering new and - * removed siemensHvac bridges. It uses the central {@link UpnpDiscoveryService}. + * removed siemensHvac bridges. It uses the central {@link UpnpDiscoveryServic}. * * @author Laurent Arnal - Initial contribution */ diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java index 815f03e88c82c..5ded3c1fdee94 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java @@ -79,8 +79,9 @@ protected void stopBackgroundDiscovery() { } private @Nullable ThingUID getThingUID(ThingTypeUID thingTypeUID, String serial) { - if (siemensHvacBridgeHandler != null) { - ThingUID localBridgeUID = siemensHvacBridgeHandler.getThing().getUID(); + final SiemensHvacBridgeBaseThingHandler lcSiemensHvacBridgeHandler = siemensHvacBridgeHandler; + if (lcSiemensHvacBridgeHandler != null) { + ThingUID localBridgeUID = lcSiemensHvacBridgeHandler.getThing().getUID(); return new ThingUID(thingTypeUID, localBridgeUID, serial); } return null; @@ -88,56 +89,56 @@ protected void stopBackgroundDiscovery() { @Override public void startScan() { + final SiemensHvacMetadataRegistry lcMetadataRegistry = metadataRegistry; + final SiemensHvacBridgeBaseThingHandler lcSiemensHvacBridgeHandler = siemensHvacBridgeHandler; logger.debug("call startScan()"); - if (metadataRegistry != null) { - metadataRegistry.ReadMeta(); + if (lcMetadataRegistry != null) { + lcMetadataRegistry.ReadMeta(); // SiemensHvacMetadataMenu rootMenu = metadataRegistry.getRoot(); // for (SiemensHvacMetadata child : rootMenu.getChilds().values()) { - if (metadataRegistry != null) { - ArrayList devices = metadataRegistry.getDevices(); + ArrayList devices = lcMetadataRegistry.getDevices(); - if (devices == null) { - return; - } + if (devices == null) { + return; + } - for (SiemensHvacMetadataDevice device : devices) { + for (SiemensHvacMetadataDevice device : devices) { - String name = device.getName(); - String type = device.getType(); - String addr = device.getAddr(); - String serialNr = device.getSerialNr(); + String name = device.getName(); + String type = device.getType(); + String addr = device.getAddr(); + String serialNr = device.getSerialNr(); - if (name.indexOf("OZW672") >= 0) { - continue; - } + if (name.indexOf("OZW672") >= 0) { + continue; + } - logger.debug("Find devices: {} / {} / {} / {}", name, type, addr, serialNr); + logger.debug("Find devices: {} / {} / {} / {}", name, type, addr, serialNr); - String typeSn = UidUtils.sanetizeId(type); - ThingTypeUID thingTypeUID = new ThingTypeUID(SiemensHvacBindingConstants.BINDING_ID, typeSn); + String typeSn = UidUtils.sanetizeId(type); + ThingTypeUID thingTypeUID = new ThingTypeUID(SiemensHvacBindingConstants.BINDING_ID, typeSn); - ThingUID thingUID = getThingUID(thingTypeUID, serialNr); + ThingUID thingUID = getThingUID(thingTypeUID, serialNr); - if (siemensHvacBridgeHandler != null) { - ThingUID bridgeUID = siemensHvacBridgeHandler.getThing().getUID(); + if (lcSiemensHvacBridgeHandler != null) { + ThingUID bridgeUID = lcSiemensHvacBridgeHandler.getThing().getUID(); - if (thingUID != null) { - Map properties = new HashMap<>(1); - properties.put("name", name); - properties.put("type", type); - properties.put("addr", addr); - properties.put("serialNr", serialNr); + if (thingUID != null) { + Map properties = new HashMap<>(1); + properties.put("name", name); + properties.put("type", type); + properties.put("addr", addr); + properties.put("serialNr", serialNr); - DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID) - .withProperties(properties).withBridge(bridgeUID).withLabel(name).build(); + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID) + .withProperties(properties).withBridge(bridgeUID).withLabel(name).build(); - thingDiscovered(discoveryResult); - } + thingDiscovered(discoveryResult); } - } + } } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java index 31a371953d956..44f7ccc670796 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -44,18 +44,18 @@ public abstract class SiemensHvacBridgeBaseThingHandler extends BaseBridgeHandle private final @Nullable NetworkAddressService networkAddressService; private final @Nullable HttpClientFactory httpClientFactory; private final SiemensHvacMetadataRegistry metaDataRegistry; - private @Nullable SiemensHvacConnector connector; public SiemensHvacBridgeBaseThingHandler(Bridge bridge, @Nullable NetworkAddressService networkAddressService, @Nullable HttpClientFactory httpClientFactory, SiemensHvacMetadataRegistry metaDataRegistry) { super(bridge); + SiemensHvacConnector lcConnector = null; this.networkAddressService = networkAddressService; this.httpClientFactory = httpClientFactory; this.metaDataRegistry = metaDataRegistry; - connector = this.metaDataRegistry.getSiemensHvacConnector(); - if (connector != null) { - connector.setSiemensHvacBridgeBaseThingHandler(this); + lcConnector = this.metaDataRegistry.getSiemensHvacConnector(); + if (lcConnector != null) { + lcConnector.setSiemensHvacBridgeBaseThingHandler(this); } } @@ -90,9 +90,10 @@ public void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nu } public boolean registerDiscoveryListener(SiemensHvacDeviceDiscoveryService listener) { - if (discoveryService == null) { - discoveryService = listener; - discoveryService.setSiemensHvacMetadataRegistry(metaDataRegistry); + SiemensHvacDeviceDiscoveryService lcDiscoveryService = discoveryService; + if (lcDiscoveryService == null) { + lcDiscoveryService = listener; + lcDiscoveryService.setSiemensHvacMetadataRegistry(metaDataRegistry); // getFullLights().forEach(listener::addLightDiscovery); return true; } @@ -101,8 +102,9 @@ public boolean registerDiscoveryListener(SiemensHvacDeviceDiscoveryService liste } public boolean unregisterDiscoveryListener() { - if (discoveryService != null) { - discoveryService = null; + SiemensHvacDeviceDiscoveryService lcDiscoveryService = discoveryService; + if (lcDiscoveryService != null) { + lcDiscoveryService = null; return true; } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index eee951d0989e2..b7ed4124cddb3 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -120,12 +120,14 @@ public void initialize() { @Override public void dispose() { - if (pollingJob != null) { - pollingJob.cancel(true); + ScheduledFuture lcPollingJob = pollingJob; + if (lcPollingJob != null) { + lcPollingJob.cancel(true); } } private void pollingCode() { + SiemensHvacChannelTypeProvider lcChannelTypeProvider = channelTypeProvider; var chList = this.getThing().getChannels(); for (Channel ch : chList) { @@ -144,8 +146,8 @@ private void pollingCode() { ChannelType tp = null; - if (channelTypeProvider != null) { - tp = channelTypeProvider.getInternalChannelType(ch.getChannelTypeUID()); + if (lcChannelTypeProvider != null) { + tp = lcChannelTypeProvider.getInternalChannelType(ch.getChannelTypeUID()); } if (tp == null) { @@ -223,6 +225,8 @@ public void DecodeReadDp(@Nullable JsonObject response, @Nullable String uid, @N } private void ReadDp(String dp, String uid, String type, boolean async) { + SiemensHvacConnector lcHvacConnector = hvacConnector; + if (("-1").equals(dp)) { return; } @@ -235,8 +239,8 @@ private void ReadDp(String dp, String uid, String type, boolean async) { // logger.debug("siemensHvac:ReadDp:DoRequest():" + request); if (async) { - if (hvacConnector != null) { - hvacConnector.DoRequest(request, new SiemensHvacCallback() { + if (lcHvacConnector != null) { + lcHvacConnector.DoRequest(request, new SiemensHvacCallback() { @Override public void execute(java.net.URI uri, int status, @Nullable Object response) { @@ -247,8 +251,8 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { }); } } else { - if (hvacConnector != null) { - JsonObject js = hvacConnector.DoRequest(request, null); + if (lcHvacConnector != null) { + JsonObject js = lcHvacConnector.DoRequest(request, null); DecodeReadDp(js, uid, dp, type); } } @@ -259,6 +263,8 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { } private void WriteDp(String dp, Type dpVal, String type) { + SiemensHvacMetadataRegistry lcMetaDataRegistry = metaDataRegistry; + SiemensHvacConnector lcHvacConnector = hvacConnector; if (("-1").equals(dp)) { return; } @@ -289,8 +295,8 @@ private void WriteDp(String dp, Type dpVal, String type) { SiemensHvacMetadataDataPoint md = null; String dptType = null; - if (metaDataRegistry != null) { - md = (SiemensHvacMetadataDataPoint) metaDataRegistry.getDptMap(dp); + if (lcMetaDataRegistry != null) { + md = (SiemensHvacMetadataDataPoint) lcMetaDataRegistry.getDptMap(dp); } if (md != null) { dptType = md.getDptType(); @@ -298,8 +304,8 @@ private void WriteDp(String dp, Type dpVal, String type) { String request = "api/menutree/write_datapoint.json?Id=" + dp + "&Value=" + valUpdate + "&Type=" + dptType; - if (hvacConnector != null) { - hvacConnector.DoRequest(request, new SiemensHvacCallback() { + if (lcHvacConnector != null) { + lcHvacConnector.DoRequest(request, new SiemensHvacCallback() { @Override public void execute(java.net.URI uri, int status, @Nullable Object response) { @@ -318,6 +324,7 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { @Override public void handleCommand(ChannelUID channelUID, Command command) { + SiemensHvacChannelTypeProvider lcChannelTypeProvider = channelTypeProvider; logger.debug("handleCommand"); if (command instanceof RefreshType) { logger.debug("handleCommandRefresh"); @@ -329,8 +336,8 @@ public void handleCommand(ChannelUID channelUID, Command command) { } ChannelType tp = null; - if (channelTypeProvider != null) { - tp = channelTypeProvider.getInternalChannelType(channel.getChannelTypeUID()); + if (lcChannelTypeProvider != null) { + tp = lcChannelTypeProvider.getInternalChannelType(channel.getChannelTypeUID()); } if (tp == null) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java index 80c9530b9d04c..94bde8704dc87 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java @@ -33,6 +33,5 @@ public interface SiemensHvacConnector { public void onError(Request request); - public void setSiemensHvacBridgeBaseThingHandler( - @Nullable SiemensHvacBridgeBaseThingHandler hvacBridgeBaseThingHandler); + public void setSiemensHvacBridgeBaseThingHandler(SiemensHvacBridgeBaseThingHandler hvacBridgeBaseThingHandler); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 105fc98a2c059..5f12c21f930f4 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -37,6 +37,7 @@ import org.openhab.binding.siemenshvac.internal.type.SiemensHvacException; import org.openhab.core.config.core.Configuration; import org.openhab.core.io.net.http.HttpClientFactory; +import org.openhab.core.thing.Bridge; import org.openhab.core.types.Type; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; @@ -175,8 +176,11 @@ public void onError(@Nullable Request request) { private void _initConfig() throws Exception { Configuration config = null; - if (hvacBridgeBaseThingHandler != null) { - config = hvacBridgeBaseThingHandler.getThing().getConfiguration(); + SiemensHvacBridgeBaseThingHandler lcHvacBridgeBaseThingHandler = hvacBridgeBaseThingHandler; + + if (lcHvacBridgeBaseThingHandler != null) { + Bridge bridge = lcHvacBridgeBaseThingHandler.getThing(); + config = bridge.getConfiguration(); } else { throw new SiemensHvacException( "siemensHvac:Exception unable to get config because hvacBridgeBaseThingHandler is null"); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java index 17fe7af89315a..deabf9688fd1d 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java @@ -12,8 +12,6 @@ */ package org.openhab.binding.siemenshvac.internal.type; -import java.util.Locale; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.thing.type.ChannelGroupType; @@ -35,7 +33,7 @@ public interface SiemensHvacChannelGroupTypeProvider extends ChannelGroupTypePro /** * Use this method to lookup a ChannelGroupType which was generated by the - * homematic binding. Other than {@link #getChannelGroupType(ChannelGroupTypeUID, Locale)} + * siemensHvac binding. Other than {@link #getChannelGroupType(ChannelGroupTypeUID)} * of this provider, it will return also those {@link ChannelGroupType}s * which are excluded by {@link HomematicThingTypeExcluder} * diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java index 3cc5ac62a048b..7730c681848ed 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java @@ -23,7 +23,6 @@ import org.openhab.core.thing.type.ChannelGroupType; import org.openhab.core.thing.type.ChannelGroupTypeProvider; import org.openhab.core.thing.type.ChannelGroupTypeUID; -import org.openhab.core.thing.type.ChannelTypeRegistry; import org.osgi.service.component.annotations.Component; /** @@ -58,7 +57,7 @@ public ChannelGroupType getChannelGroupType(ChannelGroupTypeUID channelGroupType /** * - * @see ChannelTypeRegistry#getChannelGroupTypes(Locale) + * @see ChannelTypeRegistr#getChannelGroupTypes(Locale) * */ @Override diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java index 7494af0f73fef..34254a73cc5f5 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java @@ -12,8 +12,6 @@ */ package org.openhab.binding.siemenshvac.internal.type; -import java.util.Locale; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.thing.type.ChannelType; @@ -35,12 +33,11 @@ public interface SiemensHvacChannelTypeProvider extends ChannelTypeProvider { /** * Use this method to lookup a ChannelType which was generated by the - * homematic binding. Other than {@link #getChannelType(ChannelTypeUID, Locale)} + * siemensHvac binding. Other than {@link #getChannelType(ChannelTypeUID)} * of this provider, it will return also those {@link ChannelType}s * which are excluded by {@link HomematicThingTypeExcluder} * * @param channelTypeUID - * e.g. homematic:HM-WDS40-TH-I-2_0_RSSI_DEVICE * @return ChannelType that was added to HomematicChannelTypeProvider, identified * by its config-description-uri
* null if no ChannelType with the given UID was added diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java index 25e35a33f7031..99339142bcdc6 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java @@ -22,7 +22,6 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.thing.type.ChannelType; import org.openhab.core.thing.type.ChannelTypeProvider; -import org.openhab.core.thing.type.ChannelTypeRegistry; import org.openhab.core.thing.type.ChannelTypeUID; import org.osgi.service.component.annotations.Component; @@ -58,7 +57,7 @@ public Collection getChannelTypes(@Nullable Locale locale) { } /** - * @see ChannelTypeRegistry#getChannelType(ChannelTypeUID, Locale) + * @see ChannelTypeRegistr#getChannelType(ChannelTypeUID, Locale) */ @Nullable @Override diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java index 2dd95091fa335..12d48b4eba24e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java @@ -12,8 +12,6 @@ */ package org.openhab.binding.siemenshvac.internal.type; -import java.util.Locale; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.thing.ThingTypeUID; @@ -35,7 +33,7 @@ public interface SiemensHvacThingTypeProvider extends ThingTypeProvider { /** * Use this method to lookup a ThingType which was generated by the - * binding. Other than {@link #getThingType(ThingTypeUID, Locale)} + * binding. Other than {@link #getThingType(ThingTypeUID)} * of this provider, it will return also those {@link ThingType}s which are * excluded by {@link ThingTypeExcluder} * From cc546848b9120034e5a17e89e98a40014a85787d Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sat, 14 Jan 2023 16:47:40 +0100 Subject: [PATCH 011/214] review handling of value updates Signed-off-by: Laurent ARNAL --- .../SiemensHvacMetadataRegistryImpl.java | 1 + .../handler/SiemensHvacHandlerImpl.java | 62 +++++++++++-------- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index 5fbab8a3bfa8b..a87573635bd67 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -277,6 +277,7 @@ public void ReadMeta() { SaveMetaDataToCache(); getRoot(); + lcDevices = devices; if (lcDevices != null) { for (SiemensHvacMetadataDevice device : lcDevices) { if (device.getType().indexOf("OZW672") >= 0) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index b7ed4124cddb3..cc1d31d2abd28 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -18,6 +18,8 @@ import java.time.ZonedDateTime; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -58,6 +60,8 @@ @NonNullByDefault public class SiemensHvacHandlerImpl extends BaseThingHandler implements SiemensHvacHandler { + private Lock lockObj = new ReentrantLock(); + private final Logger logger = LoggerFactory.getLogger(SiemensHvacHandlerImpl.class); private @Nullable ScheduledFuture pollingJob = null; @@ -231,9 +235,9 @@ private void ReadDp(String dp, String uid, String type, boolean async) { return; } - try - - { + try { + lockObj.lock(); + logger.info("Start read :" + dp); String request = "api/menutree/read_datapoint.json?Id=" + dp; // logger.debug("siemensHvac:ReadDp:DoRequest():" + request); @@ -259,17 +263,22 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { } catch (Exception e) { logger.error("siemensHvac:ReadDp:Error during dp reading: {} ; {}", dp, e.getLocalizedMessage()); // Reset sessionId so we redone _auth on error + } finally { + logger.info("End read :" + dp); + lockObj.unlock(); + } } private void WriteDp(String dp, Type dpVal, String type) { - SiemensHvacMetadataRegistry lcMetaDataRegistry = metaDataRegistry; SiemensHvacConnector lcHvacConnector = hvacConnector; if (("-1").equals(dp)) { return; } try { + lockObj.lock(); + logger.info("Start write :" + dp); String valUpdate = "0"; String valUpdateEnum = ""; @@ -292,38 +301,30 @@ private void WriteDp(String dp, Type dpVal, String type) { } } - SiemensHvacMetadataDataPoint md = null; - String dptType = null; - - if (lcMetaDataRegistry != null) { - md = (SiemensHvacMetadataDataPoint) lcMetaDataRegistry.getDptMap(dp); - } - if (md != null) { - dptType = md.getDptType(); - } - - String request = "api/menutree/write_datapoint.json?Id=" + dp + "&Value=" + valUpdate + "&Type=" + dptType; + String request = "api/menutree/write_datapoint.json?Id=" + dp + "&Value=" + valUpdate + "&Type=" + type; if (lcHvacConnector != null) { - lcHvacConnector.DoRequest(request, new SiemensHvacCallback() { + logger.info("Write request for : " + valUpdate); + JsonObject response = lcHvacConnector.DoRequest(request, null); - @Override - public void execute(java.net.URI uri, int status, @Nullable Object response) { - if (response instanceof JsonObject) { - logger.debug("p1"); - } - } - }); + logger.info("Write request response : " + response); + if (response instanceof JsonObject) { + logger.debug("p1"); + } } } catch (Exception e) { logger.error("siemensHvac:ReadDp:Error during dp reading: {} ; {}", dp, e.getLocalizedMessage()); // Reset sessionId so we redone _auth on error + } finally { + logger.info("End write :" + dp); + lockObj.unlock(); } } @Override public void handleCommand(ChannelUID channelUID, Command command) { + SiemensHvacMetadataRegistry lcMetaDataRegistry = metaDataRegistry; SiemensHvacChannelTypeProvider lcChannelTypeProvider = channelTypeProvider; logger.debug("handleCommand"); if (command instanceof RefreshType) { @@ -347,10 +348,21 @@ public void handleCommand(ChannelUID channelUID, Command command) { String dptId = channel.getProperties().get("dptId"); String uid = channel.getUID().getId(); String type = tp.getItemType(); + String dptType = ""; + String id = ""; + SiemensHvacMetadataDataPoint md = null; + + if (lcMetaDataRegistry != null) { + md = (SiemensHvacMetadataDataPoint) lcMetaDataRegistry.getDptMap(dptId); + if (md != null) { + id = "" + md.getId(); + dptType = md.getDptType(); + } + } if (dptId != null && type != null) { - WriteDp(dptId, command, type); - ReadDp(dptId, uid, type, false); + WriteDp(id, command, dptType); + ReadDp(id, uid, dptType, false); } } } From 98d657a122bfab3c0db2b7e0a57de08359764d59 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sat, 14 Jan 2023 19:15:01 +0100 Subject: [PATCH 012/214] review logger params Signed-off-by: Laurent ARNAL --- .../internal/handler/SiemensHvacHandlerImpl.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index cc1d31d2abd28..3751829738304 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -237,7 +237,7 @@ private void ReadDp(String dp, String uid, String type, boolean async) { try { lockObj.lock(); - logger.info("Start read :" + dp); + logger.info("Start read : {}", dp); String request = "api/menutree/read_datapoint.json?Id=" + dp; // logger.debug("siemensHvac:ReadDp:DoRequest():" + request); @@ -264,7 +264,7 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { logger.error("siemensHvac:ReadDp:Error during dp reading: {} ; {}", dp, e.getLocalizedMessage()); // Reset sessionId so we redone _auth on error } finally { - logger.info("End read :" + dp); + logger.info("End read : {}", dp); lockObj.unlock(); } @@ -278,7 +278,7 @@ private void WriteDp(String dp, Type dpVal, String type) { try { lockObj.lock(); - logger.info("Start write :" + dp); + // logger.info("Start write :" + dp); String valUpdate = "0"; String valUpdateEnum = ""; @@ -304,10 +304,10 @@ private void WriteDp(String dp, Type dpVal, String type) { String request = "api/menutree/write_datapoint.json?Id=" + dp + "&Value=" + valUpdate + "&Type=" + type; if (lcHvacConnector != null) { - logger.info("Write request for : " + valUpdate); + logger.info("Write request for : {} ", valUpdate); JsonObject response = lcHvacConnector.DoRequest(request, null); - logger.info("Write request response : " + response); + logger.info("Write request response : {} ", response); if (response instanceof JsonObject) { logger.debug("p1"); } @@ -317,7 +317,7 @@ private void WriteDp(String dp, Type dpVal, String type) { logger.error("siemensHvac:ReadDp:Error during dp reading: {} ; {}", dp, e.getLocalizedMessage()); // Reset sessionId so we redone _auth on error } finally { - logger.info("End write :" + dp); + logger.info("End write : {}", dp); lockObj.unlock(); } } From 5a3046203b2ad89780251a1b7d831d77e054c4ff Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 15 Jan 2023 10:27:33 +0100 Subject: [PATCH 013/214] review handleCommand code Signed-off-by: Laurent ARNAL --- .../internal/handler/SiemensHvacHandlerImpl.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index 3751829738304..b5771181a4f8f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -41,6 +41,7 @@ import org.openhab.core.thing.type.ChannelType; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; +import org.openhab.core.types.State; import org.openhab.core.types.Type; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; @@ -346,7 +347,6 @@ public void handleCommand(ChannelUID channelUID, Command command) { } String dptId = channel.getProperties().get("dptId"); - String uid = channel.getUID().getId(); String type = tp.getItemType(); String dptType = ""; String id = ""; @@ -360,9 +360,13 @@ public void handleCommand(ChannelUID channelUID, Command command) { } } + if (command instanceof State) { + State state = (State) command; + this.updateState(channelUID, state); + } + if (dptId != null && type != null) { WriteDp(id, command, dptType); - ReadDp(id, uid, dptType, false); } } } From 0d83e532417d094a16717e0d3dbc53151b2b3475 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sat, 21 Jan 2023 15:47:02 +0100 Subject: [PATCH 014/214] review initialization and error handling review logging Signed-off-by: Laurent ARNAL --- .../SiemensHvacMetadataRegistryImpl.java | 62 ++++----------- .../handler/SiemensHvacHandlerImpl.java | 16 ++-- .../network/SiemensHvacConnector.java | 4 +- .../network/SiemensHvacConnectorImpl.java | 75 +++++++++++++++---- .../network/SiemensHvacRequestListener.java | 37 +++++++-- 5 files changed, 113 insertions(+), 81 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index a87573635bd67..b7fc11b9a2a2b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -231,7 +231,9 @@ public int UnresolveCount() { } } } + } + return count; } @@ -244,7 +246,7 @@ public int UnresolveCount() { public void ReadMeta() { ArrayList lcDevices = devices; SiemensHvacConnector lcHvacConnector = hvacConnector; - if (root == null) { + if (root == null && lcHvacConnector != null) { logger.debug("siemensHvac:InitDptMap():begin"); LoadMetaDataFromCache(); @@ -254,24 +256,21 @@ public void ReadMeta() { if (root == null) { root = new SiemensHvacMetadataMenu(); ReadMetaData(root, -1); - if (lcHvacConnector != null) { - lcHvacConnector.WaitAllPendingRequest(); - } + lcHvacConnector.WaitNoNewRequest(); + lcHvacConnector.WaitAllPendingRequest(); } if (root != null) { InitDptMap(root); } - int unresolveCount = Integer.MAX_VALUE; + int unresolveCount = UnresolveCount(); + // unresolveCount = 0; while (unresolveCount > 0) { ResolveDetails(); - if (lcHvacConnector != null) { - lcHvacConnector.WaitAllPendingRequest(); - } + lcHvacConnector.WaitAllPendingRequest(); unresolveCount = UnresolveCount(); - } SaveMetaDataToCache(); @@ -355,8 +354,8 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { if (channelType != null) { ChannelDefinition channelDef = new ChannelDefinitionBuilder(id, channelType.getUID()).withLabel(dataPoint.getShortDesc()) - .withDescription(dataPoint.getLongDesc()) - .withProperties(props).build(); + .withDescription(dataPoint.getLongDesc()).withProperties(props) + .build(); channelDefinitions.add(channelDef); } @@ -676,8 +675,11 @@ public void ReadMetaData(@Nullable SiemensHvacMetadata parent, int id) { @Override public void execute(URI uri, int status, @Nullable Object response) { + logger.debug("response for {}, status {}:", uri, status); if (response instanceof JsonObject) { DecodeMetaDataResult((JsonObject) response, parent, id); + } else { + logger.debug("error status {}: {}", uri, status); } } }); @@ -748,40 +750,7 @@ public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMeta ((SiemensHvacMetadataMenu) parent).AddChild(childNode); } - // logger.debug(String.format("siemensHvac:ResolveDpt():findMenuItem: %d, %s, %s, %s, %s", itemId, - // subItemId, groupId, catId, longDesc)); - - boolean shouldReadSub = false; - - shouldReadSub = shouldReadSub || (itemId == 931); - shouldReadSub = shouldReadSub || (itemId == 932); - // shouldReadSub = shouldReadSub || (itemId == 936); - // shouldReadSub = shouldReadSub || (itemId == 947); - // shouldReadSub = shouldReadSub || (itemId == 956); - // shouldReadSub = shouldReadSub || (itemId == 965); - // shouldReadSub = shouldReadSub || (itemId == 974); - // shouldReadSub = shouldReadSub || (itemId == 992); - // shouldReadSub = shouldReadSub || (itemId == 1017); - // shouldReadSub = shouldReadSub || (itemId == 1030); - // shouldReadSub = shouldReadSub || (itemId == 1036); - // shouldReadSub = shouldReadSub || (itemId == 1040); - // shouldReadSub = shouldReadSub || (itemId == 1046); - // shouldReadSub = shouldReadSub || (itemId == 1070); - // shouldReadSub = shouldReadSub || (itemId == 1099); - // shouldReadSub = shouldReadSub || (itemId == 1260); - // shouldReadSub = shouldReadSub || (itemId == 1279); - // shouldReadSub = shouldReadSub || (itemId == 1300); - // shouldReadSub = shouldReadSub || (itemId == 1328); - // shouldReadSub = shouldReadSub || (itemId == 1489); - // shouldReadSub = shouldReadSub || (itemId == 1505); - // shouldReadSub = shouldReadSub || (itemId == 1558); - - shouldReadSub = shouldReadSub || true; - - if (shouldReadSub) { - ReadMetaData(childNode, itemId); - } - + ReadMetaData(childNode, itemId); } } @@ -863,9 +832,6 @@ public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMeta childNode.setGroupId(groupId); childNode.setShortDesc(shortDesc); childNode.setLongDesc(longDesc); - - // logger.debug(String.format("siemensHvac:ResolveDpt():findDpItem: %d, %s, %s, %s, %s %s", dptId, - // catId, groupId, subItemId, shortDesc, longDesc)); } if (parent != null) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index b5771181a4f8f..25e76856e499e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -43,7 +43,6 @@ import org.openhab.core.types.RefreshType; import org.openhab.core.types.State; import org.openhab.core.types.Type; -import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -57,7 +56,6 @@ * * @author Laurent ARNAL - Initial contribution */ -@Component(immediate = true) @NonNullByDefault public class SiemensHvacHandlerImpl extends BaseThingHandler implements SiemensHvacHandler { @@ -238,7 +236,7 @@ private void ReadDp(String dp, String uid, String type, boolean async) { try { lockObj.lock(); - logger.info("Start read : {}", dp); + logger.debug("Start read : {}", dp); String request = "api/menutree/read_datapoint.json?Id=" + dp; // logger.debug("siemensHvac:ReadDp:DoRequest():" + request); @@ -265,7 +263,7 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { logger.error("siemensHvac:ReadDp:Error during dp reading: {} ; {}", dp, e.getLocalizedMessage()); // Reset sessionId so we redone _auth on error } finally { - logger.info("End read : {}", dp); + logger.debug("End read : {}", dp); lockObj.unlock(); } @@ -279,7 +277,6 @@ private void WriteDp(String dp, Type dpVal, String type) { try { lockObj.lock(); - // logger.info("Start write :" + dp); String valUpdate = "0"; String valUpdateEnum = ""; @@ -305,20 +302,17 @@ private void WriteDp(String dp, Type dpVal, String type) { String request = "api/menutree/write_datapoint.json?Id=" + dp + "&Value=" + valUpdate + "&Type=" + type; if (lcHvacConnector != null) { - logger.info("Write request for : {} ", valUpdate); + logger.debug("Write request for : {} ", valUpdate); JsonObject response = lcHvacConnector.DoRequest(request, null); - logger.info("Write request response : {} ", response); - if (response instanceof JsonObject) { - logger.debug("p1"); - } + logger.debug("Write request response : {} ", response); } } catch (Exception e) { logger.error("siemensHvac:ReadDp:Error during dp reading: {} ; {}", dp, e.getLocalizedMessage()); // Reset sessionId so we redone _auth on error } finally { - logger.info("End write : {}", dp); + logger.debug("End write : {}", dp); lockObj.unlock(); } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java index 94bde8704dc87..597a082defd25 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java @@ -29,9 +29,11 @@ public interface SiemensHvacConnector { public void WaitAllPendingRequest(); + public void WaitNoNewRequest(); + public void onComplete(Request request); - public void onError(Request request); + public void onError(Request request, SiemensHvacCallback cb) throws Exception; public void setSiemensHvacBridgeBaseThingHandler(SiemensHvacBridgeBaseThingHandler hvacBridgeBaseThingHandler); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 5f12c21f930f4..fa74f9acd885a 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -70,7 +70,6 @@ public class SiemensHvacConnectorImpl implements SiemensHvacConnector { protected final HttpClientFactory httpClientFactory; protected HttpClient httpClient; - protected HttpClient httpClientInsecure; private static int startedRequest = 0; private static int completedRequest = 0; @@ -85,15 +84,26 @@ public SiemensHvacConnectorImpl(@Reference HttpClientFactory httpClientFactory) this.updateCommand = new Hashtable(); this.httpClientFactory = httpClientFactory; - this.httpClient = httpClientFactory.getCommonHttpClient(); - this.httpClient.setMaxConnectionsPerDestination(15); - this.httpClientInsecure = new HttpClient(new SslContextFactory.Client(true)); - this.httpClientInsecure.setRemoveIdleDestinations(true); - this.httpClientInsecure.setMaxConnectionsPerDestination(15); + SslContextFactory ctxFactory = new SslContextFactory.Client(true); + ctxFactory.setRenegotiationAllowed(false); + ctxFactory.setEnableCRLDP(false); + ctxFactory.setEnableOCSP(false); + ctxFactory.setTrustAll(true); + ctxFactory.setValidateCerts(false); + ctxFactory.setValidatePeerCerts(false); + ctxFactory.setEndpointIdentificationAlgorithm(null); + + this.httpClient = new HttpClient(ctxFactory); + this.httpClient.setRemoveIdleDestinations(true); + this.httpClient.setMaxConnectionsPerDestination(10); + this.httpClient.setMaxRequestsQueuedPerDestination(1000); + this.httpClient.setConnectTimeout(10000); + this.httpClient.setFollowRedirects(false); + try { - this.httpClientInsecure.start(); + this.httpClient.start(); } catch (Exception e) { - logger.warn("Failed to start insecure http client: {}", e.getMessage()); + logger.error("Failed to start http client: {}", e.getMessage()); } } @@ -123,6 +133,7 @@ public void setUserPassword(String userPassword) { public void onComplete(@Nullable Request request) { lockObj.lock(); try { + logger.debug("OnComplete"); completedRequest++; } finally { lockObj.unlock(); @@ -130,18 +141,35 @@ public void onComplete(@Nullable Request request) { } @Override - public void onError(@Nullable Request request) { + public void onError(@Nullable Request request, @Nullable SiemensHvacCallback cb) throws Exception { lockObj.lock(); try { + logger.debug("OnError"); completedRequest++; } finally { lockObj.unlock(); } + + if (cb == null || request == null) { + return; + } + + try { + final Request retryRequest = httpClient.newRequest(request.getURI()); + request.method(HttpMethod.GET); + + if (retryRequest != null) { + executeRequest(retryRequest, cb); + } + } catch (Exception ex) { + logger.error("exception"); + throw ex; + } } private @Nullable ContentResponse executeRequest(final Request request, @Nullable SiemensHvacCallback callback) throws Exception { - request.timeout(60, TimeUnit.SECONDS); + request.timeout(240, TimeUnit.SECONDS); ContentResponse response = null; @@ -158,6 +186,8 @@ public void onError(@Nullable Request request) { lockObj.lock(); try { startedRequest++; + logger.debug("StartedRequest : {}", startedRequest); + } finally { lockObj.unlock(); } @@ -189,6 +219,7 @@ private void _initConfig() throws Exception { if (config.containsKey("baseUrl")) { baseUrl = (String) config.get("baseUrl"); } + baseUrl = "https://192.168.254.42/"; if (config.containsKey("userName")) { userName = (String) config.get("userName"); } @@ -203,7 +234,7 @@ private void _doAuth() throws Exception { _initConfig(); String baseUri = baseUrl; String uri = "api/auth/login.json?user=" + userName + "&pwd=" + userPassword; - final Request request = httpClientInsecure.newRequest(baseUri + uri); + final Request request = httpClient.newRequest(baseUri + uri); request.method(HttpMethod.GET); logger.debug("siemensHvac:doAuth:connect()"); @@ -277,9 +308,9 @@ private void _doAuth() throws Exception { mUri = mUri + "&"; } mUri = mUri + "SessionId=" + sessionId; - mUri = mUri + "&user=" + userName + "&pwd=" + userPassword; - final Request request = httpClientInsecure.newRequest(baseUri + mUri); + logger.debug("Execute request: {}", uri); + final Request request = httpClient.newRequest(baseUri + mUri); request.method(HttpMethod.GET); ContentResponse response = executeRequest(request, callback); @@ -360,6 +391,24 @@ public void WaitAllPendingRequest() { logger.debug("WaitAllPendingRequest:end WaitAllPendingRequest"); } + @Override + public void WaitNoNewRequest() { + logger.debug("WaitNoNewRequest:start"); + try { + int lastRequest = startedRequest; + Thread.sleep(5000); + while (lastRequest != startedRequest) { + logger.debug("waitNoNewRequest {}/{})", startedRequest, lastRequest); + Thread.sleep(5000); + lastRequest = startedRequest; + } + } catch (InterruptedException ex) { + logger.debug("WaitAllPendingRequest:interrupted in WaitAllRequest"); + } + + logger.debug("WaitNoNewRequest:end WaitAllStartingRequest"); + } + public static Gson getGson() { GsonBuilder builder = new GsonBuilder(); Gson gson = builder.setPrettyPrinting().create(); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java index 4e4bcada5ae08..76af15d6398a5 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java @@ -14,6 +14,9 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.api.Request.BeginListener; +import org.eclipse.jetty.client.api.Request.QueuedListener; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Response.CompleteListener; import org.eclipse.jetty.client.api.Response.ContentListener; @@ -32,7 +35,7 @@ */ @NonNullByDefault public class SiemensHvacRequestListener extends BufferingResponseListener - implements SuccessListener, FailureListener, ContentListener, CompleteListener { + implements SuccessListener, FailureListener, ContentListener, CompleteListener, QueuedListener, BeginListener { private static final Logger logger = LoggerFactory.getLogger(SiemensHvacRequestListener.class); private SiemensHvacConnector hvacConnector; @@ -54,7 +57,9 @@ public SiemensHvacRequestListener(SiemensHvacCallback callback, SiemensHvacConne @Override public void onSuccess(@Nullable Response response) { - // logger.debug("{} response: {}", response.getRequest().getURI(), response.getStatus()); + if (response != null) { + logger.debug("{} response: {}", response.getRequest().getURI(), response.getStatus()); + } } @Override @@ -65,20 +70,33 @@ public void onFailure(@Nullable Response response, @Nullable Throwable failure) } } + @Override + public void onQueued(@Nullable Request request) { + if (request == null) { + return; + } + } + + @Override + public void onBegin(@Nullable Request request) { + if (request == null) { + return; + } + } + @Override public void onComplete(@Nullable Result result) { if (result == null) { return; } - hvacConnector.onComplete(result.getRequest()); - try { String content = getContentAsString(); logger.trace("response complete: {}", content); if (result.getResponse().getStatus() != 200) { logger.debug("bad gateway !!!"); + hvacConnector.onError(result.getRequest(), callback); return; } @@ -106,25 +124,27 @@ public void onComplete(@Nullable Result result) { } if (resultVal) { - + hvacConnector.onComplete(result.getRequest()); callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), resultObj); + return; } else if (("datatype not supported").equals(errorMsg)) { + hvacConnector.onComplete(result.getRequest()); callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), resultObj); } else { logger.debug("error : {}", subResultObj); - hvacConnector.onError(result.getRequest()); + hvacConnector.onError(result.getRequest(), callback); } } else { logger.debug("error"); - hvacConnector.onError(result.getRequest()); + hvacConnector.onError(result.getRequest(), callback); } } else { logger.debug("error"); - hvacConnector.onError(result.getRequest()); + hvacConnector.onError(result.getRequest(), callback); } return; @@ -132,6 +152,7 @@ public void onComplete(@Nullable Result result) { } callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), content); + hvacConnector.onComplete(result.getRequest()); } catch (Exception ex) { logger.debug("error"); } From 2b90a2da5f73f2a2a233abe244a4bd0ba64e3d84 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sat, 21 Jan 2023 15:48:41 +0100 Subject: [PATCH 015/214] apply spotless Signed-off-by: Laurent ARNAL --- .../internal/Metadata/SiemensHvacMetadataRegistryImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index b7fc11b9a2a2b..d74e80fa59111 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -354,8 +354,8 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { if (channelType != null) { ChannelDefinition channelDef = new ChannelDefinitionBuilder(id, channelType.getUID()).withLabel(dataPoint.getShortDesc()) - .withDescription(dataPoint.getLongDesc()).withProperties(props) - .build(); + .withDescription(dataPoint.getLongDesc()) + .withProperties(props).build(); channelDefinitions.add(channelDef); } From 22e124391b4c1007fd6d00154095a10b4f89aead Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 22 Jan 2023 16:33:12 +0100 Subject: [PATCH 016/214] update resource file to new format Signed-off-by: Laurent ARNAL --- .../src/main/feature/feature.xml | 1 + .../src/main/resources/OH-INF/addon/addon.xml | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/feature/feature.xml b/bundles/org.openhab.binding.siemenshvac/src/main/feature/feature.xml index 537a825b6c7f5..a253c87858518 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/feature/feature.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/feature/feature.xml @@ -19,6 +19,7 @@ openhab-runtime-base openhab-transport-upnp + openhab-transport-mdns mvn:org.openhab.addons.bundles/org.openhab.binding.siemenshvac/${project.version} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml new file mode 100644 index 0000000000000..1ccbd7cd67cf8 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml @@ -0,0 +1,10 @@ + + + + binding + SiemensHvac Binding + This is the binding for SiemensHvac. + + From e65e7af89eef51a961ea9ec09ade2cfefc256cf6 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 22 Jan 2023 16:33:42 +0100 Subject: [PATCH 017/214] add more information during init phase Signed-off-by: Laurent ARNAL --- .../SiemensHvacMetadataRegistryImpl.java | 44 ++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index d74e80fa59111..230aa968b9b5c 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -197,7 +197,25 @@ public void InitDptMap(@Nullable SiemensHvacMetadata node) { } } - public void ResolveDetails() { + class ResolveCount { + private int resolveCount = 0; + + public ResolveCount(int count) { + resolveCount = count; + } + + public void DecreaseResolveCount() { + resolveCount--; + } + + public int getResolveCount() { + return resolveCount; + } + } + + public void ResolveDetails(int unresolveCountP) { + ResolveCount rv = new ResolveCount(unresolveCountP); + for (String key : dptMap.keySet()) { if (key.indexOf("byId") < 0) { continue; @@ -208,7 +226,7 @@ public void ResolveDetails() { if (node.getClass() == SiemensHvacMetadataDataPoint.class) { SiemensHvacMetadataDataPoint dpi = (SiemensHvacMetadataDataPoint) node; if (!dpi.getDetailsResolved()) { - resolveDptDetails(dpi); + resolveDptDetails(dpi, rv); } } } @@ -246,35 +264,46 @@ public int UnresolveCount() { public void ReadMeta() { ArrayList lcDevices = devices; SiemensHvacConnector lcHvacConnector = hvacConnector; + if (root == null && lcHvacConnector != null) { - logger.debug("siemensHvac:InitDptMap():begin"); + logger.info("siemensHvac:Initialization():Begin"); + logger.info("siemensHvac:Initialization():ReadCache"); LoadMetaDataFromCache(); + logger.info("siemensHvac:Initialization():ReadDeviceList"); ReadDeviceList(); if (root == null) { + logger.info("siemensHvac:Initialization():BeginReadMenu"); root = new SiemensHvacMetadataMenu(); ReadMetaData(root, -1); lcHvacConnector.WaitNoNewRequest(); lcHvacConnector.WaitAllPendingRequest(); + logger.info("siemensHvac:Initialization():EndReadMenu"); } if (root != null) { + logger.info("siemensHvac:Initialization():BeginInitDptMap"); InitDptMap(root); + logger.info("siemensHvac:Initialization():EndInitDptMap"); } int unresolveCount = UnresolveCount(); // unresolveCount = 0; while (unresolveCount > 0) { - ResolveDetails(); + logger.info("siemensHvac:Initialization():BeginResolveDtpMap {}", unresolveCount); + ResolveDetails(unresolveCount); lcHvacConnector.WaitAllPendingRequest(); unresolveCount = UnresolveCount(); + logger.info("siemensHvac:Initialization():EndResolveDtpMap {}", unresolveCount); } + logger.info("siemensHvac:Initialization():SaveCache"); SaveMetaDataToCache(); + logger.info("siemensHvac:Initialization():InitThing"); getRoot(); lcDevices = devices; if (lcDevices != null) { @@ -901,6 +930,9 @@ public void execute(URI uri, int status, @Nullable Object response) { if (dptMap.containsKey("byDptId" + key)) { return dptMap.get("byDptId" + key); } + if (dptMap.containsKey("byId" + key)) { + return dptMap.get("byDptId" + key); + } return null; } @@ -950,7 +982,7 @@ public void SaveMetaDataToCache() { } } - public void resolveDptDetails(SiemensHvacMetadataDataPoint dpt) { + public void resolveDptDetails(SiemensHvacMetadataDataPoint dpt, ResolveCount rv) { SiemensHvacConnector lcHvacConnector = hvacConnector; if (dpt.getDetailsResolved()) { return; @@ -963,6 +995,8 @@ public void resolveDptDetails(SiemensHvacMetadataDataPoint dpt) { @Override public void execute(URI uri, int status, @Nullable Object response) { if (response instanceof JsonObject) { + rv.DecreaseResolveCount(); + logger.info("siemensHvac:Initialization():ToResolve() {}", rv.getResolveCount()); dpt.resolveDptDetails((JsonObject) response); } else { logger.debug("errror"); From 79960185500352bf2abffb3f5378f85e435e842a Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 22 Jan 2023 16:54:31 +0100 Subject: [PATCH 018/214] fix bad map key Signed-off-by: Laurent ARNAL --- .../internal/Metadata/SiemensHvacMetadataRegistryImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index 230aa968b9b5c..32db463b079dc 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -383,8 +383,8 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { if (channelType != null) { ChannelDefinition channelDef = new ChannelDefinitionBuilder(id, channelType.getUID()).withLabel(dataPoint.getShortDesc()) - .withDescription(dataPoint.getLongDesc()) - .withProperties(props).build(); + .withDescription(dataPoint.getLongDesc()).withProperties(props) + .build(); channelDefinitions.add(channelDef); } @@ -931,7 +931,7 @@ public void execute(URI uri, int status, @Nullable Object response) { return dptMap.get("byDptId" + key); } if (dptMap.containsKey("byId" + key)) { - return dptMap.get("byDptId" + key); + return dptMap.get("byId" + key); } return null; From 70f493c8c0397154000c24b92afddcd07c9971f0 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 22 Jan 2023 16:55:06 +0100 Subject: [PATCH 019/214] review id Handling on handleCommand Signed-off-by: Laurent ARNAL --- .../handler/SiemensHvacHandlerImpl.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index 25e76856e499e..fa3a048dc4f5d 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -239,7 +239,7 @@ private void ReadDp(String dp, String uid, String type, boolean async) { logger.debug("Start read : {}", dp); String request = "api/menutree/read_datapoint.json?Id=" + dp; - // logger.debug("siemensHvac:ReadDp:DoRequest():" + request); + logger.debug("siemensHvac:ReadDp:DoRequest():" + request); if (async) { if (lcHvacConnector != null) { @@ -263,7 +263,7 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { logger.error("siemensHvac:ReadDp:Error during dp reading: {} ; {}", dp, e.getLocalizedMessage()); // Reset sessionId so we redone _auth on error } finally { - logger.debug("End read : {}", dp); + logger.info("End read : {}", dp); lockObj.unlock(); } @@ -302,17 +302,17 @@ private void WriteDp(String dp, Type dpVal, String type) { String request = "api/menutree/write_datapoint.json?Id=" + dp + "&Value=" + valUpdate + "&Type=" + type; if (lcHvacConnector != null) { - logger.debug("Write request for : {} ", valUpdate); + logger.info("Write request for : {} ", valUpdate); JsonObject response = lcHvacConnector.DoRequest(request, null); - logger.debug("Write request response : {} ", response); + logger.info("Write request response : {} ", response); } } catch (Exception e) { logger.error("siemensHvac:ReadDp:Error during dp reading: {} ; {}", dp, e.getLocalizedMessage()); // Reset sessionId so we redone _auth on error } finally { - logger.debug("End write : {}", dp); + logger.info("End write : {}", dp); lockObj.unlock(); } } @@ -340,14 +340,13 @@ public void handleCommand(ChannelUID channelUID, Command command) { return; } - String dptId = channel.getProperties().get("dptId"); String type = tp.getItemType(); String dptType = ""; - String id = ""; + String id = channel.getProperties().get("id"); SiemensHvacMetadataDataPoint md = null; if (lcMetaDataRegistry != null) { - md = (SiemensHvacMetadataDataPoint) lcMetaDataRegistry.getDptMap(dptId); + md = (SiemensHvacMetadataDataPoint) lcMetaDataRegistry.getDptMap(id); if (md != null) { id = "" + md.getId(); dptType = md.getDptType(); @@ -359,7 +358,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { this.updateState(channelUID, state); } - if (dptId != null && type != null) { + if (id != null && type != null) { WriteDp(id, command, dptType); } } From 3caacec85cf0581b805cf7509946d19ed07c6feb Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 22 Jan 2023 16:55:31 +0100 Subject: [PATCH 020/214] review logging Signed-off-by: Laurent ARNAL --- .../internal/handler/SiemensHvacHandlerImpl.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index fa3a048dc4f5d..76c059a3dd2a4 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -263,7 +263,7 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { logger.error("siemensHvac:ReadDp:Error during dp reading: {} ; {}", dp, e.getLocalizedMessage()); // Reset sessionId so we redone _auth on error } finally { - logger.info("End read : {}", dp); + logger.debug("End read : {}", dp); lockObj.unlock(); } @@ -302,17 +302,17 @@ private void WriteDp(String dp, Type dpVal, String type) { String request = "api/menutree/write_datapoint.json?Id=" + dp + "&Value=" + valUpdate + "&Type=" + type; if (lcHvacConnector != null) { - logger.info("Write request for : {} ", valUpdate); + logger.debug("Write request for : {} ", valUpdate); JsonObject response = lcHvacConnector.DoRequest(request, null); - logger.info("Write request response : {} ", response); + logger.debug("Write request response : {} ", response); } } catch (Exception e) { logger.error("siemensHvac:ReadDp:Error during dp reading: {} ; {}", dp, e.getLocalizedMessage()); // Reset sessionId so we redone _auth on error } finally { - logger.info("End write : {}", dp); + logger.debug("End write : {}", dp); lockObj.unlock(); } } From 9785be737a231da0b06885155880cd132f550223 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 22 Jan 2023 18:45:26 +0100 Subject: [PATCH 021/214] review authentification Signed-off-by: Laurent ARNAL --- .../network/SiemensHvacConnectorImpl.java | 101 +++++++++++++----- 1 file changed, 76 insertions(+), 25 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index fa74f9acd885a..1a41b07e86f82 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -12,8 +12,12 @@ */ package org.openhab.binding.siemenshvac.internal.network; +import java.net.CookieStore; +import java.net.HttpCookie; +import java.net.URI; import java.util.Date; import java.util.Hashtable; +import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -61,6 +65,7 @@ public class SiemensHvacConnectorImpl implements SiemensHvacConnector { private static final Logger logger = LoggerFactory.getLogger(SiemensHvacConnectorImpl.class); private @Nullable String sessionId = null; + private @Nullable String sessionIdHttp = null; private String baseUrl = ""; private String userName = ""; private String userPassword = ""; @@ -219,7 +224,7 @@ private void _initConfig() throws Exception { if (config.containsKey("baseUrl")) { baseUrl = (String) config.get("baseUrl"); } - baseUrl = "https://192.168.254.42/"; + if (config.containsKey("userName")) { userName = (String) config.get("userName"); } @@ -228,14 +233,25 @@ private void _initConfig() throws Exception { } } - private void _doAuth() throws Exception { + private void _doAuth(boolean http) throws Exception { logger.debug("siemensHvac:doAuth()"); _initConfig(); String baseUri = baseUrl; - String uri = "api/auth/login.json?user=" + userName + "&pwd=" + userPassword; + String uri = ""; + + if (http) { + uri = "main.app"; + } else { + uri = "api/auth/login.json?user=" + userName + "&pwd=" + userPassword; + } + final Request request = httpClient.newRequest(baseUri + uri); - request.method(HttpMethod.GET); + if (http) { + request.method(HttpMethod.POST).param("user", userName).param("pwd", userPassword); + } else { + request.method(HttpMethod.GET); + } logger.debug("siemensHvac:doAuth:connect()"); @@ -247,41 +263,60 @@ private void _doAuth() throws Exception { if (statusCode == HttpStatus.OK_200) { String result = response.getContentAsString(); - if (result != null) { - JsonObject resultObj = getGson().fromJson(result, JsonObject.class); + if (http) { + CookieStore cookieStore = httpClient.getCookieStore(); + List cookies = cookieStore.getCookies(); + + for (HttpCookie httpCookie : cookies) { + if (httpCookie.getName().equals("SessionId")) { + sessionIdHttp = httpCookie.getValue(); + } + + } + + if (sessionIdHttp == null) { + logger.debug("Session request auth was unsucessfull in _doAuth()"); + } + } else { + if (result != null) { + JsonObject resultObj = getGson().fromJson(result, JsonObject.class); + + if (resultObj != null && resultObj.has("Result")) { + JsonElement resultVal = resultObj.get("Result"); + JsonObject resultObj2 = resultVal.getAsJsonObject(); - if (resultObj != null && resultObj.has("Result")) { - JsonElement resultVal = resultObj.get("Result"); - JsonObject resultObj2 = resultVal.getAsJsonObject(); + if (resultObj2.has("Success")) { + boolean successVal = resultObj2.get("Success").getAsBoolean(); - if (resultObj2.has("Success")) { - boolean successVal = resultObj2.get("Success").getAsBoolean(); + if (successVal) { - if (successVal) { + if (resultObj.has("SessionId")) { + sessionId = resultObj.get("SessionId").getAsString(); + logger.debug("Have new SessionId : {} ", sessionId); + } - if (resultObj.has("SessionId")) { - sessionId = resultObj.get("SessionId").getAsString(); - logger.debug("Have new SessionId : {} ", sessionId); } } - } - } - logger.debug("siemensHvac:doAuth:decodeResponse:()"); + logger.debug("siemensHvac:doAuth:decodeResponse:()"); - } + if (sessionId == null) { + logger.debug("Session request auth was unsucessfull in _doAuth()"); + } + + } - if (sessionId == null) { - logger.debug("Session request auth was unsucessfull in _doAuth()"); } } } logger.debug("siemensHvac:doAuth:connect()"); - } catch (Exception ex) { + } catch ( + + Exception ex) { logger.debug("siemensHvac:doAuth:error() {}", ex.getLocalizedMessage()); } finally { } @@ -296,8 +331,12 @@ private void _doAuth() throws Exception { } public @Nullable String DoBasicRequest(String uri, @Nullable SiemensHvacCallback callback) throws Exception { + if (sessionIdHttp == null) { + _doAuth(true); + } + if (sessionId == null) { - _doAuth(); + _doAuth(false); } try { @@ -307,9 +346,21 @@ private void _doAuth() throws Exception { if (!mUri.endsWith("?")) { mUri = mUri + "&"; } - mUri = mUri + "SessionId=" + sessionId; + if (mUri.indexOf("main.app") >= 0) { + mUri = mUri + "SessionId=" + sessionIdHttp; + } else { + mUri = mUri + "SessionId=" + sessionId; + } + + logger.info("Execute request: {}", uri); + CookieStore c = httpClient.getCookieStore(); + java.net.HttpCookie cookie = new HttpCookie("SessionId", sessionIdHttp); + cookie.setPath("/"); + cookie.setVersion(0); + + c.add(new URI(baseUri), cookie); - logger.debug("Execute request: {}", uri); + logger.debug("Execute request: {}", uri); final Request request = httpClient.newRequest(baseUri + mUri); request.method(HttpMethod.GET); From a1fa41aad48db7c4a1f1ec1b2d91ba6fd3ef4547 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 22 Jan 2023 18:52:14 +0100 Subject: [PATCH 022/214] spotless:aply Signed-off-by: Laurent ARNAL --- .../internal/Metadata/SiemensHvacMetadataRegistryImpl.java | 4 ++-- .../internal/network/SiemensHvacConnectorImpl.java | 2 +- .../src/main/resources/OH-INF/addon/addon.xml | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index 32db463b079dc..ee51f8d576084 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -383,8 +383,8 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { if (channelType != null) { ChannelDefinition channelDef = new ChannelDefinitionBuilder(id, channelType.getUID()).withLabel(dataPoint.getShortDesc()) - .withDescription(dataPoint.getLongDesc()).withProperties(props) - .build(); + .withDescription(dataPoint.getLongDesc()) + .withProperties(props).build(); channelDefinitions.add(channelDef); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 1a41b07e86f82..72dc1bd7c18b9 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -360,7 +360,7 @@ private void _doAuth(boolean http) throws Exception { c.add(new URI(baseUri), cookie); - logger.debug("Execute request: {}", uri); + logger.debug("Execute request: {}", uri); final Request request = httpClient.newRequest(baseUri + mUri); request.method(HttpMethod.GET); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml index 1ccbd7cd67cf8..2f3d7b9fcf53f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml @@ -1,9 +1,8 @@ + xmlns:addon="https://openhab.org/schemas/addon/v1.0.0" xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0"> - binding + binding SiemensHvac Binding This is the binding for SiemensHvac. From f6e3900cc553723eea743e824e0dde13b71c8857 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 22 Jan 2023 18:53:23 +0100 Subject: [PATCH 023/214] fix logger Signed-off-by: Laurent ARNAL --- .../siemenshvac/internal/handler/SiemensHvacHandlerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index 76c059a3dd2a4..7a2280fdc4347 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -239,7 +239,7 @@ private void ReadDp(String dp, String uid, String type, boolean async) { logger.debug("Start read : {}", dp); String request = "api/menutree/read_datapoint.json?Id=" + dp; - logger.debug("siemensHvac:ReadDp:DoRequest():" + request); + logger.debug("siemensHvac:ReadDp:DoRequest(): {}", request); if (async) { if (lcHvacConnector != null) { From d640c2547518a9daf51b19f7411d09ffa517c348 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 22 Jan 2023 20:39:01 +0100 Subject: [PATCH 024/214] add siemenshvac to parent pom Signed-off-by: Laurent ARNAL --- bundles/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/bundles/pom.xml b/bundles/pom.xml index 0faf1fb3161a5..c485b6d0fa39a 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -350,6 +350,7 @@ org.openhab.binding.shelly org.openhab.binding.silvercrestwifisocket org.openhab.binding.siemensrds + org.openhab.binding.siemenshvac org.openhab.binding.sinope org.openhab.binding.sleepiq org.openhab.binding.smaenergymeter From 2aa43135760dc57112623dfadb6ba22598d0d4f4 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 24 Jan 2023 18:04:35 +0100 Subject: [PATCH 025/214] first pass correction on pull request comments Signed-off-by: Laurent ARNAL --- .../SiemensHvacMetadataDataPoint.java | 20 +++++++++---------- ...emensHvacChannelGroupTypeProviderImpl.java | 7 +++---- .../SiemensHvacChannelTypeProviderImpl.java | 3 +-- .../SiemensHvacThingTypeProviderImpl.java | 3 +-- .../main/resources/OH-INF/binding/binding.xml | 9 --------- 5 files changed, 15 insertions(+), 27 deletions(-) delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/binding/binding.xml diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java index 57040186d4b48..8f5d50e42fa4e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java @@ -36,9 +36,9 @@ public class SiemensHvacMetadataDataPoint extends SiemensHvacMetadata { private @Nullable String resolution = null; private @Nullable String fieldWitdh = null; private @Nullable String decimalDigits = null; - private Boolean detailsResolved = false; + private boolean detailsResolved = false; @SuppressWarnings("unused") - private Boolean unresolvableDetails = false; + private boolean unresolvableDetails = false; private @Nullable String dialogType = null; private @Nullable String maxLength = null; private @Nullable String address = null; @@ -192,7 +192,7 @@ public void resolveDptDetails(JsonObject result) { if (desc != null) { this.dptType = desc.get("Type").getAsString(); - if (dptType.equals(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { + if (SiemensHvacBindingConstants.DPT_TYPE_ENUM.equals(dptType)) { JsonArray enums = desc.getAsJsonArray("Enums"); @@ -205,17 +205,17 @@ public void resolveDptDetails(JsonObject result) { ch.setIsActive(entry.get("IsCurrentValue").getAsString()); child.add(ch); } - } else if (dptType.equals(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { + } else if (SiemensHvacBindingConstants.DPT_TYPE_NUMERIC.equals(dptType)) { this.dptUnit = desc.get("Unit").getAsString(); this.min = desc.get("Min").getAsString(); this.max = desc.get("Max").getAsString(); this.resolution = desc.get("Resolution").getAsString(); this.fieldWitdh = desc.get("FieldWitdh").getAsString(); this.decimalDigits = desc.get("DecimalDigits").getAsString(); - } else if (dptType.equals(SiemensHvacBindingConstants.DPT_TYPE_STRING)) { + } else if (SiemensHvacBindingConstants.DPT_TYPE_STRING.equals(dptType)) { this.dialogType = desc.get("DialogType").getAsString(); this.maxLength = desc.get("MaxLength").getAsString(); - } else if (dptType.equals(SiemensHvacBindingConstants.DPT_TYPE_RADIO)) { + } else if (SiemensHvacBindingConstants.DPT_TYPE_RADIO.equals(dptType)) { JsonArray buttons = desc.getAsJsonArray("Buttons"); child = new ArrayList(); @@ -229,10 +229,10 @@ public void resolveDptDetails(JsonObject result) { ch.setIsActive(button.get("IsActive").getAsString()); child.add(ch); } - } else if (dptType.equals(SiemensHvacBindingConstants.DPT_TYPE_DATE)) { - } else if (dptType.equals(SiemensHvacBindingConstants.DPT_TYPE_TIME)) { - } else if (dptType.equals(SiemensHvacBindingConstants.DPT_TYPE_SCHEDULER)) { - } else if (dptType.equals(SiemensHvacBindingConstants.DPT_TYPE_CALENDAR)) { + } else if (SiemensHvacBindingConstants.DPT_TYPE_DATE.equals(dptType)) { + } else if (SiemensHvacBindingConstants.DPT_TYPE_TIME.equals(dptType)) { + } else if (SiemensHvacBindingConstants.DPT_TYPE_SCHEDULER.equals(dptType)) { + } else if (SiemensHvacBindingConstants.DPT_TYPE_CALENDAR.equals(dptType)) { } else { } detailsResolved = true; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java index 7730c681848ed..451d566258b71 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java @@ -39,8 +39,7 @@ public class SiemensHvacChannelGroupTypeProviderImpl implements SiemensHvacChann // @Override - @Nullable - public ChannelGroupType getInternalChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID) { + public @Nullable ChannelGroupType getInternalChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID) { return channelGroupTypesByUID.get(channelGroupTypeUID); } @@ -49,9 +48,9 @@ public void addChannelGroupType(ChannelGroupType channelGroupType) { channelGroupTypesByUID.put(channelGroupType.getUID(), channelGroupType); } - @Nullable @Override - public ChannelGroupType getChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID, @Nullable Locale locale) { + public @Nullable ChannelGroupType getChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID, + @Nullable Locale locale) { return channelGroupTypesByUID.get(channelGroupTypeUID); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java index 99339142bcdc6..448b8c05faa08 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java @@ -59,9 +59,8 @@ public Collection getChannelTypes(@Nullable Locale locale) { /** * @see ChannelTypeRegistr#getChannelType(ChannelTypeUID, Locale) */ - @Nullable @Override - public ChannelType getChannelType(ChannelTypeUID channelTypeUID, @Nullable Locale locale) { + public @Nullable ChannelType getChannelType(ChannelTypeUID channelTypeUID, @Nullable Locale locale) { return channelTypesByUID.get(channelTypeUID); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProviderImpl.java index 144443c696e05..dc3fc5352cbe0 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProviderImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProviderImpl.java @@ -43,9 +43,8 @@ public void addThingType(ThingType thingType) { thingTypesByUID.put(thingType.getUID(), thingType); } - @Nullable @Override - public ThingType getInternalThingType(ThingTypeUID thingTypeUID) { + public @Nullable ThingType getInternalThingType(ThingTypeUID thingTypeUID) { return thingTypesByUID.get(thingTypeUID); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/binding/binding.xml deleted file mode 100644 index 1c6b91c584fd4..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/binding/binding.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - SiemensHvac Binding - This is the binding for SiemensHvac. - - From 02b9c52ab9b1e1177e5f59795556c641f1d6cfcf Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 24 Jan 2023 18:27:54 +0100 Subject: [PATCH 026/214] fix character conversions Signed-off-by: Laurent ARNAL --- .../network/SiemensHvacRequestListener.java | 6 --- .../siemenshvac/internal/type/UidUtils.java | 54 +++++-------------- 2 files changed, 13 insertions(+), 47 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java index 76af15d6398a5..2ce7e3585a780 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java @@ -72,16 +72,10 @@ public void onFailure(@Nullable Response response, @Nullable Throwable failure) @Override public void onQueued(@Nullable Request request) { - if (request == null) { - return; - } } @Override public void onBegin(@Nullable Request request) { - if (request == null) { - return; - } } @Override diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index 17ef432ce4cb6..9610dce996e88 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -40,63 +40,35 @@ public static String sanetizeId(String st) { char c = st.charAt(i); if (c == 130) { - } else if (c == 131) { + } else if (c >= 232 && c <= 234) { c = 'e'; - } else if (c == 136) { - c = 'e'; - } else if (c == 137) { - c = 'e'; - } else if (c == 144) { - c = 'E'; - } else if (c == 212) { + } else if (c >= 200 && c <= 202) { c = 'E'; } - else if (c == 140) { - c = 'i'; - } else if (c == 139) { - c = 'i'; - } else if (c == 161) { - c = 'i'; - } else if (c == 141) { + else if (c >= 236 && c <= 239) { c = 'i'; + } else if (c >= 204 && c <= 207) { + c = 'I'; } - else if (c == 147) { - c = 'o'; - } else if (c == 162) { - c = 'o'; - } else if (c == 149) { - c = 'o'; - } else if (c == 148) { + else if (c >= 242 && c <= 246) { c = 'o'; - } - - else if (c == 163) { - c = 'u'; - } else if (c == 151) { - c = 'u'; - } else if (c == 150) { + } else if (c >= 249 && c <= 252) { c = 'u'; - } else if (c == 129) { - c = 'u'; - } else if (c == 233) { + } else if (c >= 217 && c <= 220) { c = 'U'; } - else if (c == 133) { - c = 'a'; - } else if (c == 132) { - c = 'a'; - } else if (c == 131) { - c = 'a'; - } else if (c == 160) { + else if (c >= 224 && c <= 229) { c = 'a'; + } else if (c == 192 && c <= 197) { + c = 'A'; } - else if (c == 135) { + else if (c == 199) { c = 'c'; - } else if (c == 128) { + } else if (c == 231) { c = 'C'; } From c54e2a968cafb9286526efbefcf8152e441f733a Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 24 Jan 2023 19:02:22 +0100 Subject: [PATCH 027/214] add Readme files Signed-off-by: Laurent ARNAL --- .../org.openhab.binding.siemenshvac/README.md | 80 ++++++++++-------- .../doc/Albatros.jpg | Bin 0 -> 46069 bytes 2 files changed, 47 insertions(+), 33 deletions(-) create mode 100644 bundles/org.openhab.binding.siemenshvac/doc/Albatros.jpg diff --git a/bundles/org.openhab.binding.siemenshvac/README.md b/bundles/org.openhab.binding.siemenshvac/README.md index b6df0459eef3a..eb1cc8daa25ed 100644 --- a/bundles/org.openhab.binding.siemenshvac/README.md +++ b/bundles/org.openhab.binding.siemenshvac/README.md @@ -1,56 +1,70 @@ # SiemensHvac Binding -_Give some details about what this binding is meant for - a protocol, system, specific device._ +This binding is to support Siemens Hvac controller ecosystem, and the Web Gateway interface OZW672. +A typical system is composed of: -_If possible, provide some resources like pictures, a YouTube 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._ + +<=== Ethernet ===> | OZW672 | <====== ¨BSB/LPB BUS ======> | Hvac Controler (RVS41.813/327) | ====== | Internal device in your system : sensors, boiler, external pac unit, ... | -## Supported Things +There's a lot of different Hvac controler depending on model in lot of different PAC constructor. +Mine is a Atlantic Hybrid duo whith a Siemens RVS41.813/327 inside. -_Please describe the different supported things / devices 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._ +Siemens have a complete set of controler reference under the name "Siemens Albatros". +Here some picture of such device. +You can also find this device in other type of heating system : boiler or solar based. -## Discovery +![](doc/Albatros.jpg) -_Describe the available auto-discovery features here. Mention for what it works and what needs to be kept in mind when using it._ +You will find some information about the OZW672.01 gateway on siemens web site : +[OZW 672 Page] +([https://hit.sbt.siemens.com/RWD/app.aspx?rc=FR&lang=fr&module=Catalog&action=ShowProduct&key=BPZ:OZW672.01) + +With this binding, you will be able : +- To consult the different parameters of your system like temperature, current heating mode, water temperature, and many more. +- Modify the functionning mode of your device : temperature set point, heating mode, and others. -## 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 Philips Hue Binding -# -# Default secret key for the pairing of the Philips Hue Bridge. -# It has to be between 10-40 (alphanumeric) characters -# This may be changed by the user for security reasons. -secret=openHABSecret -``` +## Supported Things -_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._ +Support many different things as the thing type is handle by autodiscovery. -_If your binding does not offer any generic configurations, you can remove this section completely._ +Mainly, it will first discover the gateway. +Currently test and support is the OZW672.x series. +No test done with OZW772.x series, but it should work as well. -## Thing Configuration +After, it will discover thing inside your PAC, mainly main controller of type RVS... +Only test in real condition with RVS41.813/327 but should work with all other type as the access interface is standard. -_Describe what is needed to manually configure a thing, either through the (Paper) UI or via a thing-file. This should be mainly about its mandatory and optional configuration parameters. A short example entry for a thing file can help!_ -_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._ +## Discovery -## Channels +Discovery of Gateway can be done using Upnp. +Just switch off/on your gateway to make it annonce itself on the network. +The gateway should appears in the Inbox a few minutes after. +Be aware what you will have to modifity the password in Gateway parameters just after the discovering to make it work properly. +Be also aware that first initialization is a little long because we need to read all the metadata from the device. -_Here you should provide information about available channel types, what their meaning is and how they can be used._ +Discovery of Hvac device have to be done through the Scan button inside the binding. +Go to the thing page, click on the "+" button, select the siemensHvac binding, and then click Scan. +Your device should appears on the page after a few seconds. -_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 | description | -|----------|--------|------------------------------| -| control | Switch | This is the control channel | +## Binding Configuration + +There is no particular configuration to be done. +The only revelant parameters is on the OZW672 thing for the user and password to use to connect to the gateway. +IP should have be discovered automatically via UPNP. + + +## Thing Configuration + -## Full Example -_Provide a full usage example based on textual configuration files (*.things, *.items, *.sitemap)._ +## Channels + +Channels are autodiscovered, you will find them on the RVS things. +They are organized the same way as the LCD screen of your PAC device, by top level menu functionnality, and sub-functionnalities. +Each channel are strongly typed, so for exemple, for heating mode, openhab will provide you with a list of choice supported by the device. -## Any custom content here! -_Feel free to add additional sections for whatever you think should also be mentioned about your binding!_ diff --git a/bundles/org.openhab.binding.siemenshvac/doc/Albatros.jpg b/bundles/org.openhab.binding.siemenshvac/doc/Albatros.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4fc744432f7fec58d1635b87c111baeac6299722 GIT binary patch literal 46069 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ì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 DG7R17 literal 0 HcmV?d00001 From bf9e6469c40e79c4fff281793b1b1663e1e8deb9 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 24 Jan 2023 19:02:30 +0100 Subject: [PATCH 028/214] fix List.of() Signed-off-by: Laurent ARNAL --- .../internal/Metadata/SiemensHvacMetadataDataPoint.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java index 8f5d50e42fa4e..098bcf65326d9 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java @@ -15,6 +15,8 @@ import java.util.ArrayList; import java.util.List; +import javax.validation.constraints.NotNull; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; @@ -44,7 +46,9 @@ public class SiemensHvacMetadataDataPoint extends SiemensHvacMetadata { private @Nullable String address = null; private int dptSubKey = -1; private boolean writeAccess = false; - private List child; + + @NotNull + private List child = List.of(); public SiemensHvacMetadataDataPoint() { child = new ArrayList(); From a453a515d7d1984ec0a1601a30e289c94a1253a8 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 24 Jan 2023 19:17:17 +0100 Subject: [PATCH 029/214] fix other comments Signed-off-by: Laurent ARNAL --- .../SiemenesHvacDiscoveryParticipant.java | 3 +-- .../factory/SiemensHvacHandlerFactory.java | 14 +++----------- .../SiemensHvacBridgeBaseThingHandler.java | 11 ----------- .../SiemensHvacOZW672BridgeThingHandler.java | 16 ---------------- .../network/SiemensHvacConnectorImpl.java | 4 ---- 5 files changed, 4 insertions(+), 44 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java index d8adf67bf3b19..9cefb70cf9ad6 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java @@ -14,7 +14,6 @@ import static org.openhab.core.thing.Thing.PROPERTY_SERIAL_NUMBER; -import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -54,7 +53,7 @@ public void modified(@Nullable Map configProperties) { @Override public Set getSupportedThingTypeUIDs() { - return Collections.singleton(SiemensHvacBindingConstants.THING_TYPE_BRIDGE); + return Set.of(SiemensHvacBindingConstants.THING_TYPE_BRIDGE); } @Override diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java index bcaf48b81b00f..2f0d8e194a85f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java @@ -31,8 +31,6 @@ import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * The {@link SiemensHvacHandlerFactory} is responsible for creating things and thing @@ -44,9 +42,6 @@ @Component(service = ThingHandlerFactory.class, configurationPid = "binding.siemenshvac") public class SiemensHvacHandlerFactory extends BaseThingHandlerFactory { - @SuppressWarnings("unused") - private final Logger logger = LoggerFactory.getLogger(SiemensHvacHandlerFactory.class); - private @Nullable NetworkAddressService networkAddressService; private @Nullable HttpClientFactory httpClientFactory; private SiemensHvacMetadataRegistry metaDataRegistry; @@ -55,9 +50,11 @@ public class SiemensHvacHandlerFactory extends BaseThingHandlerFactory { @Activate public SiemensHvacHandlerFactory(@Nullable final @Reference HttpClientFactory httpClientFactory, - final @Reference SiemensHvacMetadataRegistry metaDataRegistry) { + final @Reference SiemensHvacMetadataRegistry metaDataRegistry, + final @Reference NetworkAddressService networkAddressService) { this.httpClientFactory = httpClientFactory; this.metaDataRegistry = metaDataRegistry; + this.networkAddressService = networkAddressService; } @Override @@ -104,11 +101,6 @@ private ThingUID getIPBridgeThingUID(ThingTypeUID thingTypeUID, @Nullable ThingU return new ThingUID(thingTypeUID, ipAddress); } - @Reference - protected void setNetworkAddressService(NetworkAddressService networkAddressService) { - this.networkAddressService = networkAddressService; - } - protected void unsetNetworkAddressService(NetworkAddressService networkAddressService) { this.networkAddressService = null; } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java index 44f7ccc670796..b40342a087e4c 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -61,19 +61,8 @@ public SiemensHvacBridgeBaseThingHandler(Bridge bridge, @Nullable NetworkAddress @Override public void handleCommand(ChannelUID channelUID, Command command) { - // Nothing to do here } - /* - * public ScheduledExecutorService getScheduler() { - * return knxScheduler; - * } - * - * public ScheduledExecutorService getBackgroundScheduler() { - * return backgroundScheduler; - * } - */ - @Override public void initialize() { metaDataRegistry.ReadMeta(); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java index a4e00866c2687..85c794ecdfb22 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java @@ -59,23 +59,7 @@ public void initialize() { @Override public void dispose() { super.dispose(); - /* - * if (client != null) { - * client.dispose(); - * client = null; - * } - */ } - /* - * @Override - * protected KNXClient getClient() { - * KNXClient ret = client; - * if (ret == null) { - * return new NoOpClient(); - * } - * return ret; - * } - */ @Override public Collection> getServices() { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 72dc1bd7c18b9..8764840c96fd1 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -15,7 +15,6 @@ import java.net.CookieStore; import java.net.HttpCookie; import java.net.URI; -import java.util.Date; import java.util.Hashtable; import java.util.List; import java.util.Map; @@ -69,8 +68,6 @@ public class SiemensHvacConnectorImpl implements SiemensHvacConnector { private String baseUrl = ""; private String userName = ""; private String userPassword = ""; - @SuppressWarnings("unused") - private @Nullable Date lastUpdate; protected final HttpClientFactory httpClientFactory; @@ -479,7 +476,6 @@ public static Gson getGsonWithAdapter() { public void AddDpUpdate(String itemName, Type dp) { synchronized (updateCommand) { updateCommand.put(itemName, dp); - lastUpdate = new java.util.Date(); } } } From ca9ec4f8c123a3dad6b4d8e7dfebf189ecb9246e Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 24 Jan 2023 19:20:17 +0100 Subject: [PATCH 030/214] fix a typo on README.md Signed-off-by: Laurent ARNAL --- bundles/org.openhab.binding.siemenshvac/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/bundles/org.openhab.binding.siemenshvac/README.md b/bundles/org.openhab.binding.siemenshvac/README.md index eb1cc8daa25ed..0b831f7b50928 100644 --- a/bundles/org.openhab.binding.siemenshvac/README.md +++ b/bundles/org.openhab.binding.siemenshvac/README.md @@ -20,6 +20,7 @@ You will find some information about the OZW672.01 gateway on siemens web site : ([https://hit.sbt.siemens.com/RWD/app.aspx?rc=FR&lang=fr&module=Catalog&action=ShowProduct&key=BPZ:OZW672.01) With this binding, you will be able : + - To consult the different parameters of your system like temperature, current heating mode, water temperature, and many more. - Modify the functionning mode of your device : temperature set point, heating mode, and others. From 43c8267482322eb44ab3d5f147fcaf7c9fd666a4 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 24 Jan 2023 19:29:07 +0100 Subject: [PATCH 031/214] try to fix pull request build errors ! Signed-off-by: Laurent ARNAL --- bom/openhab-addons/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index 232adfed1394f..4d8cdb95a40ae 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -1566,6 +1566,11 @@ org.openhab.binding.shelly ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.siemenshvac + ${project.version} + org.openhab.addons.bundles org.openhab.binding.siemensrds From af23bad270c6166065dfd363d32dca99f88fae11 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 24 Jan 2023 19:38:29 +0100 Subject: [PATCH 032/214] fix indentations Signed-off-by: Laurent ARNAL --- bundles/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/pom.xml b/bundles/pom.xml index c485b6d0fa39a..852fa9ee41016 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -350,7 +350,7 @@ org.openhab.binding.shelly org.openhab.binding.silvercrestwifisocket org.openhab.binding.siemensrds - org.openhab.binding.siemenshvac + org.openhab.binding.siemenshvac org.openhab.binding.sinope org.openhab.binding.sleepiq org.openhab.binding.smaenergymeter From 8d5692bce890441c42cd589037c84a80c33e80b5 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sat, 4 Feb 2023 14:38:28 +0100 Subject: [PATCH 033/214] fix internationalization files Signed-off-by: Laurent ARNAL --- .../OH-INF/i18n/siemenshvac_xx_XX.properties | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_xx_XX.properties diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_xx_XX.properties b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_xx_XX.properties deleted file mode 100644 index 9f2240460a27d..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_xx_XX.properties +++ /dev/null @@ -1,17 +0,0 @@ -# FIXME: please substitute the xx_XX with a proper locale, ie. de_DE -# FIXME: please do not add the file to the repo if you add or change no content -# binding -binding.siemenshvac.name = -binding.siemenshvac.description = - -# thing types -thing-type.siemenshvac.sample.label = -thing-type.siemenshvac.sample.description = - -# thing type config description -thing-type.config.siemenshvac.sample.config1.label = -thing-type.config.siemenshvac.sample.config1.description = - -# channel types -channel-type.siemenshvac.sample-channel.label = -channel-type.siemenshvac.sample-channel.description = From b1ab1b3460cf57c7fcda1d23520d5e09ab04f72c Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sat, 4 Feb 2023 14:47:02 +0100 Subject: [PATCH 034/214] fixes for 2nd review Signed-off-by: Laurent ARNAL --- .../SiemensHvacMetadataDataPoint.java | 8 +-- .../Metadata/SiemensHvacMetadataDevice.java | 6 +- .../SiemensHvacMetadataRegistryImpl.java | 15 ++--- .../SiemensHvacDeviceDiscoveryService.java | 11 ---- .../factory/SiemensHvacHandlerFactory.java | 26 +++------ .../SiemensHvacBridgeBaseThingHandler.java | 8 --- .../handler/SiemensHvacHandlerImpl.java | 57 ++++++------------- .../SiemensHvacOZW672BridgeThingHandler.java | 4 +- .../network/SiemensHvacConnectorImpl.java | 44 ++++++++------ ...emensHvacChannelGroupTypeProviderImpl.java | 2 - .../SiemensHvacChannelTypeProviderImpl.java | 3 +- .../OH-INF/i18n/siemenshvac_fr_FR.properties | 9 +++ 12 files changed, 74 insertions(+), 119 deletions(-) create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr_FR.properties diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java index 098bcf65326d9..f3436dd16c3d7 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java @@ -39,16 +39,13 @@ public class SiemensHvacMetadataDataPoint extends SiemensHvacMetadata { private @Nullable String fieldWitdh = null; private @Nullable String decimalDigits = null; private boolean detailsResolved = false; - @SuppressWarnings("unused") - private boolean unresolvableDetails = false; private @Nullable String dialogType = null; private @Nullable String maxLength = null; private @Nullable String address = null; private int dptSubKey = -1; private boolean writeAccess = false; - @NotNull - private List child = List.of(); + private @NotNull List child = List.of(); public SiemensHvacMetadataDataPoint() { child = new ArrayList(); @@ -62,7 +59,7 @@ public void setDptType(String dptType) { this.dptType = dptType; } - public @Nullable List getChild() { + public List getChild() { return child; } @@ -187,7 +184,6 @@ public void resolveDptDetails(JsonObject result) { if (("datatype not supported").equals(errorMsg)) { detailsResolved = true; - unresolvableDetails = true; return; } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java index a5799d1afbb29..4495872048154 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java @@ -30,11 +30,9 @@ public class SiemensHvacMetadataDevice { private String serialNr = ""; - @Nullable - private String treeDate; + private @Nullable String treeDate; - @Nullable - private String treeTime; + private @Nullable String treeTime; private boolean treeGenerated; private int treeId; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index ee51f8d576084..9b5096114d723 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -79,9 +79,6 @@ public class SiemensHvacMetadataRegistryImpl implements SiemensHvacMetadataRegis private static final Logger logger = LoggerFactory.getLogger(SiemensHvacMetadataRegistryImpl.class); - // private Map thingTypesByUID = new HashMap<>(); - // protected List homematicThingTypeExcluders = new CopyOnWriteArrayList<>(); - // A map contains data point config read from Api and/or WebPages private Map dptMap = new Hashtable(); private @Nullable SiemensHvacMetadata root = null; @@ -383,8 +380,8 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { if (channelType != null) { ChannelDefinition channelDef = new ChannelDefinitionBuilder(id, channelType.getUID()).withLabel(dataPoint.getShortDesc()) - .withDescription(dataPoint.getLongDesc()) - .withProperties(props).build(); + .withDescription(dataPoint.getLongDesc()).withProperties(props) + .build(); channelDefinitions.add(channelDef); } @@ -433,11 +430,9 @@ private ChannelType createChannelType(SiemensHvacMetadataDataPoint dpt, ChannelT List options = new ArrayList(); if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { List childs = dpt.getChild(); - if (childs != null) { - for (SiemensHvacMetadataPointChild opt : childs) { - StateOption stOpt = new StateOption(opt.getValue(), opt.getText()); - options.add(stOpt); - } + for (SiemensHvacMetadataPointChild opt : childs) { + StateOption stOpt = new StateOption(opt.getValue(), opt.getText()); + options.add(stOpt); } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java index 5ded3c1fdee94..be60e61505a87 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java @@ -75,7 +75,6 @@ protected void startBackgroundDiscovery() { @Override protected void stopBackgroundDiscovery() { - // can be overridden } private @Nullable ThingUID getThingUID(ThingTypeUID thingTypeUID, String serial) { @@ -96,8 +95,6 @@ public void startScan() { if (lcMetadataRegistry != null) { lcMetadataRegistry.ReadMeta(); - // SiemensHvacMetadataMenu rootMenu = metadataRegistry.getRoot(); - // for (SiemensHvacMetadata child : rootMenu.getChilds().values()) { ArrayList devices = lcMetadataRegistry.getDevices(); if (devices == null) { @@ -152,7 +149,6 @@ protected synchronized void stopScan() { public void setThingHandler(@Nullable ThingHandler handler) { if (handler instanceof SiemensHvacBridgeBaseThingHandler) { siemensHvacBridgeHandler = (SiemensHvacBridgeBaseThingHandler) handler; - // bridgeUID = handler.getThing().getUID(); } } @@ -171,12 +167,5 @@ public void activate() { @Override public void deactivate() { - /* - * removeOlderResults(new Date().getTime(), bridgeUID); - * final HueBridgeHandler handler = hueBridgeHandler; - * if (handler != null) { - * handler.unregisterDiscoveryListener(); - * } - */ } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java index 2f0d8e194a85f..9ff2c2b30337c 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java @@ -42,14 +42,12 @@ @Component(service = ThingHandlerFactory.class, configurationPid = "binding.siemenshvac") public class SiemensHvacHandlerFactory extends BaseThingHandlerFactory { - private @Nullable NetworkAddressService networkAddressService; - private @Nullable HttpClientFactory httpClientFactory; - private SiemensHvacMetadataRegistry metaDataRegistry; - - // + private final NetworkAddressService networkAddressService; + private final HttpClientFactory httpClientFactory; + private final SiemensHvacMetadataRegistry metaDataRegistry; @Activate - public SiemensHvacHandlerFactory(@Nullable final @Reference HttpClientFactory httpClientFactory, + public SiemensHvacHandlerFactory(final @Reference HttpClientFactory httpClientFactory, final @Reference SiemensHvacMetadataRegistry metaDataRegistry, final @Reference NetworkAddressService networkAddressService) { this.httpClientFactory = httpClientFactory; @@ -78,15 +76,12 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { @Override protected @Nullable ThingHandler createHandler(Thing thing) { if (thing.getThingTypeUID().equals(SiemensHvacBindingConstants.THING_TYPE_OZW672)) { - if (networkAddressService != null) { - return new SiemensHvacOZW672BridgeThingHandler((Bridge) thing, networkAddressService, httpClientFactory, - metaDataRegistry); - } + return new SiemensHvacOZW672BridgeThingHandler((Bridge) thing, networkAddressService, httpClientFactory, + metaDataRegistry); } else if (SiemensHvacBindingConstants.BINDING_ID.equals(thing.getThingTypeUID().getBindingId())) { - SiemensHvacHandlerImpl handler = new SiemensHvacHandlerImpl(thing); - handler.setChannelTypeProvider(metaDataRegistry.getChannelTypeProvider()); - handler.setSiemensHvacConnector(metaDataRegistry.getSiemensHvacConnector()); - handler.setSiemensHvacMetadataRegistry(metaDataRegistry); + SiemensHvacHandlerImpl handler = new SiemensHvacHandlerImpl(thing, + metaDataRegistry.getSiemensHvacConnector(), metaDataRegistry, + metaDataRegistry.getChannelTypeProvider()); return handler; } return null; @@ -101,7 +96,4 @@ private ThingUID getIPBridgeThingUID(ThingTypeUID thingTypeUID, @Nullable ThingU return new ThingUID(thingTypeUID, ipAddress); } - protected void unsetNetworkAddressService(NetworkAddressService networkAddressService) { - this.networkAddressService = null; - } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java index b40342a087e4c..42fcb57c0cbb4 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -35,13 +35,7 @@ @NonNullByDefault public abstract class SiemensHvacBridgeBaseThingHandler extends BaseBridgeHandler { - // protected ConcurrentHashMap destinations = new ConcurrentHashMap<>(); - // private final ScheduledExecutorService knxScheduler = ThreadPoolManager.getScheduledPool("knx"); - // private final ScheduledExecutorService backgroundScheduler = Executors.newSingleThreadScheduledExecutor(); - private @Nullable SiemensHvacDeviceDiscoveryService discoveryService; - @SuppressWarnings("unused") - private final @Nullable NetworkAddressService networkAddressService; private final @Nullable HttpClientFactory httpClientFactory; private final SiemensHvacMetadataRegistry metaDataRegistry; @@ -49,7 +43,6 @@ public SiemensHvacBridgeBaseThingHandler(Bridge bridge, @Nullable NetworkAddress @Nullable HttpClientFactory httpClientFactory, SiemensHvacMetadataRegistry metaDataRegistry) { super(bridge); SiemensHvacConnector lcConnector = null; - this.networkAddressService = networkAddressService; this.httpClientFactory = httpClientFactory; this.metaDataRegistry = metaDataRegistry; @@ -83,7 +76,6 @@ public boolean registerDiscoveryListener(SiemensHvacDeviceDiscoveryService liste if (lcDiscoveryService == null) { lcDiscoveryService = listener; lcDiscoveryService.setSiemensHvacMetadataRegistry(metaDataRegistry); - // getFullLights().forEach(listener::addLightDiscovery); return true; } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index 7a2280fdc4347..b69356b46f1f7 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -43,7 +43,6 @@ import org.openhab.core.types.RefreshType; import org.openhab.core.types.State; import org.openhab.core.types.Type; -import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -65,43 +64,22 @@ public class SiemensHvacHandlerImpl extends BaseThingHandler implements SiemensH private @Nullable ScheduledFuture pollingJob = null; - private @Nullable SiemensHvacChannelTypeProvider channelTypeProvider; - private @Nullable SiemensHvacConnector hvacConnector; - private @Nullable SiemensHvacMetadataRegistry metaDataRegistry; + private final @Nullable SiemensHvacChannelTypeProvider channelTypeProvider; + private final @Nullable SiemensHvacConnector hvacConnector; + private final @Nullable SiemensHvacMetadataRegistry metaDataRegistry; - public SiemensHvacHandlerImpl(Thing thing) { + public SiemensHvacHandlerImpl(Thing thing, @Nullable SiemensHvacConnector hvacConnector, + @Nullable SiemensHvacMetadataRegistry metaDataRegistry, + @Nullable SiemensHvacChannelTypeProvider channelTypeProvider) { super(thing); - logger.debug("==========================================================="); - logger.debug("Siemens HVac"); - logger.debug("==========================================================="); - } - - @Reference - public void setSiemensHvacConnector(@Nullable SiemensHvacConnector hvacConnector) { + this.channelTypeProvider = channelTypeProvider; this.hvacConnector = hvacConnector; - } - - public void unsetSiemensHvacConnector(SiemensHvacConnector hvacConnector) { - this.hvacConnector = null; - } - - @Reference - public void setSiemensHvacMetadataRegistry(@Nullable SiemensHvacMetadataRegistry metaDataRegistry) { this.metaDataRegistry = metaDataRegistry; - } - - public void unsetSiemensHvacMetadataRegistry(SiemensHvacMetadataRegistry metaDataRegistry) { - this.metaDataRegistry = null; - } - @Reference - public void setChannelTypeProvider(@Nullable SiemensHvacChannelTypeProvider channelTypeProvider) { - this.channelTypeProvider = channelTypeProvider; - } - - public void unsetChannelTypeProvider(@Nullable SiemensHvacChannelTypeProvider channelTypeProvider) { - this.channelTypeProvider = null; + logger.debug("==========================================================="); + logger.debug("Siemens HVac"); + logger.debug("==========================================================="); } @Override @@ -204,15 +182,15 @@ public void DecodeReadDp(@Nullable JsonObject response, @Nullable String uid, @N if (("Numeric").equals(typer)) { updateState(updateKey, new DecimalType(value.getAsDouble())); - } else if (("Enumeration").equals(typer)) { + } else if ("Enumeration".equals(typer)) { if (enumValue != null) { updateState(updateKey, new DecimalType(enumValue.getAsInt())); } - } else if (("Text").equals(typer)) { + } else if ("Text".equals(typer)) { updateState(updateKey, new StringType(value.getAsString())); - } else if (("RadioButton").equals(typer)) { + } else if ("RadioButton".equals(typer)) { updateState(updateKey, new StringType(value.getAsString())); - } else if (("DayOfTime").equals(typer) || ("DateTime").equals(typer)) { + } else if ("DayOfTime".equals(typer) || "DateTime".equals(typer)) { try { SimpleDateFormat dtf = new SimpleDateFormat("EEEE, d. MMMM yyyy hh:mm"); // first example ZonedDateTime zdt = dtf.parse(value.getAsString()).toInstant().atZone(ZoneId.systemDefault()); @@ -230,7 +208,7 @@ public void DecodeReadDp(@Nullable JsonObject response, @Nullable String uid, @N private void ReadDp(String dp, String uid, String type, boolean async) { SiemensHvacConnector lcHvacConnector = hvacConnector; - if (("-1").equals(dp)) { + if ("-1".equals(dp)) { return; } @@ -271,7 +249,7 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { private void WriteDp(String dp, Type dpVal, String type) { SiemensHvacConnector lcHvacConnector = hvacConnector; - if (("-1").equals(dp)) { + if ("-1".equals(dp)) { return; } @@ -299,7 +277,8 @@ private void WriteDp(String dp, Type dpVal, String type) { } } - String request = "api/menutree/write_datapoint.json?Id=" + dp + "&Value=" + valUpdate + "&Type=" + type; + String request = String.format("api/menutree/write_datapoint.json?Id=%s&Value=%s&Type=%s", dp, valUpdate, + type); if (lcHvacConnector != null) { logger.debug("Write request for : {} ", valUpdate); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java index 85c794ecdfb22..f5e5154225a8a 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java @@ -13,7 +13,7 @@ package org.openhab.binding.siemenshvac.internal.handler; import java.util.Collection; -import java.util.Collections; +import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -63,6 +63,6 @@ public void dispose() { @Override public Collection> getServices() { - return Collections.singleton(SiemensHvacDeviceDiscoveryService.class); + return Set.of(SiemensHvacDeviceDiscoveryService.class); } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 8764840c96fd1..c654d700425d4 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -24,6 +24,8 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import javax.validation.constraints.NotNull; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -63,6 +65,9 @@ public class SiemensHvacConnectorImpl implements SiemensHvacConnector { private static final Logger logger = LoggerFactory.getLogger(SiemensHvacConnectorImpl.class); + private final static @NotNull Gson gson; + private final static @NotNull Gson gsonWithAdpter; + private @Nullable String sessionId = null; private @Nullable String sessionIdHttp = null; private String baseUrl = ""; @@ -81,6 +86,19 @@ public class SiemensHvacConnectorImpl implements SiemensHvacConnector { private @Nullable SiemensHvacBridgeBaseThingHandler hvacBridgeBaseThingHandler; + static { + GsonBuilder builder = new GsonBuilder(); + gson = builder.setPrettyPrinting().create(); + + RuntimeTypeAdapterFactory adapter = RuntimeTypeAdapterFactory + .of(SiemensHvacMetadata.class); + adapter.registerSubtype(SiemensHvacMetadataMenu.class); + adapter.registerSubtype(SiemensHvacMetadataDataPoint.class); + + gsonWithAdpter = new GsonBuilder().setPrettyPrinting().registerTypeAdapterFactory(adapter).create(); + + } + @Activate public SiemensHvacConnectorImpl(@Reference HttpClientFactory httpClientFactory) { this.updateCommand = new Hashtable(); @@ -205,7 +223,7 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacCallback cb) return response; } - private void _initConfig() throws Exception { + private void initConfig() throws Exception { Configuration config = null; SiemensHvacBridgeBaseThingHandler lcHvacBridgeBaseThingHandler = hvacBridgeBaseThingHandler; @@ -230,17 +248,17 @@ private void _initConfig() throws Exception { } } - private void _doAuth(boolean http) throws Exception { + private void doAuth(boolean http) throws Exception { logger.debug("siemensHvac:doAuth()"); - _initConfig(); + initConfig(); String baseUri = baseUrl; String uri = ""; if (http) { uri = "main.app"; } else { - uri = "api/auth/login.json?user=" + userName + "&pwd=" + userPassword; + uri = String.format("api/auth/login.json?user=%s&pwd=%s", userName, userPassword); } final Request request = httpClient.newRequest(baseUri + uri); @@ -311,9 +329,7 @@ private void _doAuth(boolean http) throws Exception { logger.debug("siemensHvac:doAuth:connect()"); - } catch ( - - Exception ex) { + } catch (Exception ex) { logger.debug("siemensHvac:doAuth:error() {}", ex.getLocalizedMessage()); } finally { } @@ -329,11 +345,11 @@ private void _doAuth(boolean http) throws Exception { public @Nullable String DoBasicRequest(String uri, @Nullable SiemensHvacCallback callback) throws Exception { if (sessionIdHttp == null) { - _doAuth(true); + doAuth(true); } if (sessionId == null) { - _doAuth(false); + doAuth(false); } try { @@ -458,19 +474,11 @@ public void WaitNoNewRequest() { } public static Gson getGson() { - GsonBuilder builder = new GsonBuilder(); - Gson gson = builder.setPrettyPrinting().create(); return gson; } public static Gson getGsonWithAdapter() { - RuntimeTypeAdapterFactory adapter = RuntimeTypeAdapterFactory - .of(SiemensHvacMetadata.class); - adapter.registerSubtype(SiemensHvacMetadataMenu.class); - adapter.registerSubtype(SiemensHvacMetadataDataPoint.class); - - Gson gson = new GsonBuilder().setPrettyPrinting().registerTypeAdapterFactory(adapter).create(); - return gson; + return gsonWithAdpter; } public void AddDpUpdate(String itemName, Type dp) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java index 451d566258b71..0cd5a73323420 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java @@ -36,8 +36,6 @@ public class SiemensHvacChannelGroupTypeProviderImpl implements SiemensHvacChann private final Map channelGroupTypesByUID = new HashMap<>(); - // - @Override public @Nullable ChannelGroupType getInternalChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID) { return channelGroupTypesByUID.get(channelGroupTypeUID); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java index 448b8c05faa08..62006de0cb240 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java @@ -64,9 +64,8 @@ public Collection getChannelTypes(@Nullable Locale locale) { return channelTypesByUID.get(channelTypeUID); } - @Nullable @Override - public ChannelType getInternalChannelType(@Nullable ChannelTypeUID channelTypeUID) { + public @Nullable ChannelType getInternalChannelType(@Nullable ChannelTypeUID channelTypeUID) { return channelTypesByUID.get(channelTypeUID); } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr_FR.properties b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr_FR.properties new file mode 100644 index 0000000000000..bda3881e71c95 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr_FR.properties @@ -0,0 +1,9 @@ +# FIXME: please substitute the xx_XX with a proper locale, ie. de_DE +# FIXME: please do not add the file to the repo if you add or change no content +# binding +binding.siemenshvac.name = Siemens HVac +binding.siemenshvac.description = Un binding permettant d'interfacer Openhab avec une PAC équipé d'un controleur Siemens RVS et d'une interface Web OZW672. + +# thing types +thing-type.siemenshvac.ozw672.label = Paserelle OZW672 +thing-type.siemenshvac.ozw672.description = Paserelle Web OZW672 entre le bus LSB du controleur RVS et Openhab From 7ab5f1a9e2a3f100aacdd8f440557529f9031b1e Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sat, 4 Feb 2023 14:52:49 +0100 Subject: [PATCH 035/214] spotless:apply Signed-off-by: Laurent ARNAL --- .../org.openhab.binding.siemenshvac/README.md | 22 ++++++++++++++++++- .../SiemensHvacMetadataRegistryImpl.java | 4 ++-- .../factory/SiemensHvacHandlerFactory.java | 1 - 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/README.md b/bundles/org.openhab.binding.siemenshvac/README.md index 0b831f7b50928..dbbebf4204f71 100644 --- a/bundles/org.openhab.binding.siemenshvac/README.md +++ b/bundles/org.openhab.binding.siemenshvac/README.md @@ -68,4 +68,24 @@ Channels are autodiscovered, you will find them on the RVS things. They are organized the same way as the LCD screen of your PAC device, by top level menu functionnality, and sub-functionnalities. Each channel are strongly typed, so for exemple, for heating mode, openhab will provide you with a list of choice supported by the device. - +## Full Example + +Things file `.things` + +```java +Thing robonect:mower:automower "Mower" @ "Garden" [ host="192.168.2.1", pollInterval=5, user="gardener", password = "cutter"] +``` + +Items file `.items` + +```java +Sring Chaudiere_Etat_Pompe_ECS "Etat Pompe ECS [%s]" { channel = "siemenshvac:RVS41_813_327:ff00f445:84c80f3715:2237#2259_PpeChargeECS" } +Number Chaudiere_Etat_ECS "Etat ECS [%s]" { channel = "siemenshvac:RVS41_813_327:ff00f445:84c80f3715:2032#2035_Etat_ECS" } +Number Temperature_Depart_Reel "Départ réel [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:ff00f445:84c80f3715:2237#2248_ValReelleTempDep_CC1" } +Number Temperature_Depart_Consigne "Départ cons [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:ff00f445:84c80f3715:2237#2249_ConsTDepResultCC1" } +Number Heure_fct_ECS "Heure Fct Ecs" { channel = "siemenshvac:RVS41_813_327:ff00f445:84c80f3715:2237#2263_HeuresFoncPompeECS" } +Number Nb_Demarrage_ECS "Nbr dém ECS [%.1f]" { channel = "siemenshvac:RVS41_813_327:ff00f445:84c80f3715:2237#2266_ComptDemarResEl_ECS" } +Number TemperatureThermostat "Temp thermt [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:ff00f445:84c80f3715:2237#2246_TAmbAct_CC1" } +Number Temperature_Consigne_C "Chauf Cons [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:ff00f445:84c80f3715:1724#1726_ConsConfort_TA_CC1" } +Number Chauffage_Mode "Chauffage Mode [%s]" { channel="siemenshvac:RVS41_813_327:ff00f445:84c80f3715:1724#1725_Regime_CC1" } +``` diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index 9b5096114d723..fcbc0b9754d53 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -380,8 +380,8 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { if (channelType != null) { ChannelDefinition channelDef = new ChannelDefinitionBuilder(id, channelType.getUID()).withLabel(dataPoint.getShortDesc()) - .withDescription(dataPoint.getLongDesc()).withProperties(props) - .build(); + .withDescription(dataPoint.getLongDesc()) + .withProperties(props).build(); channelDefinitions.add(channelDef); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java index 9ff2c2b30337c..a8f0fbf600935 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java @@ -95,5 +95,4 @@ private ThingUID getIPBridgeThingUID(ThingTypeUID thingTypeUID, @Nullable ThingU String ipAddress = (String) configuration.get(SiemensHvacBindingConstants.IP_ADDRESS); return new ThingUID(thingTypeUID, ipAddress); } - } From 4a588c77cbfce3f8c4c19ee0f1764e6056208ecc Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sat, 4 Feb 2023 15:08:06 +0100 Subject: [PATCH 036/214] fix typo Signed-off-by: Laurent ARNAL --- .../src/main/resources/OH-INF/i18n/siemenshvac_fr_FR.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr_FR.properties b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr_FR.properties index bda3881e71c95..215658bba0834 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr_FR.properties +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr_FR.properties @@ -6,4 +6,4 @@ binding.siemenshvac.description = Un binding permettant d'interfacer Openhab ave # thing types thing-type.siemenshvac.ozw672.label = Paserelle OZW672 -thing-type.siemenshvac.ozw672.description = Paserelle Web OZW672 entre le bus LSB du controleur RVS et Openhab +thing-type.siemenshvac.ozw672.description = Passerelle Web OZW672 entre le bus LSB du controleur RVS et Openhab From a6ff40f540782e726bd8490fae853837afafcbe2 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sat, 11 Feb 2023 17:50:13 +0100 Subject: [PATCH 037/214] better type initialization : only 1 type registered for basic types Signed-off-by: Laurent ARNAL --- .../SiemensHvacMetadataRegistryImpl.java | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index fcbc0b9754d53..b61736daf326a 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -380,8 +380,8 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { if (channelType != null) { ChannelDefinition channelDef = new ChannelDefinitionBuilder(id, channelType.getUID()).withLabel(dataPoint.getShortDesc()) - .withDescription(dataPoint.getLongDesc()) - .withProperties(props).build(); + .withDescription(dataPoint.getLongDesc()).withProperties(props) + .build(); channelDefinitions.add(channelDef); } @@ -422,18 +422,30 @@ private ChannelType createChannelType(SiemensHvacMetadataDataPoint dpt, ChannelT String itemType = getItemType(dpt); String category = getCategory(dpt); - String label = dpt.getShortDesc(); - String description = dpt.getLongDesc(); + String label = itemType; + String description = ""; StateDescriptionFragmentBuilder stateFragment = StateDescriptionFragmentBuilder.create(); List options = new ArrayList(); if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { + StringBuilder descBuilder = new StringBuilder(); + descBuilder.append("Enum:"); List childs = dpt.getChild(); + int idx = 0; + for (SiemensHvacMetadataPointChild opt : childs) { StateOption stOpt = new StateOption(opt.getValue(), opt.getText()); options.add(stOpt); + if (idx > 0) { + descBuilder.append("_"); + } + + descBuilder.append(String.format("(%s:%s)", opt.getValue(), opt.getText())); + idx++; } + description = descBuilder.toString(); + label = channelTypeUID.getId(); } if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { @@ -442,6 +454,9 @@ private ChannelType createChannelType(SiemensHvacMetadataDataPoint dpt, ChannelT BigDecimal step = new BigDecimal(dpt.getResolution()); stateFragment.withMinimum(min).withMaximum(max).withStep(step).withReadOnly(false); + + description = channelTypeUID.toString(); + label = channelTypeUID.getId(); } else { stateFragment.withPattern(getStatePattern(dpt)).withReadOnly(dpt.getWriteAccess() == false); } From 7e8cf1ecac6fd517511e81fceec1c092ff5f69a1 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sat, 11 Feb 2023 17:51:13 +0100 Subject: [PATCH 038/214] use channelTypeRegistry for getting type Implement code to handle Refresh Command Signed-off-by: Laurent ARNAL --- .../factory/SiemensHvacHandlerFactory.java | 9 +- .../handler/SiemensHvacHandlerImpl.java | 91 ++++++++++--------- 2 files changed, 53 insertions(+), 47 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java index a8f0fbf600935..7d4fdd403be6b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java @@ -28,6 +28,7 @@ import org.openhab.core.thing.binding.BaseThingHandlerFactory; import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.thing.binding.ThingHandlerFactory; +import org.openhab.core.thing.type.ChannelTypeRegistry; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; @@ -45,14 +46,17 @@ public class SiemensHvacHandlerFactory extends BaseThingHandlerFactory { private final NetworkAddressService networkAddressService; private final HttpClientFactory httpClientFactory; private final SiemensHvacMetadataRegistry metaDataRegistry; + private final ChannelTypeRegistry channelTypeRegistry; @Activate public SiemensHvacHandlerFactory(final @Reference HttpClientFactory httpClientFactory, final @Reference SiemensHvacMetadataRegistry metaDataRegistry, - final @Reference NetworkAddressService networkAddressService) { + final @Reference NetworkAddressService networkAddressService, + final @Reference ChannelTypeRegistry channelTypeRegistry) { this.httpClientFactory = httpClientFactory; this.metaDataRegistry = metaDataRegistry; this.networkAddressService = networkAddressService; + this.channelTypeRegistry = channelTypeRegistry; } @Override @@ -80,8 +84,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { metaDataRegistry); } else if (SiemensHvacBindingConstants.BINDING_ID.equals(thing.getThingTypeUID().getBindingId())) { SiemensHvacHandlerImpl handler = new SiemensHvacHandlerImpl(thing, - metaDataRegistry.getSiemensHvacConnector(), metaDataRegistry, - metaDataRegistry.getChannelTypeProvider()); + metaDataRegistry.getSiemensHvacConnector(), metaDataRegistry, channelTypeRegistry); return handler; } return null; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index b69356b46f1f7..b8ceb98a3d52a 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -27,7 +27,6 @@ import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataRegistry; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacCallback; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; -import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelTypeProvider; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.PercentType; @@ -39,6 +38,7 @@ import org.openhab.core.thing.binding.BaseThingHandler; import org.openhab.core.thing.binding.ThingHandlerCallback; import org.openhab.core.thing.type.ChannelType; +import org.openhab.core.thing.type.ChannelTypeRegistry; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; import org.openhab.core.types.State; @@ -64,18 +64,17 @@ public class SiemensHvacHandlerImpl extends BaseThingHandler implements SiemensH private @Nullable ScheduledFuture pollingJob = null; - private final @Nullable SiemensHvacChannelTypeProvider channelTypeProvider; private final @Nullable SiemensHvacConnector hvacConnector; private final @Nullable SiemensHvacMetadataRegistry metaDataRegistry; + private final ChannelTypeRegistry channelTypeRegistry; public SiemensHvacHandlerImpl(Thing thing, @Nullable SiemensHvacConnector hvacConnector, - @Nullable SiemensHvacMetadataRegistry metaDataRegistry, - @Nullable SiemensHvacChannelTypeProvider channelTypeProvider) { + @Nullable SiemensHvacMetadataRegistry metaDataRegistry, ChannelTypeRegistry channelTypeRegistry) { super(thing); - this.channelTypeProvider = channelTypeProvider; this.hvacConnector = hvacConnector; this.metaDataRegistry = metaDataRegistry; + this.channelTypeRegistry = channelTypeRegistry; logger.debug("==========================================================="); logger.debug("Siemens HVac"); @@ -108,48 +107,50 @@ public void dispose() { } private void pollingCode() { - SiemensHvacChannelTypeProvider lcChannelTypeProvider = channelTypeProvider; - var chList = this.getThing().getChannels(); - for (Channel ch : chList) { - logger.debug("{}", ch.getDescription()); + for (Channel channel : chList) { + ReadChannel(channel); + } + } - ThingHandlerCallback cb = this.getCallback(); - boolean isLink = false; + private void ReadChannel(Channel channel) { + logger.debug("{}", channel.getDescription()); - if (cb != null) { - isLink = cb.isChannelLinked(ch.getUID()); - } + ThingHandlerCallback cb = this.getCallback(); + boolean isLink = false; - if (!isLink) { - continue; - } + if (cb != null) { + isLink = cb.isChannelLinked(channel.getUID()); + } - ChannelType tp = null; + if (!isLink) { + return; + } - if (lcChannelTypeProvider != null) { - tp = lcChannelTypeProvider.getInternalChannelType(ch.getChannelTypeUID()); - } + ChannelType tp = channelTypeRegistry.getChannelType(channel.getChannelTypeUID()); - if (tp == null) { - continue; - } + if (tp == null) { + return; + } - String Id = ch.getProperties().get("id"); - String uid = ch.getUID().getId(); - String type = tp.getItemType(); + String Id = channel.getProperties().get("id"); + String uid = channel.getUID().getId(); + String type = tp.getItemType(); - if (Id == null) { - logger.debug("poolingCode : Id is null {} ", ch); - continue; - } - if (type == null) { - logger.debug("poolingCode : type is null {} ", ch); - continue; - } - ReadDp(Id, uid, type, true); - logger.debug("{}", isLink); + if (Id == null) { + Id = (String) channel.getConfiguration().getProperties().get("id"); } + + if (Id == null) { + logger.debug("poolingCode : Id is null {} ", channel); + return; + } + if (type == null) { + logger.debug("poolingCode : type is null {} ", channel); + return; + } + ReadDp(Id, uid, type, true); + logger.debug("{}", isLink); } public void DecodeReadDp(@Nullable JsonObject response, @Nullable String uid, @Nullable String dp, @@ -299,21 +300,19 @@ private void WriteDp(String dp, Type dpVal, String type) { @Override public void handleCommand(ChannelUID channelUID, Command command) { SiemensHvacMetadataRegistry lcMetaDataRegistry = metaDataRegistry; - SiemensHvacChannelTypeProvider lcChannelTypeProvider = channelTypeProvider; logger.debug("handleCommand"); if (command instanceof RefreshType) { - logger.debug("handleCommandRefresh"); + var channel = this.getThing().getChannel(channelUID); + if (channel != null) { + ReadChannel(channel); + } } else { - Channel channel = getThing().getChannel(channelUID); if (channel == null) { return; } - ChannelType tp = null; - if (lcChannelTypeProvider != null) { - tp = lcChannelTypeProvider.getInternalChannelType(channel.getChannelTypeUID()); - } + ChannelType tp = channelTypeRegistry.getChannelType(channel.getChannelTypeUID()); if (tp == null) { return; @@ -324,6 +323,10 @@ public void handleCommand(ChannelUID channelUID, Command command) { String id = channel.getProperties().get("id"); SiemensHvacMetadataDataPoint md = null; + if (id == null) { + id = (String) channel.getConfiguration().getProperties().get("id"); + } + if (lcMetaDataRegistry != null) { md = (SiemensHvacMetadataDataPoint) lcMetaDataRegistry.getDptMap(id); if (md != null) { From 1fd8aa52e9157ea06127d80531b05c842d6b54d3 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sat, 11 Feb 2023 17:52:04 +0100 Subject: [PATCH 039/214] review generation of ChannelType UUID Signed-off-by: Laurent ARNAL --- .../siemenshvac/internal/type/UidUtils.java | 41 +++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index 9610dce996e88..473120c223089 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -16,6 +16,7 @@ import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataDataPoint; import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataDevice; import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataMenu; +import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataPointChild; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; @@ -123,9 +124,43 @@ public static ThingTypeUID generateThingTypeUID(String name) { */ public static ChannelTypeUID generateChannelTypeUID(SiemensHvacMetadataDataPoint dpt) { - String shortDesc = sanetizeId(dpt.getShortDesc()); - return new ChannelTypeUID(SiemensHvacBindingConstants.BINDING_ID, - String.format("%s_%s_%s", dpt.getDptType(), dpt.getId(), shortDesc)); + String type = dpt.getDptType(); + String id = ""; + + if (SiemensHvacBindingConstants.DPT_TYPE_ENUM.equals(type)) { + StringBuilder builder = new StringBuilder(); + int idx = 0; + for (SiemensHvacMetadataPointChild child : dpt.getChild()) { + + if (idx > 0) { + builder.append("_"); + } + + String opt = child.getText(); + String[] subParts = opt.split(" "); + for (String subPart : subParts) { + if (subPart.length() > 0) { + builder.append(subPart.charAt(0)); + } + } + idx++; + + } + String token = sanetizeId(builder.toString()); + + id = String.format("%s_%s", type, token); + } else if (SiemensHvacBindingConstants.DPT_TYPE_NUMERIC.equals(type)) { + id = sanetizeId(String.format("%s_%s_%s_%s_%s_%s", type, dpt.getDptUnit(), dpt.getMin(), dpt.getMax(), + dpt.getFieldWitdh(), dpt.getResolution())); + } else if (SiemensHvacBindingConstants.DPT_TYPE_STRING.equals(type)) { + id = String.format("%s_%s", type, dpt.getMaxLength()); + } else { + id = String.format("%s", dpt.getDptType()); + } + + // dpt.Display(); + + return new ChannelTypeUID(SiemensHvacBindingConstants.BINDING_ID, id); } /** From c4a71c1b96d1ae868f63f32e071af00ac28ef758 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sat, 11 Feb 2023 17:52:44 +0100 Subject: [PATCH 040/214] add binding range basic default type in case of initialization from files without discovery Signed-off-by: Laurent ARNAL --- .../main/resources/OH-INF/thing/ozw672.xml | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml index ddb89cb49140f..a3ef6d82fc9e3 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml @@ -29,5 +29,70 @@ + + + Number + + A numeric value + + + String + + A string with 16 characters + + + String + + A string with unlimited characters + + + TimeOfDay + + TimeOfDay + + + DateTime + + DateTime + + + RadioButton + + RadioButton + + + Scheduler + + Scheduler + + + Number + + Current temperature in degrees celsius + Temperature + + + + + Number + + Current setpoint temperature in degrees Celsius + Temperature + + + + + Number + + + + + + + + + + + From 7f8e63f086514acb573b106d8e044426fffc2504 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sat, 11 Feb 2023 18:00:16 +0100 Subject: [PATCH 041/214] add documentation and samples Signed-off-by: Laurent ARNAL --- .../org.openhab.binding.siemenshvac/README.md | 57 +++++++++++++++---- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/README.md b/bundles/org.openhab.binding.siemenshvac/README.md index dbbebf4204f71..63ccdcd414484 100644 --- a/bundles/org.openhab.binding.siemenshvac/README.md +++ b/bundles/org.openhab.binding.siemenshvac/README.md @@ -58,6 +58,15 @@ The only revelant parameters is on the OZW672 thing for the user and password to IP should have be discovered automatically via UPNP. +## Bridge Configuration + +| Parameter | Required | Default | Description | +|-----------------|----------|---------------|---------------------------------------------------------------------| +| ipAdress | yes | | The address of the OZW672 devices | +| userName | yes | Administrator | The userName to log into the OZW672 | +| userPass | yes | | The userPassword to log into the OZW672 | + + ## Thing Configuration @@ -68,24 +77,52 @@ Channels are autodiscovered, you will find them on the RVS things. They are organized the same way as the LCD screen of your PAC device, by top level menu functionnality, and sub-functionnalities. Each channel are strongly typed, so for exemple, for heating mode, openhab will provide you with a list of choice supported by the device. +| Channel | Description | Type | Unit | Security Access Level | ReadOnly | Advanced | +| ------------------------- | ------------------------------------------------------------------------------- | -------- | :--: | :-------------------: | :------: | :------: | +| `controlBoilerApproval` | Set Boiler Approval (`AUTO`, `OFF`, `ON`) | `String` | | 🔐 W1 | R/W | true | +| `controlProgram` | Set Program (`OFF`, `NORMAL`, `WARMWATER`, `MANUAL`[1](#f1)) | `String` | | 🔐 W1 | + +| Channel Type ID | Item Type | Description | +|------------------|--------------|----------------------------------------------------------| +| Numeric | Number | Handle basic numeric value | +| String | String | a String | +| String_16 | String | a String of length <= 16 char | +| TimeOfDay | TimeOfDay | | +| Datetime | Datetime | | +| RadioButton | RadioButton | | +| Scheduler | Scheduler | | +| Temperature | Number | Use to handle reading of temperature | +| Setpoint | Number | Handle the setting of a temperature | +| Regime | Number | Enumeration for handling mode change | + + ## Full Example Things file `.things` ```java -Thing robonect:mower:automower "Mower" @ "Garden" [ host="192.168.2.1", pollInterval=5, user="gardener", password = "cutter"] +Bridge siemenshvac:ozw672:local "Ozw672"@"Chaufferie" [ baseUrl="https://192.168.254.42/", userName="Administrator", userPassword="mypass" ] +{ + Thing RVS41_813_327 local "RVS41.813/327" @ "Chaudiere" [ ] + { + Channels: + Type Setpoint:temperature "Température" [ id="1726" ] + Type Regime:cc1 "CC1" [ id="1725" ] + + } ``` + Items file `.items` ```java -Sring Chaudiere_Etat_Pompe_ECS "Etat Pompe ECS [%s]" { channel = "siemenshvac:RVS41_813_327:ff00f445:84c80f3715:2237#2259_PpeChargeECS" } -Number Chaudiere_Etat_ECS "Etat ECS [%s]" { channel = "siemenshvac:RVS41_813_327:ff00f445:84c80f3715:2032#2035_Etat_ECS" } -Number Temperature_Depart_Reel "Départ réel [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:ff00f445:84c80f3715:2237#2248_ValReelleTempDep_CC1" } -Number Temperature_Depart_Consigne "Départ cons [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:ff00f445:84c80f3715:2237#2249_ConsTDepResultCC1" } -Number Heure_fct_ECS "Heure Fct Ecs" { channel = "siemenshvac:RVS41_813_327:ff00f445:84c80f3715:2237#2263_HeuresFoncPompeECS" } -Number Nb_Demarrage_ECS "Nbr dém ECS [%.1f]" { channel = "siemenshvac:RVS41_813_327:ff00f445:84c80f3715:2237#2266_ComptDemarResEl_ECS" } -Number TemperatureThermostat "Temp thermt [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:ff00f445:84c80f3715:2237#2246_TAmbAct_CC1" } -Number Temperature_Consigne_C "Chauf Cons [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:ff00f445:84c80f3715:1724#1726_ConsConfort_TA_CC1" } -Number Chauffage_Mode "Chauffage Mode [%s]" { channel="siemenshvac:RVS41_813_327:ff00f445:84c80f3715:1724#1725_Regime_CC1" } +Sring Chaudiere_Etat_Pompe_ECS "Etat Pompe ECS [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2259_PpeChargeECS" } +Number Chaudiere_Etat_ECS "Etat ECS [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:2032#2035_Etat_ECS" } +Number Temperature_Depart_Reel "Départ réel [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2248_ValReelleTempDep_CC1" } +Number Temperature_Depart_Consigne "Départ cons [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2249_ConsTDepResultCC1" } +Number Heure_fct_ECS "Heure Fct Ecs" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2263_HeuresFoncPompeECS" } +Number Nb_Demarrage_ECS "Nbr dém ECS [%.1f]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2266_ComptDemarResEl_ECS" } +Number TemperatureThermostat "Temp thermt [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2246_TAmbAct_CC1" } +Number Temperature_Consigne_C "Chauf Cons [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:1724#1726_ConsConfort_TA_CC1" } +Number Chauffage_Mode "Chauffage Mode [%s]" { channel="siemenshvac:RVS41_813_327:local:local:1724#1725_Regime_CC1" } ``` From d4a5353c982f78ed0e23ee8c754bbba48420a64a Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sat, 11 Feb 2023 18:10:19 +0100 Subject: [PATCH 042/214] spotless:apply Signed-off-by: Laurent ARNAL --- .../SiemensHvacMetadataRegistryImpl.java | 4 +- .../main/resources/OH-INF/thing/ozw672.xml | 130 +++++++++--------- 2 files changed, 67 insertions(+), 67 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index b61736daf326a..8865d4cae0a7e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -380,8 +380,8 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { if (channelType != null) { ChannelDefinition channelDef = new ChannelDefinitionBuilder(id, channelType.getUID()).withLabel(dataPoint.getShortDesc()) - .withDescription(dataPoint.getLongDesc()).withProperties(props) - .build(); + .withDescription(dataPoint.getLongDesc()) + .withProperties(props).build(); channelDefinitions.add(channelDef); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml index a3ef6d82fc9e3..e6915604a22ac 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml @@ -29,70 +29,70 @@ - - - Number - - A numeric value - - - String - - A string with 16 characters - - - String - - A string with unlimited characters - - - TimeOfDay - - TimeOfDay - - - DateTime - - DateTime - - - RadioButton - - RadioButton - - - Scheduler - - Scheduler - - - Number - - Current temperature in degrees celsius - Temperature - - - - - Number - - Current setpoint temperature in degrees Celsius - Temperature - - - - - Number - - - - - - - - - - - + + + Number + + A numeric value + + + String + + A string with 16 characters + + + String + + A string with unlimited characters + + + TimeOfDay + + TimeOfDay + + + DateTime + + DateTime + + + RadioButton + + RadioButton + + + Scheduler + + Scheduler + + + Number + + Current temperature in degrees celsius + Temperature + + + + + Number + + Current setpoint temperature in degrees Celsius + Temperature + + + + + Number + + + + + + + + + + + From 68fafb5114e12d6c06ada23b81006747c3b19874 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 12 Feb 2023 09:07:10 +0100 Subject: [PATCH 043/214] change log level Signed-off-by: Laurent ARNAL --- .../siemenshvac/internal/network/SiemensHvacConnectorImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index c654d700425d4..1e30cd4e2b872 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -365,7 +365,7 @@ private void doAuth(boolean http) throws Exception { mUri = mUri + "SessionId=" + sessionId; } - logger.info("Execute request: {}", uri); + logger.debug("Execute request: {}", uri); CookieStore c = httpClient.getCookieStore(); java.net.HttpCookie cookie = new HttpCookie("SessionId", sessionIdHttp); cookie.setPath("/"); From cfa3eb12bfccfed78b7236f47ff835ce9b265b17 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 12 Feb 2023 15:42:46 +0100 Subject: [PATCH 044/214] add constraint control from state on numeric before writing values Signed-off-by: Laurent ARNAL --- .../handler/SiemensHvacHandlerImpl.java | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index b8ceb98a3d52a..251869138d2bd 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.siemenshvac.internal.handler; +import java.math.BigDecimal; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.ZoneId; @@ -42,6 +43,7 @@ import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; import org.openhab.core.types.State; +import org.openhab.core.types.StateDescription; import org.openhab.core.types.Type; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -297,6 +299,46 @@ private void WriteDp(String dp, Type dpVal, String type) { } } + private Command applyState(ChannelType tp, Command command) { + StateDescription sd = tp.getState(); + Command result = command; + + if (sd != null) { + BigDecimal maxb = sd.getMaximum(); + BigDecimal minb = sd.getMinimum(); + BigDecimal step = sd.getStep(); + + if (command instanceof DecimalType) { + DecimalType bdc = (DecimalType) command; + double v1 = bdc.doubleValue(); + if (step != null) { + int divider = 1; + if (step.floatValue() == 0.5) { + divider = 2; + } else if (step.floatValue() == 0.1) { + divider = 10; + } else if (step.floatValue() == 0.01) { + divider = 100; + } + v1 = v1 * divider; + v1 = Math.floor(v1); + v1 = v1 / divider; + } + + if (minb != null && v1 < minb.floatValue()) { + v1 = minb.floatValue(); + } + if (maxb != null && v1 > maxb.floatValue()) { + v1 = maxb.floatValue(); + } + + result = new DecimalType("" + v1); + + } + } + return result; + } + @Override public void handleCommand(ChannelUID channelUID, Command command) { SiemensHvacMetadataRegistry lcMetaDataRegistry = metaDataRegistry; @@ -312,6 +354,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { return; } + Command commandVar = command; ChannelType tp = channelTypeRegistry.getChannelType(channel.getChannelTypeUID()); if (tp == null) { @@ -336,12 +379,15 @@ public void handleCommand(ChannelUID channelUID, Command command) { } if (command instanceof State) { - State state = (State) command; + + commandVar = applyState(tp, commandVar); + State state = (State) commandVar; + this.updateState(channelUID, state); } if (id != null && type != null) { - WriteDp(id, command, dptType); + WriteDp(id, commandVar, dptType); } } } From 34c30d4d876ed670a8e57a89e461552bf2339a77 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 12 Feb 2023 15:55:45 +0100 Subject: [PATCH 045/214] spotless:apply Signed-off-by: Laurent ARNAL --- .../internal/Metadata/SiemensHvacMetadataRegistryImpl.java | 6 +++--- .../internal/network/SiemensHvacConnectorImpl.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index 8865d4cae0a7e..67f1412758232 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -263,7 +263,7 @@ public void ReadMeta() { SiemensHvacConnector lcHvacConnector = hvacConnector; if (root == null && lcHvacConnector != null) { - logger.info("siemensHvac:Initialization():Begin"); + logger.info("siemensHvac:Initialization():Begin_0001"); logger.info("siemensHvac:Initialization():ReadCache"); LoadMetaDataFromCache(); @@ -380,8 +380,8 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { if (channelType != null) { ChannelDefinition channelDef = new ChannelDefinitionBuilder(id, channelType.getUID()).withLabel(dataPoint.getShortDesc()) - .withDescription(dataPoint.getLongDesc()) - .withProperties(props).build(); + .withDescription(dataPoint.getLongDesc()).withProperties(props) + .build(); channelDefinitions.add(channelDef); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 1e30cd4e2b872..d4b7c24a88f6b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -238,7 +238,7 @@ private void initConfig() throws Exception { if (config.containsKey("baseUrl")) { baseUrl = (String) config.get("baseUrl"); - } + if (config.containsKey("userName")) { userName = (String) config.get("userName"); @@ -365,7 +365,7 @@ private void doAuth(boolean http) throws Exception { mUri = mUri + "SessionId=" + sessionId; } - logger.debug("Execute request: {}", uri); + logger.info("Execute request: {}", uri); CookieStore c = httpClient.getCookieStore(); java.net.HttpCookie cookie = new HttpCookie("SessionId", sessionIdHttp); cookie.setPath("/"); From 7c84e72b2a9b59c86616df3aae94f67a8af17395 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 12 Feb 2023 16:14:24 +0100 Subject: [PATCH 046/214] remove logs Signed-off-by: Laurent ARNAL --- .../internal/Metadata/SiemensHvacMetadataRegistryImpl.java | 4 ++-- .../internal/network/SiemensHvacConnectorImpl.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index 67f1412758232..5cca49fba3273 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -380,8 +380,8 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { if (channelType != null) { ChannelDefinition channelDef = new ChannelDefinitionBuilder(id, channelType.getUID()).withLabel(dataPoint.getShortDesc()) - .withDescription(dataPoint.getLongDesc()).withProperties(props) - .build(); + .withDescription(dataPoint.getLongDesc()) + .withProperties(props).build(); channelDefinitions.add(channelDef); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index d4b7c24a88f6b..1e30cd4e2b872 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -238,7 +238,7 @@ private void initConfig() throws Exception { if (config.containsKey("baseUrl")) { baseUrl = (String) config.get("baseUrl"); - + } if (config.containsKey("userName")) { userName = (String) config.get("userName"); @@ -365,7 +365,7 @@ private void doAuth(boolean http) throws Exception { mUri = mUri + "SessionId=" + sessionId; } - logger.info("Execute request: {}", uri); + logger.debug("Execute request: {}", uri); CookieStore c = httpClient.getCookieStore(); java.net.HttpCookie cookie = new HttpCookie("SessionId", sessionIdHttp); cookie.setPath("/"); From 9348fe6ed3574994bbd85eec1c35d0342b1277b6 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 16 Apr 2023 09:04:05 +0200 Subject: [PATCH 047/214] spotless:apply Signed-off-by: Laurent ARNAL --- .../internal/Metadata/SiemensHvacMetadataRegistryImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index 5cca49fba3273..67f1412758232 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -380,8 +380,8 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { if (channelType != null) { ChannelDefinition channelDef = new ChannelDefinitionBuilder(id, channelType.getUID()).withLabel(dataPoint.getShortDesc()) - .withDescription(dataPoint.getLongDesc()) - .withProperties(props).build(); + .withDescription(dataPoint.getLongDesc()).withProperties(props) + .build(); channelDefinitions.add(channelDef); } From 2883c737190c8874539ffe4a1faab28e3ef041d3 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 16 Apr 2023 09:04:17 +0200 Subject: [PATCH 048/214] review write request load Signed-off-by: Laurent ARNAL --- .../handler/SiemensHvacHandlerImpl.java | 22 ++++++++++++++----- .../network/SiemensHvacConnectorImpl.java | 2 +- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index 251869138d2bd..d298de56e5889 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -69,6 +69,7 @@ public class SiemensHvacHandlerImpl extends BaseThingHandler implements SiemensH private final @Nullable SiemensHvacConnector hvacConnector; private final @Nullable SiemensHvacMetadataRegistry metaDataRegistry; private final ChannelTypeRegistry channelTypeRegistry; + private long LastWrite = 0; public SiemensHvacHandlerImpl(Thing thing, @Nullable SiemensHvacConnector hvacConnector, @Nullable SiemensHvacMetadataRegistry metaDataRegistry, ChannelTypeRegistry channelTypeRegistry) { @@ -97,7 +98,7 @@ public void initialize() { } }); - pollingJob = scheduler.scheduleWithFixedDelay(this::pollingCode, 0, 10, TimeUnit.SECONDS); + pollingJob = scheduler.scheduleWithFixedDelay(this::pollingCode, 0, 60, TimeUnit.SECONDS); } @Override @@ -217,10 +218,11 @@ private void ReadDp(String dp, String uid, String type, boolean async) { try { lockObj.lock(); - logger.debug("Start read : {}", dp); + + logger.info("Start read : {}", dp); String request = "api/menutree/read_datapoint.json?Id=" + dp; - logger.debug("siemensHvac:ReadDp:DoRequest(): {}", request); + logger.info("siemensHvac:ReadDp:DoRequest(): {}", request); if (async) { if (lcHvacConnector != null) { @@ -228,6 +230,12 @@ private void ReadDp(String dp, String uid, String type, boolean async) { @Override public void execute(java.net.URI uri, int status, @Nullable Object response) { + // prevent async read if we just write so we have no overlaps + long now = System.currentTimeMillis(); + if (now - LastWrite < 5000) { + return; + } + if (response instanceof JsonObject) { DecodeReadDp((JsonObject) response, uid, dp, type); } @@ -246,7 +254,6 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { } finally { logger.debug("End read : {}", dp); lockObj.unlock(); - } } @@ -258,6 +265,9 @@ private void WriteDp(String dp, Type dpVal, String type) { try { lockObj.lock(); + logger.info("Start write : {}", dp); + LastWrite = System.currentTimeMillis(); + String valUpdate = "0"; String valUpdateEnum = ""; @@ -284,10 +294,10 @@ private void WriteDp(String dp, Type dpVal, String type) { type); if (lcHvacConnector != null) { - logger.debug("Write request for : {} ", valUpdate); + logger.info("Write request for : {} ", valUpdate); JsonObject response = lcHvacConnector.DoRequest(request, null); - logger.debug("Write request response : {} ", response); + logger.info("Write request response : {} ", response); } } catch (Exception e) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 1e30cd4e2b872..30ff29857627e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -206,7 +206,7 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacCallback cb) lockObj.lock(); try { startedRequest++; - logger.debug("StartedRequest : {}", startedRequest); + logger.info("StartedRequest : {}", startedRequest - completedRequest); } finally { lockObj.unlock(); From f4f30cb582a7f8591eb1200a67c745a33d800176 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 16 Apr 2023 13:01:16 +0200 Subject: [PATCH 049/214] fix enum Signed-off-by: Laurent ARNAL --- .../internal/Metadata/SiemensHvacMetadataRegistryImpl.java | 2 +- .../internal/constants/SiemensHvacBindingConstants.java | 1 + .../internal/handler/SiemensHvacHandlerImpl.java | 6 ++++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index 67f1412758232..a8ecf667873d9 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -545,7 +545,7 @@ public static String getItemType(SiemensHvacMetadataDataPoint dpt) { } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { return SiemensHvacBindingConstants.ITEM_TYPE_NUMBER; } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { - return SiemensHvacBindingConstants.ITEM_TYPE_NUMBER; + return SiemensHvacBindingConstants.ITEM_TYPE_ENUM; } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_DATE)) { return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_TIME)) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java index 8fb0f30d33c8c..53dae61f439d8 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java @@ -47,6 +47,7 @@ public class SiemensHvacBindingConstants { public static final String ITEM_TYPE_CONTACT = "Contact"; public static final String ITEM_TYPE_STRING = "String"; public static final String ITEM_TYPE_NUMBER = "Number"; + public static final String ITEM_TYPE_ENUM = "Enum"; public static final String ITEM_TYPE_DIMMER = "Dimmer"; public static final String ITEM_TYPE_DATETIME = "DateTime"; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index d298de56e5889..cc9afbfd8b507 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -145,11 +145,11 @@ private void ReadChannel(Channel channel) { } if (Id == null) { - logger.debug("poolingCode : Id is null {} ", channel); + logger.debug("pollingCode : Id is null {} ", channel); return; } if (type == null) { - logger.debug("poolingCode : type is null {} ", channel); + logger.debug("pollingCode : type is null {} ", channel); return; } ReadDp(Id, uid, type, true); @@ -236,6 +236,8 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { return; } + logger.info("End read : {}", dp); + if (response instanceof JsonObject) { DecodeReadDp((JsonObject) response, uid, dp, type); } From f76601cb602ac6ab18487cbc8727ad736505cd0a Mon Sep 17 00:00:00 2001 From: lo92fr Date: Sun, 17 Sep 2023 15:34:34 +0200 Subject: [PATCH 050/214] Update bundles/org.openhab.binding.siemenshvac/pom.xml Co-authored-by: Wouter Born Signed-off-by: lo92fr --- bundles/org.openhab.binding.siemenshvac/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/pom.xml b/bundles/org.openhab.binding.siemenshvac/pom.xml index a4e7eb9a2165e..5103b0a2d7431 100644 --- a/bundles/org.openhab.binding.siemenshvac/pom.xml +++ b/bundles/org.openhab.binding.siemenshvac/pom.xml @@ -7,7 +7,7 @@ org.openhab.addons.bundles org.openhab.addons.reactor.bundles - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT org.openhab.binding.siemenshvac From 9ce7efebdfe448d27d26ab9a3100fbd91d125148 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Fri, 13 Oct 2023 09:13:39 +0200 Subject: [PATCH 051/214] First pass on jlaur review Signed-off-by: Laurent ARNAL --- .../org.openhab.binding.siemenshvac/README.md | 30 +++++++++---------- .../Metadata/SiemensHvacMetadataDevice.java | 2 +- .../SiemensHvacMetadataRegistryImpl.java | 27 ++++++++--------- .../SiemenesHvacDiscoveryParticipant.java | 2 +- .../SiemensHvacDeviceDiscoveryService.java | 8 ++--- .../factory/SiemensHvacHandlerFactory.java | 3 +- .../internal/handler/SiemensHvacHandler.java | 24 --------------- .../handler/SiemensHvacHandlerImpl.java | 28 ++++++++--------- .../SiemensHvacOZW672BridgeThingHandler.java | 9 ++---- .../network/SiemensHvacConnector.java | 3 +- .../network/SiemensHvacConnectorImpl.java | 23 +++++++------- .../network/SiemensHvacRequestListener.java | 13 ++++---- .../SiemensHvacChannelGroupTypeProvider.java | 4 +-- .../SiemensHvacConfigDescriptionProvider.java | 2 +- .../internal/type/SiemensHvacException.java | 5 ++-- .../type/SiemensHvacThingTypeProvider.java | 4 +-- .../siemenshvac/internal/type/UidUtils.java | 3 -- .../src/main/resources/OH-INF/addon/addon.xml | 1 + .../OH-INF/i18n/siemenshvac_fr_FR.properties | 9 ------ .../main/resources/OH-INF/thing/ozw672.xml | 22 +++++++------- 20 files changed, 88 insertions(+), 134 deletions(-) delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandler.java delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr_FR.properties diff --git a/bundles/org.openhab.binding.siemenshvac/README.md b/bundles/org.openhab.binding.siemenshvac/README.md index 63ccdcd414484..387819d1a41b3 100644 --- a/bundles/org.openhab.binding.siemenshvac/README.md +++ b/bundles/org.openhab.binding.siemenshvac/README.md @@ -2,29 +2,27 @@ This binding is to support Siemens Hvac controller ecosystem, and the Web Gateway interface OZW672. A typical system is composed of: - <=== Ethernet ===> | OZW672 | <====== ¨BSB/LPB BUS ======> | Hvac Controler (RVS41.813/327) | ====== | Internal device in your system : sensors, boiler, external pac unit, ... | -There's a lot of different Hvac controler depending on model in lot of different PAC constructor. -Mine is a Atlantic Hybrid duo whith a Siemens RVS41.813/327 inside. +There's a lot of different HVAC controllers depending on model in lot of different PAC constructors. +Siemens RVS41.813/327 inside a Atlantic Hybrid Duo was used for the developpement, and is fully supported and tested. -Siemens have a complete set of controler reference under the name "Siemens Albatros". -Here some picture of such device. -You can also find this device in other type of heating system : boiler or solar based. +Siemens have a complete set of controller references under the name "Siemens Albatros". +Here is a picture of such device. +You can also find this device in other types of heating systems: boiler or solar based. ![](doc/Albatros.jpg) -You will find some information about the OZW672.01 gateway on siemens web site : +You will find some information about the OZW672.01 gateway on the Siemens web site : [OZW 672 Page] -([https://hit.sbt.siemens.com/RWD/app.aspx?rc=FR&lang=fr&module=Catalog&action=ShowProduct&key=BPZ:OZW672.01) -With this binding, you will be able : - -- To consult the different parameters of your system like temperature, current heating mode, water temperature, and many more. -- Modify the functionning mode of your device : temperature set point, heating mode, and others. +([https://hit.sbt.siemens.com/RWD/app.aspx?rc=FR&lang=fr&module=Catalog&action=ShowProduct&key=BPZ:OZW672.01) +With this binding, you will be able to: +- Consult the different parameters of your system, like temperature, current heating mode, water temperature, and many more. +- Modify the functioning mode of your device: temperature set point, heating mode, and others. ## Supported Things @@ -62,9 +60,9 @@ IP should have be discovered automatically via UPNP. | Parameter | Required | Default | Description | |-----------------|----------|---------------|---------------------------------------------------------------------| -| ipAdress | yes | | The address of the OZW672 devices | -| userName | yes | Administrator | The userName to log into the OZW672 | -| userPass | yes | | The userPassword to log into the OZW672 | +| baseUrl | yes | | The address of the OZW672 devices | +| userName | yes | Administrator | The user name to log into the OZW672 | +| userPass | yes | | The user password to log into the OZW672 | ## Thing Configuration @@ -116,7 +114,7 @@ Bridge siemenshvac:ozw672:local "Ozw672"@"Chaufferie" [ baseUrl="https://192.168 Items file `.items` ```java -Sring Chaudiere_Etat_Pompe_ECS "Etat Pompe ECS [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2259_PpeChargeECS" } +String Chaudiere_Etat_Pompe_ECS "Etat Pompe ECS [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2259_PpeChargeECS" } Number Chaudiere_Etat_ECS "Etat ECS [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:2032#2035_Etat_ECS" } Number Temperature_Depart_Reel "Départ réel [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2248_ValReelleTempDep_CC1" } Number Temperature_Depart_Consigne "Départ cons [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2249_ConsTDepResultCC1" } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java index 4495872048154..676cef38e25e5 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java @@ -26,7 +26,7 @@ public class SiemensHvacMetadataDevice { private String addr = ""; - private String type = "unknow"; + private String type = "unknown"; private String serialNr = ""; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index a8ecf667873d9..d1c50cf362383 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -13,11 +13,12 @@ package org.openhab.binding.siemenshvac.internal.Metadata; import java.io.File; -import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.math.BigDecimal; import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; @@ -67,8 +68,6 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import io.micrometer.core.instrument.util.IOUtils; - /** * * @author Laurent Arnal - Initial contribution @@ -620,7 +619,7 @@ public void ReadDeviceList() { JsonObject response = null; if (lcHvacConnector != null) { - response = lcHvacConnector.DoRequest(request, null); + response = lcHvacConnector.doRequest(request, null); } JsonArray devicesList = null; if (response != null) { @@ -681,7 +680,7 @@ public void ReadDeviceList() { String request2 = "api/menutree/device_root.json?TreeName=Web&SerialNumber=" + SerialNr; if (lcHvacConnector != null) { - JsonObject response2 = lcHvacConnector.DoRequest(request2, null); + JsonObject response2 = lcHvacConnector.doRequest(request2, null); if (response2 != null && response2.has("TreeItem")) { JsonObject tree = response2.getAsJsonObject("TreeItem"); @@ -710,7 +709,7 @@ public void ReadMetaData(@Nullable SiemensHvacMetadata parent, int id) { } if (lcHvacConnector != null) { - lcHvacConnector.DoRequest(request, new SiemensHvacCallback() { + lcHvacConnector.doRequest(request, new SiemensHvacCallback() { @Override public void execute(URI uri, int status, @Nullable Object response) { @@ -885,7 +884,7 @@ public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMeta } if (lcHvacConnector != null) { - lcHvacConnector.DoRequest(request2, new SiemensHvacCallback() { + lcHvacConnector.doRequest(request2, new SiemensHvacCallback() { @Override public void execute(URI uri, int status, @Nullable Object response) { @@ -957,12 +956,12 @@ public void LoadMetaDataFromCache() { return; } - FileInputStream is = new FileInputStream(file); - String js = IOUtils.toString(is); + byte[] bytes = Files.readAllBytes(file.toPath()); + String js = new String(bytes, StandardCharsets.UTF_8); root = SiemensHvacConnectorImpl.getGsonWithAdapter().fromJson(js, SiemensHvacMetadataMenu.class); } catch (IOException ioe) { - logger.error("Couldn't write WithingsAccount to file '{}'.", file.getAbsolutePath()); + logger.warn("Couldn't read Siemens MetaData information from file '{}'.", file.getAbsolutePath()); } } @@ -987,7 +986,7 @@ public void SaveMetaDataToCache() { } } catch (IOException ioe) { - logger.error("Couldn't write WithingsAccount to file '{}'.", file.getAbsolutePath()); + logger.warn("Couldn't write Siemens MetaData information to file '{}'.", file.getAbsolutePath()); } } @@ -1000,16 +999,16 @@ public void resolveDptDetails(SiemensHvacMetadataDataPoint dpt, ResolveCount rv) String request = "api/menutree/datapoint_desc.json?Id=" + dpt.getId(); if (lcHvacConnector != null) { - lcHvacConnector.DoRequest(request, new SiemensHvacCallback() { + lcHvacConnector.doRequest(request, new SiemensHvacCallback() { @Override public void execute(URI uri, int status, @Nullable Object response) { if (response instanceof JsonObject) { rv.DecreaseResolveCount(); - logger.info("siemensHvac:Initialization():ToResolve() {}", rv.getResolveCount()); + logger.debug("siemensHvac:Initialization():ToResolve() {}", rv.getResolveCount()); dpt.resolveDptDetails((JsonObject) response); } else { - logger.debug("errror"); + logger.debug("Invalid response from Siemens gateway, result is not a JsonObject"); } } }); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java index 9cefb70cf9ad6..64580dd1b152e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java @@ -40,7 +40,7 @@ * @author Laurent Arnal - Initial contribution */ @NonNullByDefault -@Component(service = UpnpDiscoveryParticipant.class, configurationPid = "discovery.siemensHvac") +@Component(service = UpnpDiscoveryParticipant.class, configurationPid = "discovery.siemenshvac") public class SiemenesHvacDiscoveryParticipant implements UpnpDiscoveryParticipant { @Activate diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java index be60e61505a87..0270a75af3604 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java @@ -13,7 +13,6 @@ package org.openhab.binding.siemenshvac.internal.discovery; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -29,6 +28,7 @@ import org.openhab.core.config.discovery.DiscoveryResult; import org.openhab.core.config.discovery.DiscoveryResultBuilder; import org.openhab.core.config.discovery.DiscoveryService; +import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; import org.openhab.core.thing.binding.ThingHandler; @@ -42,7 +42,6 @@ * * @author Laurent Arnal - Initial contribution */ -// @Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.siemenshvac") @NonNullByDefault public class SiemensHvacDeviceDiscoveryService extends AbstractDiscoveryService implements DiscoveryService, ThingHandlerService { @@ -51,8 +50,7 @@ public class SiemensHvacDeviceDiscoveryService extends AbstractDiscoveryService private @Nullable SiemensHvacMetadataRegistry metadataRegistry; private @Nullable SiemensHvacBridgeBaseThingHandler siemensHvacBridgeHandler; - public static final Set SUPPORTED_THING_TYPES = Collections - .singleton(SiemensHvacBindingConstants.THING_TYPE_OZW672); + public static final Set SUPPORTED_THING_TYPES = Set.of(SiemensHvacBindingConstants.THING_TYPE_OZW672); private static final int SEARCH_TIME = 10; @@ -127,7 +125,7 @@ public void startScan() { properties.put("name", name); properties.put("type", type); properties.put("addr", addr); - properties.put("serialNr", serialNr); + properties.put(Thing.PROPERTY_SERIAL_NUMBER, serialNr); DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID) .withProperties(properties).withBridge(bridgeUID).withLabel(name).build(); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java index 7d4fdd403be6b..3a2aa788cd241 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java @@ -74,7 +74,8 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { } else if (SiemensHvacBindingConstants.BINDING_ID.equals(thingTypeUID.getBindingId())) { return super.createThing(thingTypeUID, configuration, thingUID, bridgeUID); } - throw new IllegalArgumentException("The thing type " + thingTypeUID + " is not supported by the KNX binding."); + throw new IllegalArgumentException( + "The thing type " + thingTypeUID + " is not supported by the SiemensHvac binding."); } @Override diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandler.java deleted file mode 100644 index 11662f37cc227..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandler.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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.siemenshvac.internal.handler; - -import org.eclipse.jdt.annotation.NonNullByDefault; - -/** - * - * @author Laurent Arnal - Initial contribution - */ -@NonNullByDefault -public interface SiemensHvacHandler { - -} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index cc9afbfd8b507..42fef9e18ce3b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -58,7 +58,7 @@ * @author Laurent ARNAL - Initial contribution */ @NonNullByDefault -public class SiemensHvacHandlerImpl extends BaseThingHandler implements SiemensHvacHandler { +public class SiemensHvacHandlerImpl extends BaseThingHandler { private Lock lockObj = new ReentrantLock(); @@ -78,10 +78,6 @@ public SiemensHvacHandlerImpl(Thing thing, @Nullable SiemensHvacConnector hvacCo this.hvacConnector = hvacConnector; this.metaDataRegistry = metaDataRegistry; this.channelTypeRegistry = channelTypeRegistry; - - logger.debug("==========================================================="); - logger.debug("Siemens HVac"); - logger.debug("==========================================================="); } @Override @@ -226,7 +222,7 @@ private void ReadDp(String dp, String uid, String type, boolean async) { if (async) { if (lcHvacConnector != null) { - lcHvacConnector.DoRequest(request, new SiemensHvacCallback() { + lcHvacConnector.doRequest(request, new SiemensHvacCallback() { @Override public void execute(java.net.URI uri, int status, @Nullable Object response) { @@ -236,7 +232,7 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { return; } - logger.info("End read : {}", dp); + logger.trace("End read : {}", dp); if (response instanceof JsonObject) { DecodeReadDp((JsonObject) response, uid, dp, type); @@ -246,15 +242,15 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { } } else { if (lcHvacConnector != null) { - JsonObject js = lcHvacConnector.DoRequest(request, null); + JsonObject js = lcHvacConnector.doRequest(request, null); DecodeReadDp(js, uid, dp, type); } } } catch (Exception e) { - logger.error("siemensHvac:ReadDp:Error during dp reading: {} ; {}", dp, e.getLocalizedMessage()); + logger.debug("siemensHvac:ReadDp:Error during dp reading: {} ; {}", dp, e.getLocalizedMessage()); // Reset sessionId so we redone _auth on error } finally { - logger.debug("End read : {}", dp); + logger.trace("End read : {}", dp); lockObj.unlock(); } } @@ -267,7 +263,7 @@ private void WriteDp(String dp, Type dpVal, String type) { try { lockObj.lock(); - logger.info("Start write : {}", dp); + logger.trace("Start write: {}", dp); LastWrite = System.currentTimeMillis(); String valUpdate = "0"; @@ -296,17 +292,17 @@ private void WriteDp(String dp, Type dpVal, String type) { type); if (lcHvacConnector != null) { - logger.info("Write request for : {} ", valUpdate); - JsonObject response = lcHvacConnector.DoRequest(request, null); + logger.trace("Write request for: {} ", valUpdate); + JsonObject response = lcHvacConnector.doRequest(request, null); - logger.info("Write request response : {} ", response); + logger.trace("Write request response: {} ", response); } } catch (Exception e) { - logger.error("siemensHvac:ReadDp:Error during dp reading: {} ; {}", dp, e.getLocalizedMessage()); + logger.debug("siemensHvac:ReadDp:Error during dp reading: {}: {}", dp, e.getLocalizedMessage()); // Reset sessionId so we redone _auth on error } finally { - logger.debug("End write : {}", dp); + logger.debug("End write: {}", dp); lockObj.unlock(); } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java index f5e5154225a8a..3386c8cd51945 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java @@ -29,13 +29,10 @@ import org.slf4j.LoggerFactory; /** - * The {@link IPBridgeThingHandler} is responsible for handling commands, which are - * sent to one of the channels. It implements a KNX/IP Gateway, that either acts a a - * conduit for other {@link DeviceThingHandler}s, or for Channels that are - * directly defined on the bridge + * The {@link IPBridgeThingHandler} is responsible for handling communication to Siemens Gateway using HTTP API + * interface. * - * @author Karel Goderis - Initial contribution - * @author Simon Kaufmann - Refactoring & cleanup + * @author Laurent ARNAL - Initial contribution */ @NonNullByDefault public class SiemensHvacOZW672BridgeThingHandler extends SiemensHvacBridgeBaseThingHandler { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java index 597a082defd25..d74a95ce6b3e8 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java @@ -25,7 +25,8 @@ @NonNullByDefault public interface SiemensHvacConnector { - public @Nullable JsonObject DoRequest(String req, @Nullable SiemensHvacCallback callback); + @Nullable + JsonObject doRequest(String req, @Nullable SiemensHvacCallback callback); public void WaitAllPendingRequest(); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 30ff29857627e..7a1d98574f626 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -24,8 +24,6 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import javax.validation.constraints.NotNull; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -65,8 +63,8 @@ public class SiemensHvacConnectorImpl implements SiemensHvacConnector { private static final Logger logger = LoggerFactory.getLogger(SiemensHvacConnectorImpl.class); - private final static @NotNull Gson gson; - private final static @NotNull Gson gsonWithAdpter; + private final static Gson gson; + private final static Gson gsonWithAdpter; private @Nullable String sessionId = null; private @Nullable String sessionIdHttp = null; @@ -182,7 +180,7 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacCallback cb) executeRequest(retryRequest, cb); } } catch (Exception ex) { - logger.error("exception"); + logger.debug("Error during gateway request:", ex); throw ex; } } @@ -206,7 +204,7 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacCallback cb) lockObj.lock(); try { startedRequest++; - logger.info("StartedRequest : {}", startedRequest - completedRequest); + logger.trace("StartedRequest: {}", startedRequest - completedRequest); } finally { lockObj.unlock(); @@ -290,7 +288,7 @@ private void doAuth(boolean http) throws Exception { } if (sessionIdHttp == null) { - logger.debug("Session request auth was unsucessfull in _doAuth()"); + logger.debug("Session request auth was unsuccessful in _doAuth()"); } } else { if (result != null) { @@ -307,7 +305,7 @@ private void doAuth(boolean http) throws Exception { if (resultObj.has("SessionId")) { sessionId = resultObj.get("SessionId").getAsString(); - logger.debug("Have new SessionId : {} ", sessionId); + logger.debug("Have new SessionId: {} ", sessionId); } } @@ -318,7 +316,7 @@ private void doAuth(boolean http) throws Exception { logger.debug("siemensHvac:doAuth:decodeResponse:()"); if (sessionId == null) { - logger.debug("Session request auth was unsucessfull in _doAuth()"); + logger.debug("Session request auth was unsuccessful in _doAuth()"); } } @@ -327,7 +325,7 @@ private void doAuth(boolean http) throws Exception { } } - logger.debug("siemensHvac:doAuth:connect()"); + logger.trace("siemensHvac:doAuth:connect()"); } catch (Exception ex) { logger.debug("siemensHvac:doAuth:error() {}", ex.getLocalizedMessage()); @@ -388,8 +386,7 @@ private void doAuth(boolean http) throws Exception { } } } catch (Exception ex) { - logger.error("siemensHvac:DoRequest:Exception by executing Request: {} ; {} ", uri, - ex.getLocalizedMessage()); + logger.warn("siemensHvac:DoRequest:Exception by executing Request: {}: {} ", uri, ex.getLocalizedMessage()); } finally { } @@ -397,7 +394,7 @@ private void doAuth(boolean http) throws Exception { } @Override - public @Nullable JsonObject DoRequest(String req, @Nullable SiemensHvacCallback callback) { + public @Nullable JsonObject doRequest(String req, @Nullable SiemensHvacCallback callback) { try { String response = DoBasicRequest(req, callback); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java index 2ce7e3585a780..46415f778e6ce 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java @@ -29,6 +29,7 @@ import com.google.gson.Gson; import com.google.gson.JsonObject; +import com.google.gson.JsonSyntaxException; /** * @author Laurent Arnal - Initial contribution @@ -89,7 +90,7 @@ public void onComplete(@Nullable Result result) { logger.trace("response complete: {}", content); if (result.getResponse().getStatus() != 200) { - logger.debug("bad gateway !!!"); + logger.debug("Error requesting gateway, non success code: {}", result.getResponse().getStatus()); hvacConnector.onError(result.getRequest(), callback); return; } @@ -102,7 +103,7 @@ public void onComplete(@Nullable Result result) { try { Gson gson = SiemensHvacConnectorImpl.getGson(); resultObj = gson.fromJson(content, JsonObject.class); - } catch (Exception ex) { + } catch (JsonSyntaxException ex) { logger.debug("error: {}", ex.toString()); } @@ -128,16 +129,16 @@ public void onComplete(@Nullable Result result) { callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), resultObj); } else { - logger.debug("error : {}", subResultObj); + logger.debug("error: {}", subResultObj); hvacConnector.onError(result.getRequest(), callback); } } else { - logger.debug("error"); + logger.debug("error: invalid response from gateway, missing subResultObj:Success entry"); hvacConnector.onError(result.getRequest(), callback); } } else { - logger.debug("error"); + logger.debug("error: invalid response from gateway, missing Result entry"); hvacConnector.onError(result.getRequest(), callback); } @@ -148,7 +149,7 @@ public void onComplete(@Nullable Result result) { callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), content); hvacConnector.onComplete(result.getRequest()); } catch (Exception ex) { - logger.debug("error"); + logger.debug("An error occurred", ex); } } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java index deabf9688fd1d..563a8c6d92d14 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java @@ -29,7 +29,7 @@ public interface SiemensHvacChannelGroupTypeProvider extends ChannelGroupTypePro /** * Adds the ChannelGroupType to this provider. */ - public void addChannelGroupType(ChannelGroupType channelGroupType); + void addChannelGroupType(ChannelGroupType channelGroupType); /** * Use this method to lookup a ChannelGroupType which was generated by the @@ -45,5 +45,5 @@ public interface SiemensHvacChannelGroupTypeProvider extends ChannelGroupTypePro * before */ @Nullable - public ChannelGroupType getInternalChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID); + ChannelGroupType getInternalChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java index 7ba1d5a200503..691b2935b47df 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java @@ -31,7 +31,7 @@ public interface SiemensHvacConfigDescriptionProvider extends ConfigDescriptionP /** * Adds the ConfigDescription to this provider. */ - public void addConfigDescription(ConfigDescription configDescription); + void addConfigDescription(ConfigDescription configDescription); /** * Provides a {@link ConfigDescription} for the given URI. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacException.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacException.java index de8334df555b1..9bf259caf90c2 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacException.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacException.java @@ -16,11 +16,12 @@ /** * - * @author Laurent Arnal - Initial contribution + * An exception that occurred while operating the binding * - * An exception that occurred while operating the binding + * @author Laurent Arnal - Initial contribution * */ + @NonNullByDefault public class SiemensHvacException extends Exception { private static final long serialVersionUID = -3398100220952729816L; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java index 12d48b4eba24e..c6f10be87ef1b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java @@ -29,7 +29,7 @@ public interface SiemensHvacThingTypeProvider extends ThingTypeProvider { /** * Adds the ThingType to this provider. */ - public void addThingType(ThingType thingType); + void addThingType(ThingType thingType); /** * Use this method to lookup a ThingType which was generated by the @@ -45,5 +45,5 @@ public interface SiemensHvacThingTypeProvider extends ThingTypeProvider { * before */ @Nullable - public ThingType getInternalThingType(ThingTypeUID thingTypeUID); + ThingType getInternalThingType(ThingTypeUID thingTypeUID); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index 473120c223089..575ad7ac262d6 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -158,8 +158,6 @@ public static ChannelTypeUID generateChannelTypeUID(SiemensHvacMetadataDataPoint id = String.format("%s", dpt.getDptType()); } - // dpt.Display(); - return new ChannelTypeUID(SiemensHvacBindingConstants.BINDING_ID, id); } @@ -176,7 +174,6 @@ public static ThingUID generateThingUID(Bridge bridge) { */ public static ChannelUID generateChannelUID(ThingUID thingUID) { return new ChannelUID(thingUID, ""); - // String.valueOf(dp.getChannel().getNumber()), dp.getName()); } /** diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml index 2f3d7b9fcf53f..81ee90bcd3ce0 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml @@ -6,4 +6,5 @@ SiemensHvac Binding This is the binding for SiemensHvac. + local z diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr_FR.properties b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr_FR.properties deleted file mode 100644 index 215658bba0834..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr_FR.properties +++ /dev/null @@ -1,9 +0,0 @@ -# FIXME: please substitute the xx_XX with a proper locale, ie. de_DE -# FIXME: please do not add the file to the repo if you add or change no content -# binding -binding.siemenshvac.name = Siemens HVac -binding.siemenshvac.description = Un binding permettant d'interfacer Openhab avec une PAC équipé d'un controleur Siemens RVS et d'une interface Web OZW672. - -# thing types -thing-type.siemenshvac.ozw672.label = Paserelle OZW672 -thing-type.siemenshvac.ozw672.description = Passerelle Web OZW672 entre le bus LSB du controleur RVS et Openhab diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml index e6915604a22ac..11bb3b4d88d38 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml @@ -16,13 +16,13 @@ network-address - UserName of the Siemens Hvac gateway + User name of the Siemens Hvac gateway false Administrator - UserPassword of the Siemens Hvac gateway + User password of the Siemens Hvac gateway false password @@ -66,19 +66,19 @@ Scheduler - Number + Number:Temperature Current temperature in degrees celsius Temperature - + - - Number + + Number:Temperature Current setpoint temperature in degrees Celsius Temperature - + @@ -86,10 +86,10 @@ - - - - + + + + From 624c27db934faf7e30693b8272010e8d036f3a01 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Fri, 13 Oct 2023 09:23:54 +0200 Subject: [PATCH 052/214] running spotless:apply Signed-off-by: Laurent ARNAL --- .../src/main/resources/OH-INF/addon/addon.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml index 81ee90bcd3ce0..cafd8e7ee15b8 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml @@ -6,5 +6,5 @@ SiemensHvac Binding This is the binding for SiemensHvac. - local z + local From 5ed68a2047430d42ad6c68e6faa7b3766b5421a5 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Fri, 13 Oct 2023 09:34:39 +0200 Subject: [PATCH 053/214] remove deprecated call Signed-off-by: Laurent ARNAL --- bundles/org.openhab.binding.siemenshvac/README.md | 2 +- .../siemenshvac/internal/network/SiemensHvacConnectorImpl.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/README.md b/bundles/org.openhab.binding.siemenshvac/README.md index 387819d1a41b3..3e578fedfad4a 100644 --- a/bundles/org.openhab.binding.siemenshvac/README.md +++ b/bundles/org.openhab.binding.siemenshvac/README.md @@ -78,7 +78,7 @@ Each channel are strongly typed, so for exemple, for heating mode, openhab will | Channel | Description | Type | Unit | Security Access Level | ReadOnly | Advanced | | ------------------------- | ------------------------------------------------------------------------------- | -------- | :--: | :-------------------: | :------: | :------: | | `controlBoilerApproval` | Set Boiler Approval (`AUTO`, `OFF`, `ON`) | `String` | | 🔐 W1 | R/W | true | -| `controlProgram` | Set Program (`OFF`, `NORMAL`, `WARMWATER`, `MANUAL`[1](#f1)) | `String` | | 🔐 W1 | +| `controlProgram` | Set Program (`OFF`, `NORMAL`, `WARMWATER`, `MANUAL`[1](f1)) | `String` | | 🔐 W1 | | Channel Type ID | Item Type | Description | |------------------|--------------|----------------------------------------------------------| diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 7a1d98574f626..b9057bdaeffdd 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -112,7 +112,6 @@ public SiemensHvacConnectorImpl(@Reference HttpClientFactory httpClientFactory) ctxFactory.setEndpointIdentificationAlgorithm(null); this.httpClient = new HttpClient(ctxFactory); - this.httpClient.setRemoveIdleDestinations(true); this.httpClient.setMaxConnectionsPerDestination(10); this.httpClient.setMaxRequestsQueuedPerDestination(1000); this.httpClient.setConnectTimeout(10000); From 4b128c2bd20ad96480af4f8273b4711dcee681ea Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 31 Oct 2023 11:46:13 +0100 Subject: [PATCH 054/214] pass on jlaur review Signed-off-by: Laurent ARNAL --- .../Diagram.png | Bin 0 -> 181348 bytes .../org.openhab.binding.siemenshvac/README.md | 165 ++++++++++++++---- .../SiemensHvacMetadataDataPoint.java | 6 +- .../SiemensHvacMetadataRegistryImpl.java | 12 +- .../SiemensHvacBindingConstants.java | 2 +- .../factory/SiemensHvacHandlerFactory.java | 9 +- .../SiemensHvacBridgeBaseThingHandler.java | 7 + .../handler/SiemensHvacBridgeConfig.java | 11 ++ .../handler/SiemensHvacHandlerImpl.java | 24 ++- .../SiemensHvacOZW672BridgeThingHandler.java | 1 - .../network/SiemensHvacConnectorImpl.java | 111 +++++------- .../siemenshvac/internal/type/UidUtils.java | 13 +- .../main/resources/OH-INF/thing/ozw672.xml | 13 +- .../internal/type/UidUtilsTest.java | 35 ++++ 14 files changed, 275 insertions(+), 134 deletions(-) create mode 100644 bundles/org.openhab.binding.siemenshvac/Diagram.png create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java diff --git a/bundles/org.openhab.binding.siemenshvac/Diagram.png b/bundles/org.openhab.binding.siemenshvac/Diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..300cc2f85838738d1915c87989e2724fa6460279 GIT binary patch literal 181348 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ǤI0X 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

Tf%`~6m*Ej73A^xXEZ*!7Q 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 literal 0 HcmV?d00001 diff --git a/bundles/org.openhab.binding.siemenshvac/README.md b/bundles/org.openhab.binding.siemenshvac/README.md index 3e578fedfad4a..1207d4044c33a 100644 --- a/bundles/org.openhab.binding.siemenshvac/README.md +++ b/bundles/org.openhab.binding.siemenshvac/README.md @@ -2,8 +2,9 @@ This binding is to support Siemens Hvac controller ecosystem, and the Web Gateway interface OZW672. A typical system is composed of: - -<=== Ethernet ===> | OZW672 | <====== ¨BSB/LPB BUS ======> | Hvac Controler (RVS41.813/327) | ====== | Internal device in your system : sensors, boiler, external pac unit, ... | + +![Diagram](Diagram.png) + There's a lot of different HVAC controllers depending on model in lot of different PAC constructors. Siemens RVS41.813/327 inside a Atlantic Hybrid Duo was used for the developpement, and is fully supported and tested. @@ -58,11 +59,35 @@ IP should have be discovered automatically via UPNP. ## Bridge Configuration -| Parameter | Required | Default | Description | -|-----------------|----------|---------------|---------------------------------------------------------------------| -| baseUrl | yes | | The address of the OZW672 devices | -| userName | yes | Administrator | The user name to log into the OZW672 | -| userPass | yes | | The user password to log into the OZW672 | + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterRequiredDefaultDescription
baseUrlyesThe address of the OZW672 devices
userNameyesAdministratorThe user name to log into the OZW672
userPassyesThe user password to log into the OZW672
+ + ## Thing Configuration @@ -75,23 +100,97 @@ Channels are autodiscovered, you will find them on the RVS things. They are organized the same way as the LCD screen of your PAC device, by top level menu functionnality, and sub-functionnalities. Each channel are strongly typed, so for exemple, for heating mode, openhab will provide you with a list of choice supported by the device. -| Channel | Description | Type | Unit | Security Access Level | ReadOnly | Advanced | -| ------------------------- | ------------------------------------------------------------------------------- | -------- | :--: | :-------------------: | :------: | :------: | -| `controlBoilerApproval` | Set Boiler Approval (`AUTO`, `OFF`, `ON`) | `String` | | 🔐 W1 | R/W | true | -| `controlProgram` | Set Program (`OFF`, `NORMAL`, `WARMWATER`, `MANUAL`[1](f1)) | `String` | | 🔐 W1 | - -| Channel Type ID | Item Type | Description | -|------------------|--------------|----------------------------------------------------------| -| Numeric | Number | Handle basic numeric value | -| String | String | a String | -| String_16 | String | a String of length <= 16 char | -| TimeOfDay | TimeOfDay | | -| Datetime | Datetime | | -| RadioButton | RadioButton | | -| Scheduler | Scheduler | | -| Temperature | Number | Use to handle reading of temperature | -| Setpoint | Number | Handle the setting of a temperature | -| Regime | Number | Enumeration for handling mode change | + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ChannelDescriptionTypeUnitSecurity Access LevelReadOnlyAdvanced
controlBoilerApprovalSet Boiler Approval (`AUTO`, `OFF`, `ON`)StringR/Wtrue
controlProgramSet Program (`OFF`, `NORMAL`, `WARMWATER`, `MANUAL`)StringR/Wtrue
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Channel Type IDItem Type Description
NumericNumberHandle basic numeric value
StringStringa String
String_16Stringa String of length <= 16 char
TimeOfDayTimeOfDay
DatetimeDatetime
RadioButtonRadioButton
SchedulerScheduler
TemperatureNumberUse to handle reading of temperature
SetpointNumberHandle the setting of a temperature
RegimeNumberEnumeration for handling mode change
## Full Example @@ -114,13 +213,13 @@ Bridge siemenshvac:ozw672:local "Ozw672"@"Chaufferie" [ baseUrl="https://192.168 Items file `.items` ```java -String Chaudiere_Etat_Pompe_ECS "Etat Pompe ECS [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2259_PpeChargeECS" } -Number Chaudiere_Etat_ECS "Etat ECS [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:2032#2035_Etat_ECS" } -Number Temperature_Depart_Reel "Départ réel [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2248_ValReelleTempDep_CC1" } -Number Temperature_Depart_Consigne "Départ cons [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2249_ConsTDepResultCC1" } -Number Heure_fct_ECS "Heure Fct Ecs" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2263_HeuresFoncPompeECS" } -Number Nb_Demarrage_ECS "Nbr dém ECS [%.1f]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2266_ComptDemarResEl_ECS" } -Number TemperatureThermostat "Temp thermt [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2246_TAmbAct_CC1" } -Number Temperature_Consigne_C "Chauf Cons [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:1724#1726_ConsConfort_TA_CC1" } -Number Chauffage_Mode "Chauffage Mode [%s]" { channel="siemenshvac:RVS41_813_327:local:local:1724#1725_Regime_CC1" } +String Boiler_State_Pump_HWS "HWS Pump State [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2259_PpeChargeECS" } +Number Boiler_State_HWS "HWS State [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:2032#2035_Etat_ECS" } +Number Flow_Temperature_Real "Flow Temparature Real [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2248_ValReelleTempDep_CC1" } +Number Flow_Temperature_Setpoint "Flow Temperature Setpoint [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2249_ConsTDepResultCC1" } +Number Hour_fct_HWS "HWS Hour function" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2263_HeuresFoncPompeECS" } +Number Nb_Start_HWS "HWS Number of start [%.1f]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2266_ComptDemarResEl_ECS" } +Number Thermostat_Temperature "Thermostat tempeature [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2246_TAmbAct_CC1" } +Number Thermostat_Setpoint "Thermostat setpoint [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:1724#1726_ConsConfort_TA_CC1" } +Number Heat_Mode "Heat mode [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:1724#1725_Regime_CC1" } ``` diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java index f3436dd16c3d7..95972b402a26d 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java @@ -229,12 +229,8 @@ public void resolveDptDetails(JsonObject result) { ch.setIsActive(button.get("IsActive").getAsString()); child.add(ch); } - } else if (SiemensHvacBindingConstants.DPT_TYPE_DATE.equals(dptType)) { - } else if (SiemensHvacBindingConstants.DPT_TYPE_TIME.equals(dptType)) { - } else if (SiemensHvacBindingConstants.DPT_TYPE_SCHEDULER.equals(dptType)) { - } else if (SiemensHvacBindingConstants.DPT_TYPE_CALENDAR.equals(dptType)) { - } else { } + detailsResolved = true; } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index d1c50cf362383..6dd2104b9e7ad 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -545,7 +545,7 @@ public static String getItemType(SiemensHvacMetadataDataPoint dpt) { return SiemensHvacBindingConstants.ITEM_TYPE_NUMBER; } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { return SiemensHvacBindingConstants.ITEM_TYPE_ENUM; - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_DATE)) { + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_DATE_TIME)) { return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_TIME)) { return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; @@ -574,15 +574,15 @@ public static String getCategory(SiemensHvacMetadataDataPoint dp) { return ""; } else if (dptUnit.contains("°C")) { return SiemensHvacBindingConstants.CATEGORY_CHANNEL_PROPS_TEMP; - } else if (dpType.contains("DateTime")) { + } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_DATE_TIME)) { return SiemensHvacBindingConstants.CATEGORY_CHANNEL_PROPS_TIME; - } else if (dpType.contains("TimeOfDay")) { + } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_TIME)) { return SiemensHvacBindingConstants.CATEGORY_CHANNEL_PROPS_TIME; - } else if (dpType.contains("Enumeration")) { + } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_SWITCH; - } else if (dpType.contains("RadioButton")) { + } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_RADIO)) { return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_SWITCH; - } else if (dpType.contains("Numeric")) { + } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_NUMBER; } else { return SiemensHvacBindingConstants.CATEGORY_CHANNEL_CONTROL_HEATING; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java index 53dae61f439d8..8ce33aac4e8b4 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java @@ -57,7 +57,7 @@ public class SiemensHvacBindingConstants { public static final String DPT_TYPE_ENUM = "Enumeration"; public static final String DPT_TYPE_NUMERIC = "Numeric"; public static final String DPT_TYPE_RADIO = "RadioButton"; - public static final String DPT_TYPE_DATE = "DateTime"; + public static final String DPT_TYPE_DATE_TIME = "DateTime"; public static final String DPT_TYPE_TIME = "TimeOfDay"; public static final String DPT_TYPE_SCHEDULER = "Scheduler"; public static final String DPT_TYPE_CALENDAR = "Calendar"; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java index 3a2aa788cd241..2f88844d0feaf 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java @@ -19,6 +19,7 @@ import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacHandlerImpl; import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacOZW672BridgeThingHandler; import org.openhab.core.config.core.Configuration; +import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.net.NetworkAddressService; import org.openhab.core.thing.Bridge; @@ -47,16 +48,19 @@ public class SiemensHvacHandlerFactory extends BaseThingHandlerFactory { private final HttpClientFactory httpClientFactory; private final SiemensHvacMetadataRegistry metaDataRegistry; private final ChannelTypeRegistry channelTypeRegistry; + private final TimeZoneProvider timeZoneProvider; @Activate public SiemensHvacHandlerFactory(final @Reference HttpClientFactory httpClientFactory, final @Reference SiemensHvacMetadataRegistry metaDataRegistry, final @Reference NetworkAddressService networkAddressService, - final @Reference ChannelTypeRegistry channelTypeRegistry) { + final @Reference ChannelTypeRegistry channelTypeRegistry, + final @Reference TimeZoneProvider timeZoneProvider) { this.httpClientFactory = httpClientFactory; this.metaDataRegistry = metaDataRegistry; this.networkAddressService = networkAddressService; this.channelTypeRegistry = channelTypeRegistry; + this.timeZoneProvider = timeZoneProvider; } @Override @@ -85,7 +89,8 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { metaDataRegistry); } else if (SiemensHvacBindingConstants.BINDING_ID.equals(thing.getThingTypeUID().getBindingId())) { SiemensHvacHandlerImpl handler = new SiemensHvacHandlerImpl(thing, - metaDataRegistry.getSiemensHvacConnector(), metaDataRegistry, channelTypeRegistry); + metaDataRegistry.getSiemensHvacConnector(), metaDataRegistry, channelTypeRegistry, + timeZoneProvider); return handler; } return null; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java index 42fcb57c0cbb4..173f64d6376ba 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -38,6 +38,7 @@ public abstract class SiemensHvacBridgeBaseThingHandler extends BaseBridgeHandle private @Nullable SiemensHvacDeviceDiscoveryService discoveryService; private final @Nullable HttpClientFactory httpClientFactory; private final SiemensHvacMetadataRegistry metaDataRegistry; + private @Nullable SiemensHvacBridgeConfig config; public SiemensHvacBridgeBaseThingHandler(Bridge bridge, @Nullable NetworkAddressService networkAddressService, @Nullable HttpClientFactory httpClientFactory, SiemensHvacMetadataRegistry metaDataRegistry) { @@ -58,9 +59,15 @@ public void handleCommand(ChannelUID channelUID, Command command) { @Override public void initialize() { + config = getConfigAs(SiemensHvacBridgeConfig.class); + metaDataRegistry.ReadMeta(); } + public @Nullable SiemensHvacBridgeConfig getBridgeConfiguration() { + return config; + } + @Override public void updateStatus(ThingStatus status) { super.updateStatus(status); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java new file mode 100644 index 0000000000000..796e907a231be --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java @@ -0,0 +1,11 @@ +package org.openhab.binding.siemenshvac.internal.handler; + +import org.eclipse.jdt.annotation.Nullable; + +public class SiemensHvacBridgeConfig { + + public @Nullable String baseUrl; + public @Nullable String userName; + public @Nullable String userPassword; + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index 42fef9e18ce3b..10860868bd10e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -15,7 +15,6 @@ import java.math.BigDecimal; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -28,6 +27,7 @@ import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataRegistry; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacCallback; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; +import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.PercentType; @@ -69,31 +69,25 @@ public class SiemensHvacHandlerImpl extends BaseThingHandler { private final @Nullable SiemensHvacConnector hvacConnector; private final @Nullable SiemensHvacMetadataRegistry metaDataRegistry; private final ChannelTypeRegistry channelTypeRegistry; + private final TimeZoneProvider timeZoneProvider; + private long LastWrite = 0; public SiemensHvacHandlerImpl(Thing thing, @Nullable SiemensHvacConnector hvacConnector, - @Nullable SiemensHvacMetadataRegistry metaDataRegistry, ChannelTypeRegistry channelTypeRegistry) { + @Nullable SiemensHvacMetadataRegistry metaDataRegistry, ChannelTypeRegistry channelTypeRegistry, + final TimeZoneProvider timeZoneProvider) { super(thing); this.hvacConnector = hvacConnector; this.metaDataRegistry = metaDataRegistry; this.channelTypeRegistry = channelTypeRegistry; + this.timeZoneProvider = timeZoneProvider; } @Override public void initialize() { updateStatus(ThingStatus.UNKNOWN); - - scheduler.execute(() -> { - boolean thingReachable = true; - if (thingReachable) { - updateStatus(ThingStatus.ONLINE); - } else { - updateStatus(ThingStatus.OFFLINE); - } - }); - pollingJob = scheduler.scheduleWithFixedDelay(this::pollingCode, 0, 60, TimeUnit.SECONDS); } @@ -110,6 +104,7 @@ private void pollingCode() { for (Channel channel : chList) { ReadChannel(channel); } + updateStatus(ThingStatus.ONLINE); } private void ReadChannel(Channel channel) { @@ -193,10 +188,11 @@ public void DecodeReadDp(@Nullable JsonObject response, @Nullable String uid, @N } else if ("DayOfTime".equals(typer) || "DateTime".equals(typer)) { try { SimpleDateFormat dtf = new SimpleDateFormat("EEEE, d. MMMM yyyy hh:mm"); // first example - ZonedDateTime zdt = dtf.parse(value.getAsString()).toInstant().atZone(ZoneId.systemDefault()); + ZonedDateTime zdt = dtf.parse(value.getAsString()).toInstant() + .atZone(this.timeZoneProvider.getTimeZone()); updateState(updateKey, new DateTimeType(zdt)); } catch (ParseException ex) { - logger.debug("error decoding date!"); + logger.debug("Error decoding date : " + value.getAsString()); } } else { updateState(updateKey, new StringType(value.getAsString())); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java index 3386c8cd51945..42ac88088620f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java @@ -48,7 +48,6 @@ public SiemensHvacOZW672BridgeThingHandler(Bridge bridge, @Nullable NetworkAddre @Override public void initialize() { logger.debug("Initialize() bridge"); - super.initialize(); updateStatus(ThingStatus.ONLINE); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index b9057bdaeffdd..7b6304f190726 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -37,10 +37,9 @@ import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataDataPoint; import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataMenu; import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeBaseThingHandler; +import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeConfig; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacException; -import org.openhab.core.config.core.Configuration; import org.openhab.core.io.net.http.HttpClientFactory; -import org.openhab.core.thing.Bridge; import org.openhab.core.types.Type; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; @@ -68,9 +67,7 @@ public class SiemensHvacConnectorImpl implements SiemensHvacConnector { private @Nullable String sessionId = null; private @Nullable String sessionIdHttp = null; - private String baseUrl = ""; - private String userName = ""; - private String userPassword = ""; + private @Nullable SiemensHvacBridgeConfig config = null; protected final HttpClientFactory httpClientFactory; @@ -134,18 +131,6 @@ public void unsetSiemensHvacBridgeBaseThingHandler(SiemensHvacBridgeBaseThingHan this.hvacBridgeBaseThingHandler = null; } - public void setBaseUrl(String baseUrl) { - this.baseUrl = baseUrl; - } - - public void setUserName(String userName) { - this.userName = userName; - } - - public void setUserPassword(String userPassword) { - this.userPassword = userPassword; - } - @Override public void onComplete(@Nullable Request request) { lockObj.lock(); @@ -186,7 +171,7 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacCallback cb) private @Nullable ContentResponse executeRequest(final Request request, @Nullable SiemensHvacCallback callback) throws Exception { - request.timeout(240, TimeUnit.SECONDS); + request.timeout(60, TimeUnit.SECONDS); ContentResponse response = null; @@ -221,46 +206,38 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacCallback cb) } private void initConfig() throws Exception { - - Configuration config = null; SiemensHvacBridgeBaseThingHandler lcHvacBridgeBaseThingHandler = hvacBridgeBaseThingHandler; if (lcHvacBridgeBaseThingHandler != null) { - Bridge bridge = lcHvacBridgeBaseThingHandler.getThing(); - config = bridge.getConfiguration(); + config = lcHvacBridgeBaseThingHandler.getBridgeConfiguration(); } else { throw new SiemensHvacException( "siemensHvac:Exception unable to get config because hvacBridgeBaseThingHandler is null"); } - - if (config.containsKey("baseUrl")) { - baseUrl = (String) config.get("baseUrl"); - } - - if (config.containsKey("userName")) { - userName = (String) config.get("userName"); - } - if (config.containsKey("userPassword")) { - userPassword = (String) config.get("userPassword"); - } } private void doAuth(boolean http) throws Exception { logger.debug("siemensHvac:doAuth()"); initConfig(); - String baseUri = baseUrl; + + SiemensHvacBridgeConfig config = this.config; + if (config == null) { + throw new SiemensHvacException("Missing SiemensHvacOZW672 Bridge configuration"); + } + + String baseUri = config.baseUrl; String uri = ""; if (http) { uri = "main.app"; } else { - uri = String.format("api/auth/login.json?user=%s&pwd=%s", userName, userPassword); + uri = String.format("api/auth/login.json?user=%s&pwd=%s", config.userName, config.userPassword); } final Request request = httpClient.newRequest(baseUri + uri); if (http) { - request.method(HttpMethod.POST).param("user", userName).param("pwd", userPassword); + request.method(HttpMethod.POST).param("user", config.userName).param("pwd", config.userPassword); } else { request.method(HttpMethod.GET); } @@ -349,44 +326,44 @@ private void doAuth(boolean http) throws Exception { doAuth(false); } - try { - String baseUri = baseUrl; + SiemensHvacBridgeConfig config = this.config; + if (config == null) { + throw new SiemensHvacException("Missing SiemensHvacOZW672 Bridge configuration"); + } - String mUri = uri; - if (!mUri.endsWith("?")) { - mUri = mUri + "&"; - } - if (mUri.indexOf("main.app") >= 0) { - mUri = mUri + "SessionId=" + sessionIdHttp; - } else { - mUri = mUri + "SessionId=" + sessionId; - } + String baseUri = config.baseUrl; - logger.debug("Execute request: {}", uri); - CookieStore c = httpClient.getCookieStore(); - java.net.HttpCookie cookie = new HttpCookie("SessionId", sessionIdHttp); - cookie.setPath("/"); - cookie.setVersion(0); + String mUri = uri; + if (!mUri.endsWith("?")) { + mUri = mUri + "&"; + } + if (mUri.indexOf("main.app") >= 0) { + mUri = mUri + "SessionId=" + sessionIdHttp; + } else { + mUri = mUri + "SessionId=" + sessionId; + } - c.add(new URI(baseUri), cookie); + logger.debug("Execute request: {}", uri); + CookieStore c = httpClient.getCookieStore(); + java.net.HttpCookie cookie = new HttpCookie("SessionId", sessionIdHttp); + cookie.setPath("/"); + cookie.setVersion(0); - logger.debug("Execute request: {}", uri); - final Request request = httpClient.newRequest(baseUri + mUri); - request.method(HttpMethod.GET); + c.add(new URI(baseUri), cookie); - ContentResponse response = executeRequest(request, callback); - if (callback == null && response != null) { - int statusCode = response.getStatus(); + logger.debug("Execute request: {}", uri); + final Request request = httpClient.newRequest(baseUri + mUri); + request.method(HttpMethod.GET); - if (statusCode == HttpStatus.OK_200) { - String result = response.getContentAsString(); + ContentResponse response = executeRequest(request, callback); + if (callback == null && response != null) { + int statusCode = response.getStatus(); - return result; - } + if (statusCode == HttpStatus.OK_200) { + String result = response.getContentAsString(); + + return result; } - } catch (Exception ex) { - logger.warn("siemensHvac:DoRequest:Exception by executing Request: {}: {} ", uri, ex.getLocalizedMessage()); - } finally { } return null; @@ -416,7 +393,7 @@ private void doAuth(boolean http) throws Exception { return null; } } catch (Exception e) { - logger.error("siemensHvac:DoRequest:Exception by executing jsonRequest: {} ; {} ", req, + logger.warn("siemensHvac:DoRequest:Exception by executing jsonRequest: {} ; {} ", req, e.getLocalizedMessage()); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index 575ad7ac262d6..c635b35b42b17 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -33,12 +33,19 @@ @NonNullByDefault public class UidUtils { - public static String sanetizeId(String st) { + /** + * The methods remove specific local character (like 'é'/'ê','â') so we have a correctly formated UID from a + * localize item label + * + * @param label + * @return the label without invalid character + */ + public static String sanetizeId(String label) { StringBuffer buffer = new StringBuffer(); - for (int i = 0; i < st.length(); i++) { - char c = st.charAt(i); + for (int i = 0; i < label.length(); i++) { + char c = label.charAt(i); if (c == 130) { } else if (c >= 232 && c <= 234) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml index 11bb3b4d88d38..b2a2fff286963 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml @@ -35,36 +35,43 @@ A numeric value + String A string with 16 characters + String A string with unlimited characters + TimeOfDay TimeOfDay + DateTime DateTime + RadioButton RadioButton + Scheduler Scheduler + Number:Temperature @@ -73,14 +80,16 @@ + Number:Temperature - Current setpoint temperature in degrees Celsius + Current setpoint temperature in degrees Temperature - + + Number diff --git a/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java b/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java new file mode 100644 index 0000000000000..19ea1b2495f00 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java @@ -0,0 +1,35 @@ +/** + * 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.siemenshvac.internal.type; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +/** + * @author Sönke Küper - Initial contribution + */ +@NonNullByDefault +@Disabled("These tests use the real website which may not always be available") +public class UidUtilsTest { + + @Test + public void testSanetizeId() throws Exception { + assertEquals(UidUtils.sanetizeId("Début heure été"), "Debut_heure_ete"); + assertEquals(UidUtils.sanetizeId("App.Ambiance 1"), "App_Ambiance_1"); + assertEquals(UidUtils.sanetizeId("Appareil d'ambiance P"), "Appareil_d_ambiance_P"); + } + +} From a61dd369e4ca8b6dbaadfc88797db2bb1ac49a06 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 31 Oct 2023 11:46:42 +0100 Subject: [PATCH 055/214] fix pom (jlaur review) Signed-off-by: Laurent ARNAL --- bom/openhab-addons/pom.xml | 5 +++++ bundles/pom.xml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index 4d8cdb95a40ae..5a075eef9e2a5 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -1566,6 +1566,11 @@ org.openhab.binding.shelly ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.siemenshvac + ${project.version} + org.openhab.addons.bundles org.openhab.binding.siemenshvac diff --git a/bundles/pom.xml b/bundles/pom.xml index 852fa9ee41016..cd0911d0efc08 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -349,8 +349,8 @@ org.openhab.binding.serialbutton org.openhab.binding.shelly org.openhab.binding.silvercrestwifisocket - org.openhab.binding.siemensrds org.openhab.binding.siemenshvac + org.openhab.binding.siemensrds org.openhab.binding.sinope org.openhab.binding.sleepiq org.openhab.binding.smaenergymeter From 7045d5fbb44aa5e9d90188f4884af746de601676 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 31 Oct 2023 15:18:41 +0100 Subject: [PATCH 056/214] fix bad state handling for Enumeration Signed-off-by: Laurent ARNAL --- .../internal/handler/SiemensHvacHandlerImpl.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index 10860868bd10e..d53a18a014bd0 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -311,11 +311,14 @@ private Command applyState(ChannelType tp, Command command) { BigDecimal maxb = sd.getMaximum(); BigDecimal minb = sd.getMinimum(); BigDecimal step = sd.getStep(); + boolean doMods = false; if (command instanceof DecimalType) { DecimalType bdc = (DecimalType) command; double v1 = bdc.doubleValue(); + if (step != null) { + doMods = true; int divider = 1; if (step.floatValue() == 0.5) { divider = 2; @@ -330,13 +333,17 @@ private Command applyState(ChannelType tp, Command command) { } if (minb != null && v1 < minb.floatValue()) { + doMods = true; v1 = minb.floatValue(); } if (maxb != null && v1 > maxb.floatValue()) { + doMods = true; v1 = maxb.floatValue(); } - result = new DecimalType("" + v1); + if (doMods) { + result = new DecimalType("" + v1); + } } } From 32edb71f56651ff44462ce9f99443f1f17314b5c Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 31 Oct 2023 15:25:01 +0100 Subject: [PATCH 057/214] fix enumeration underlying type Signed-off-by: Laurent ARNAL --- .../internal/Metadata/SiemensHvacMetadataRegistryImpl.java | 2 +- .../internal/constants/SiemensHvacBindingConstants.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index 6dd2104b9e7ad..6cff16d43fefd 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -544,7 +544,7 @@ public static String getItemType(SiemensHvacMetadataDataPoint dpt) { } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { return SiemensHvacBindingConstants.ITEM_TYPE_NUMBER; } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { - return SiemensHvacBindingConstants.ITEM_TYPE_ENUM; + return SiemensHvacBindingConstants.ITEM_TYPE_ENUMERATION; } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_DATE_TIME)) { return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_TIME)) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java index 8ce33aac4e8b4..27b8e4f69f51e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java @@ -47,7 +47,7 @@ public class SiemensHvacBindingConstants { public static final String ITEM_TYPE_CONTACT = "Contact"; public static final String ITEM_TYPE_STRING = "String"; public static final String ITEM_TYPE_NUMBER = "Number"; - public static final String ITEM_TYPE_ENUM = "Enum"; + public static final String ITEM_TYPE_ENUMERATION = "Number"; public static final String ITEM_TYPE_DIMMER = "Dimmer"; public static final String ITEM_TYPE_DATETIME = "DateTime"; From f46a6f0169b59020dd633f50b042d5635b7f0d7b Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 2 Nov 2023 08:32:56 +0100 Subject: [PATCH 058/214] review metadata reading to handle localization more properly and get english label Signed-off-by: Laurent ARNAL --- .../Metadata/SiemensHvacMetadata.java | 18 ++ .../Metadata/SiemensHvacMetadataMenu.java | 16 +- .../SiemensHvacMetadataRegistryImpl.java | 285 ++++++++++++++---- .../network/SiemensHvacConnector.java | 8 + .../network/SiemensHvacConnectorImpl.java | 15 +- 5 files changed, 284 insertions(+), 58 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java index a2170d9685790..723de41f1257e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java @@ -25,6 +25,8 @@ public class SiemensHvacMetadata { private int subId = -1; private int groupId = -1; private int catId = -1; + private String shortDescEn = ""; + private String longDescEn = ""; private String shortDesc = ""; private String longDesc = ""; @Nullable @@ -65,6 +67,22 @@ public void setCatId(int catId) { this.catId = catId; } + public String getShortDescEn() { + return shortDescEn; + } + + public void setShortDescEn(String shortDesc) { + this.shortDescEn = shortDesc; + } + + public String getLongDescEn() { + return longDescEn; + } + + public void setLongDescEn(String longDesc) { + this.longDescEn = longDesc; + } + public String getShortDesc() { return shortDesc; } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java index 749d3df69d591..cb4d4c143f635 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java @@ -23,17 +23,25 @@ */ @NonNullByDefault public class SiemensHvacMetadataMenu extends SiemensHvacMetadata { - private LinkedHashMap childList; + private LinkedHashMap childList; public SiemensHvacMetadataMenu() { - childList = new LinkedHashMap(); + childList = new LinkedHashMap(); } public void AddChild(SiemensHvacMetadata information) { - childList.put(information.getLongDesc(), information); + childList.put(information.getId(), information); } - public HashMap getChilds() { + public HashMap getChilds() { return this.childList; } + + public boolean hasChild(int Id) { + return this.childList.containsKey(Id); + } + + public SiemensHvacMetadata getChild(int Id) { + return this.childList.get(Id); + } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index 6cff16d43fefd..fb21687be6572 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -30,6 +30,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; +import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeConfig; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacCallback; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnectorImpl; @@ -91,7 +92,10 @@ public class SiemensHvacMetadataRegistryImpl implements SiemensHvacMetadataRegis private @Nullable SiemensHvacConfigDescriptionProvider configDescriptionProvider; private @Nullable SiemensHvacConnector hvacConnector; + private final HashMap userList; + public SiemensHvacMetadataRegistryImpl() { + userList = new HashMap(); } @Reference @@ -261,59 +265,95 @@ public void ReadMeta() { ArrayList lcDevices = devices; SiemensHvacConnector lcHvacConnector = hvacConnector; - if (root == null && lcHvacConnector != null) { - logger.info("siemensHvac:Initialization():Begin_0001"); + if (root != null) { + return; + } - logger.info("siemensHvac:Initialization():ReadCache"); - LoadMetaDataFromCache(); + if (lcHvacConnector == null) { + logger.info("SiemensHvacMetadataRegistryImpl:ReadMeta() : lHvacConnector not initialize."); + return; + } - logger.info("siemensHvac:Initialization():ReadDeviceList"); - ReadDeviceList(); + ReadUserInfo(); - if (root == null) { - logger.info("siemensHvac:Initialization():BeginReadMenu"); - root = new SiemensHvacMetadataMenu(); - ReadMetaData(root, -1); - lcHvacConnector.WaitNoNewRequest(); - lcHvacConnector.WaitAllPendingRequest(); - logger.info("siemensHvac:Initialization():EndReadMenu"); - } + SiemensHvacBridgeConfig config = lcHvacConnector.getBridgeConfiguration(); + if (config == null) { + logger.info("SiemensHvacMetadataRegistryImpl:ReadMeta() : config not initialize."); + return; + } - if (root != null) { - logger.info("siemensHvac:Initialization():BeginInitDptMap"); - InitDptMap(root); - logger.info("siemensHvac:Initialization():EndInitDptMap"); - } + SiemensHvacMetadataUser user = null; - int unresolveCount = UnresolveCount(); - // unresolveCount = 0; + String userName = config.userName; + if (userList.containsKey(userName)) { + user = userList.get(userName); + } - while (unresolveCount > 0) { - logger.info("siemensHvac:Initialization():BeginResolveDtpMap {}", unresolveCount); - ResolveDetails(unresolveCount); - lcHvacConnector.WaitAllPendingRequest(); - unresolveCount = UnresolveCount(); - logger.info("siemensHvac:Initialization():EndResolveDtpMap {}", unresolveCount); - } + if (user == null) { + logger.info("SiemensHvacMetadataRegistryImpl:ReadMeta() : cannot find user, aborting."); + return; + } - logger.info("siemensHvac:Initialization():SaveCache"); - SaveMetaDataToCache(); + logger.info("siemensHvac:Initialization():Begin_0001"); - logger.info("siemensHvac:Initialization():InitThing"); - getRoot(); - lcDevices = devices; - if (lcDevices != null) { - for (SiemensHvacMetadataDevice device : lcDevices) { - if (device.getType().indexOf("OZW672") >= 0) { - continue; - } + logger.info("siemensHvac:Initialization():ReadCache"); + // LoadMetaDataFromCache(); + + logger.info("siemensHvac:Initialization():ReadDeviceList"); + ReadDeviceList(); + + if (root == null) { + logger.info("siemensHvac:Initialization():BeginReadMenu"); + root = new SiemensHvacMetadataMenu(); + + ChangeLanguage(1); + ReadMetaData(root, -1, 1); + lcHvacConnector.WaitNoNewRequest(); + lcHvacConnector.WaitAllPendingRequest(); + + ChangeLanguage(user.getLanguageId()); + ReadMetaData(root, -1, 3); + lcHvacConnector.WaitNoNewRequest(); + lcHvacConnector.WaitAllPendingRequest(); + + logger.info("siemensHvac:Initialization():EndReadMenu"); + } + + if (root != null) { + logger.info("siemensHvac:Initialization():BeginInitDptMap"); + InitDptMap(root); + logger.info("siemensHvac:Initialization():EndInitDptMap"); + } + + int unresolveCount = UnresolveCount(); + // unresolveCount = 0; + + while (unresolveCount > 0) { + logger.info("siemensHvac:Initialization():BeginResolveDtpMap {}", unresolveCount); + ResolveDetails(unresolveCount); + lcHvacConnector.WaitAllPendingRequest(); + unresolveCount = UnresolveCount(); + logger.info("siemensHvac:Initialization():EndResolveDtpMap {}", unresolveCount); + } - generateThingsType(device); + logger.info("siemensHvac:Initialization():SaveCache"); + SaveMetaDataToCache(); + + logger.info("siemensHvac:Initialization():InitThing"); + getRoot(); + lcDevices = devices; + if (lcDevices != null) { + for (SiemensHvacMetadataDevice device : lcDevices) { + if (device.getType().indexOf("OZW672") >= 0) { + continue; } - } - logger.debug("siemensHvac:InitDptMap():end"); + generateThingsType(device); + } } + + logger.debug("siemensHvac:InitDptMap():end"); + } private void generateThingsType(SiemensHvacMetadataDevice device) { @@ -608,6 +648,132 @@ public static String getStatePattern(SiemensHvacMetadataDataPoint dpt) { return ""; } + public void ReadUserInfo() { + try { + SiemensHvacConnector lcHvacConnector = hvacConnector; + String request = "main.app?section=settings&subsection=user"; + + if (lcHvacConnector != null) { + String response = lcHvacConnector.DoBasicRequest(request); + + if (response != null) { + String st = response; + st = st.replace("\n", ""); + + Pattern pattern1 = Pattern.compile("table class=\\\"user_table\\\".*?>(.*?)<\\/table>"); + Matcher matcher1 = pattern1.matcher(st); + + if (matcher1.find()) { + String userTable = matcher1.group(1); + + Pattern pattern2 = Pattern.compile("(.*?)<\\/tr>"); + Matcher matcher2 = pattern2.matcher(userTable); + + int idx = 0; + while (matcher2.find()) { + String line = matcher2.group(1); + + if (idx > 0) { + Pattern pattern3 = Pattern.compile("(.*?)<\\/td>"); + Matcher matcher3 = pattern3.matcher(line); + + int idxCell = 0; + String userName = ""; + String userEdit = ""; + String userId = ""; + while (matcher3.find()) { + String cell = matcher3.group(2); + String header = matcher3.group(1); + + if (idxCell == 0) { + userName = cell; + } else if (idxCell == 5) { + userEdit = header; + } + idxCell++; + } + + if (userName.equals("")) { + continue; + } + + Pattern pattern4 = Pattern.compile("userid=(.+?)"); + Matcher matcher4 = pattern4.matcher(userEdit); + + if (matcher4.find()) { + userId = matcher4.group(1); + } + + SiemensHvacMetadataUser user = new SiemensHvacMetadataUser(); + user.setName(userName); + user.setId(Integer.parseInt(userId)); + + request = "main.app?section=settings&subsection=user&action=modify&userid=" + userId; + response = lcHvacConnector.DoBasicRequest(request); + + Pattern pattern5 = Pattern.compile( + "", Pattern.MULTILINE); + Matcher matcher5 = pattern5.matcher(response); + + if (matcher5.find()) { + String optionsList = matcher5.group(1); + + Pattern pattern6 = java.util.regex.Pattern.compile( + "", Pattern.MULTILINE); + Matcher matcher6 = pattern6.matcher(optionsList); + + while (matcher6.find()) { + String id = matcher6.group(1); + String opt = matcher6.group(2); + String lang = matcher6.group(3); + + if (opt.indexOf("selected") >= 0) { + user.setLanguage(lang); + user.setLanguageId(Integer.parseInt(id)); + } + + logger.info("aaaa"); + } + } + + userList.put(userName, user); + } + + idx++; + + } + } + } + } + } catch ( + + Exception e) { + logger.error("siemensHvac:ResolveDpt:Error during dp reading: {}", e.getLocalizedMessage()); + // Reset sessionId so we redone _auth on error + } + + logger.info("toto"); + } + + public void ChangeLanguage(int lang) { + try { + SiemensHvacConnector lcHvacConnector = hvacConnector; + String request = "main.app?section=settings&subsection=user&action=modify&userid=1&language=" + lang + + "&submit=OK"; + if (lcHvacConnector != null) { + lcHvacConnector.DoBasicRequest(request); + lcHvacConnector.ResetSessionId(false); + lcHvacConnector.ResetSessionId(true); + } + + } catch ( + + Exception e) { + logger.error("siemensHvac:ResolveDpt:Error during dp reading: {}", e.getLocalizedMessage()); + // Reset sessionId so we redone _auth on error + } + } + public void ReadDeviceList() { try { SiemensHvacConnector lcHvacConnector = hvacConnector; @@ -700,7 +866,7 @@ public void ReadDeviceList() { } } - public void ReadMetaData(@Nullable SiemensHvacMetadata parent, int id) { + public void ReadMetaData(@Nullable SiemensHvacMetadata parent, int id, int currentLanguage) { try { SiemensHvacConnector lcHvacConnector = hvacConnector; String request = "api/menutree/list.json?"; @@ -715,7 +881,7 @@ public void ReadMetaData(@Nullable SiemensHvacMetadata parent, int id) { public void execute(URI uri, int status, @Nullable Object response) { logger.debug("response for {}, status {}:", uri, status); if (response instanceof JsonObject) { - DecodeMetaDataResult((JsonObject) response, parent, id); + DecodeMetaDataResult((JsonObject) response, parent, id, currentLanguage); } else { logger.debug("error status {}: {}", uri, status); } @@ -732,7 +898,8 @@ public void execute(URI uri, int status, @Nullable Object response) { @SuppressWarnings("unused") private static int nbDpt = 0; - public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMetadata parent, int id) { + public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMetadata parent, int id, + int currentLanguage) { SiemensHvacConnector lcHvacConnector = hvacConnector; if (resultObj.has("MenuItems")) { if (parent != null) { @@ -744,15 +911,24 @@ public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMeta for (JsonElement child : menuItems) { JsonObject menuItem = child.getAsJsonObject(); - childNode = new SiemensHvacMetadataMenu(); - childNode.setParent(parent); - int itemId = -1; if (menuItem.has("Id")) { itemId = menuItem.get("Id").getAsInt(); } - childNode.setId(itemId); + SiemensHvacMetadataMenu menu = (SiemensHvacMetadataMenu) parent; + + if (menu.hasChild(itemId)) { + childNode = menu.getChild(itemId); + } else { + childNode = new SiemensHvacMetadataMenu(); + childNode.setId(itemId); + childNode.setParent(parent); + + if (parent != null) { + menu.AddChild(childNode); + } + } if (menuItem.has("Text")) { JsonObject descObj = menuItem.getAsJsonObject("Text"); @@ -772,6 +948,7 @@ public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMeta if (descObj.has("Id")) { subItemId = descObj.get("Id").getAsInt(); } + if (descObj.has("Long")) { longDesc = descObj.get("Long").getAsString(); } @@ -782,13 +959,15 @@ public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMeta childNode.setSubId(subItemId); childNode.setCatId(catId); childNode.setGroupId(groupId); - childNode.setShortDesc(shortDesc); - childNode.setLongDesc(longDesc); - if (parent != null) { - ((SiemensHvacMetadataMenu) parent).AddChild(childNode); + if (currentLanguage == 1) { + childNode.setShortDescEn(shortDesc); + childNode.setLongDescEn(longDesc); + } else { + childNode.setShortDesc(shortDesc); + childNode.setLongDesc(longDesc); } - ReadMetaData(childNode, itemId); + ReadMetaData(childNode, itemId, currentLanguage); } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java index d74a95ce6b3e8..fd58d0ad0d518 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java @@ -16,6 +16,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.api.Request; import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeBaseThingHandler; +import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeConfig; import com.google.gson.JsonObject; @@ -25,6 +26,9 @@ @NonNullByDefault public interface SiemensHvacConnector { + @Nullable + String DoBasicRequest(String uri) throws Exception; + @Nullable JsonObject doRequest(String req, @Nullable SiemensHvacCallback callback); @@ -37,4 +41,8 @@ public interface SiemensHvacConnector { public void onError(Request request, SiemensHvacCallback cb) throws Exception; public void setSiemensHvacBridgeBaseThingHandler(SiemensHvacBridgeBaseThingHandler hvacBridgeBaseThingHandler); + + public @Nullable SiemensHvacBridgeConfig getBridgeConfiguration(); + + void ResetSessionId(boolean web); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 7b6304f190726..3a8066cbde34c 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -216,6 +216,10 @@ private void initConfig() throws Exception { } } + public @Nullable SiemensHvacBridgeConfig getBridgeConfiguration() { + return config; + } + private void doAuth(boolean http) throws Exception { logger.debug("siemensHvac:doAuth()"); @@ -309,6 +313,7 @@ private void doAuth(boolean http) throws Exception { } } + @Override public @Nullable String DoBasicRequest(String uri) throws Exception { return DoBasicRequest(uri, null); } @@ -375,7 +380,6 @@ private void doAuth(boolean http) throws Exception { String response = DoBasicRequest(req, callback); if (response != null) { - JsonObject resultObj = getGson().fromJson(response, JsonObject.class); if (resultObj != null && resultObj.has("Result")) { @@ -459,4 +463,13 @@ public void AddDpUpdate(String itemName, Type dp) { updateCommand.put(itemName, dp); } } + + @Override + public void ResetSessionId(boolean web) { + if (web) { + sessionIdHttp = null; + } else { + sessionId = null; + } + } } From 8b46c04da12f358d8e33167a6d909df29ebb63d5 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 2 Nov 2023 08:34:21 +0100 Subject: [PATCH 059/214] remove comment on cache loading Signed-off-by: Laurent ARNAL --- .../internal/Metadata/SiemensHvacMetadataRegistryImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index fb21687be6572..ef7fe804bc9f4 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -297,7 +297,7 @@ public void ReadMeta() { logger.info("siemensHvac:Initialization():Begin_0001"); logger.info("siemensHvac:Initialization():ReadCache"); - // LoadMetaDataFromCache(); + LoadMetaDataFromCache(); logger.info("siemensHvac:Initialization():ReadDeviceList"); ReadDeviceList(); From e4845e307efc110cb66aaa9880377070cc54e057 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 2 Nov 2023 13:29:20 +0100 Subject: [PATCH 060/214] fix bad flow on HTML request remove extra debug information div fixes on metadata readings code cleanups Signed-off-by: Laurent ARNAL --- .../SiemensHvacMetadataRegistryImpl.java | 55 +++++++------- .../handler/SiemensHvacHandlerImpl.java | 4 +- .../network/SiemensHvacConnector.java | 5 ++ .../network/SiemensHvacConnectorImpl.java | 16 +++- .../network/SiemensHvacRequestListener.java | 75 +++++++++++++++++-- 5 files changed, 120 insertions(+), 35 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index ef7fe804bc9f4..65dd677cc9836 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -307,12 +307,12 @@ public void ReadMeta() { root = new SiemensHvacMetadataMenu(); ChangeLanguage(1); - ReadMetaData(root, -1, 1); + ReadMetaData(root, -1, false); lcHvacConnector.WaitNoNewRequest(); lcHvacConnector.WaitAllPendingRequest(); ChangeLanguage(user.getLanguageId()); - ReadMetaData(root, -1, 3); + ReadMetaData(root, -1, true); lcHvacConnector.WaitNoNewRequest(); lcHvacConnector.WaitAllPendingRequest(); @@ -333,7 +333,6 @@ public void ReadMeta() { ResolveDetails(unresolveCount); lcHvacConnector.WaitAllPendingRequest(); unresolveCount = UnresolveCount(); - logger.info("siemensHvac:Initialization():EndResolveDtpMap {}", unresolveCount); } logger.info("siemensHvac:Initialization():SaveCache"); @@ -731,8 +730,6 @@ public void ReadUserInfo() { user.setLanguage(lang); user.setLanguageId(Integer.parseInt(id)); } - - logger.info("aaaa"); } } @@ -745,14 +742,10 @@ public void ReadUserInfo() { } } } - } catch ( - - Exception e) { + } catch (Exception e) { logger.error("siemensHvac:ResolveDpt:Error during dp reading: {}", e.getLocalizedMessage()); // Reset sessionId so we redone _auth on error } - - logger.info("toto"); } public void ChangeLanguage(int lang) { @@ -785,7 +778,7 @@ public void ReadDeviceList() { JsonObject response = null; if (lcHvacConnector != null) { - response = lcHvacConnector.doRequest(request, null); + response = lcHvacConnector.doRequest(request); } JsonArray devicesList = null; if (response != null) { @@ -846,7 +839,7 @@ public void ReadDeviceList() { String request2 = "api/menutree/device_root.json?TreeName=Web&SerialNumber=" + SerialNr; if (lcHvacConnector != null) { - JsonObject response2 = lcHvacConnector.doRequest(request2, null); + JsonObject response2 = lcHvacConnector.doRequest(request2); if (response2 != null && response2.has("TreeItem")) { JsonObject tree = response2.getAsJsonObject("TreeItem"); @@ -866,7 +859,7 @@ public void ReadDeviceList() { } } - public void ReadMetaData(@Nullable SiemensHvacMetadata parent, int id, int currentLanguage) { + public void ReadMetaData(@Nullable SiemensHvacMetadata parent, int id, boolean localized) { try { SiemensHvacConnector lcHvacConnector = hvacConnector; String request = "api/menutree/list.json?"; @@ -881,7 +874,7 @@ public void ReadMetaData(@Nullable SiemensHvacMetadata parent, int id, int curre public void execute(URI uri, int status, @Nullable Object response) { logger.debug("response for {}, status {}:", uri, status); if (response instanceof JsonObject) { - DecodeMetaDataResult((JsonObject) response, parent, id, currentLanguage); + DecodeMetaDataResult((JsonObject) response, parent, id, localized); } else { logger.debug("error status {}: {}", uri, status); } @@ -899,7 +892,7 @@ public void execute(URI uri, int status, @Nullable Object response) { private static int nbDpt = 0; public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMetadata parent, int id, - int currentLanguage) { + boolean localized) { SiemensHvacConnector lcHvacConnector = hvacConnector; if (resultObj.has("MenuItems")) { if (parent != null) { @@ -959,7 +952,7 @@ public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMeta childNode.setSubId(subItemId); childNode.setCatId(catId); childNode.setGroupId(groupId); - if (currentLanguage == 1) { + if (!localized) { childNode.setShortDescEn(shortDesc); childNode.setLongDescEn(longDesc); } else { @@ -967,7 +960,7 @@ public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMeta childNode.setLongDesc(longDesc); } - ReadMetaData(childNode, itemId, currentLanguage); + ReadMetaData(childNode, itemId, localized); } } @@ -989,9 +982,6 @@ public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMeta nbDpt++; - childNode = new SiemensHvacMetadataDataPoint(); - childNode.setParent(parent); - int nodeId = -1; int dpSubKey = -1; boolean hasWriteAccess = false; @@ -1000,6 +990,19 @@ public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMeta if (dptItem.has("Id")) { nodeId = dptItem.get("Id").getAsInt(); } + + SiemensHvacMetadataMenu menu = (SiemensHvacMetadataMenu) parent; + + if (menu.hasChild(nodeId)) { + childNode = menu.getChild(nodeId); + } else { + childNode = new SiemensHvacMetadataDataPoint(); + childNode.setId(nodeId); + childNode.setParent(parent); + + menu.AddChild(childNode); + } + if (dptItem.has("Address")) { address = dptItem.get("Address").getAsString(); } @@ -1047,12 +1050,14 @@ public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMeta childNode.setSubId(subItemId); childNode.setCatId(catId); childNode.setGroupId(groupId); - childNode.setShortDesc(shortDesc); - childNode.setLongDesc(longDesc); - } - if (parent != null) { - ((SiemensHvacMetadataMenu) parent).AddChild(childNode); + if (!localized) { + childNode.setShortDescEn(shortDesc); + childNode.setLongDescEn(longDesc); + } else { + childNode.setShortDesc(shortDesc); + childNode.setLongDesc(longDesc); + } } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index d53a18a014bd0..edc9c4ae006cb 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -238,7 +238,7 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { } } else { if (lcHvacConnector != null) { - JsonObject js = lcHvacConnector.doRequest(request, null); + JsonObject js = lcHvacConnector.doRequest(request); DecodeReadDp(js, uid, dp, type); } } @@ -289,7 +289,7 @@ private void WriteDp(String dp, Type dpVal, String type) { if (lcHvacConnector != null) { logger.trace("Write request for: {} ", valUpdate); - JsonObject response = lcHvacConnector.doRequest(request, null); + JsonObject response = lcHvacConnector.doRequest(request); logger.trace("Write request response: {} ", response); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java index fd58d0ad0d518..e3c5ef2ab0284 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java @@ -29,6 +29,9 @@ public interface SiemensHvacConnector { @Nullable String DoBasicRequest(String uri) throws Exception; + @Nullable + JsonObject doRequest(String req); + @Nullable JsonObject doRequest(String req, @Nullable SiemensHvacCallback callback); @@ -45,4 +48,6 @@ public interface SiemensHvacConnector { public @Nullable SiemensHvacBridgeConfig getBridgeConfiguration(); void ResetSessionId(boolean web); + + public void DisplayRequestStats(); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 3a8066cbde34c..490711a97215a 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -110,7 +110,7 @@ public SiemensHvacConnectorImpl(@Reference HttpClientFactory httpClientFactory) this.httpClient = new HttpClient(ctxFactory); this.httpClient.setMaxConnectionsPerDestination(10); - this.httpClient.setMaxRequestsQueuedPerDestination(1000); + this.httpClient.setMaxRequestsQueuedPerDestination(10000); this.httpClient.setConnectTimeout(10000); this.httpClient.setFollowRedirects(false); @@ -179,8 +179,6 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacCallback cb) SiemensHvacRequestListener requestListener = null; if (callback != null) { requestListener = new SiemensHvacRequestListener(callback, this); - request.onResponseSuccess(requestListener); - request.onResponseFailure(requestListener); } try { @@ -216,6 +214,7 @@ private void initConfig() throws Exception { } } + @Override public @Nullable SiemensHvacBridgeConfig getBridgeConfiguration() { return config; } @@ -374,6 +373,11 @@ private void doAuth(boolean http) throws Exception { return null; } + @Override + public @Nullable JsonObject doRequest(String req) { + return doRequest(req, null); + } + @Override public @Nullable JsonObject doRequest(String req, @Nullable SiemensHvacCallback callback) { try { @@ -404,6 +408,12 @@ private void doAuth(boolean http) throws Exception { return null; } + @Override + public void DisplayRequestStats() { + logger.info("DisplayRequestStats : {} ({}/{})", (startedRequest - completedRequest), startedRequest, + completedRequest); + } + @Override public void WaitAllPendingRequest() { logger.debug("WaitAllPendingRequest:start"); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java index 46415f778e6ce..a01a4f11c1d86 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java @@ -12,6 +12,9 @@ */ package org.openhab.binding.siemenshvac.internal.network; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.api.Request; @@ -38,6 +41,14 @@ public class SiemensHvacRequestListener extends BufferingResponseListener implements SuccessListener, FailureListener, ContentListener, CompleteListener, QueuedListener, BeginListener { + private static Lock lockObj = new ReentrantLock(); + private static int requestListenerCount = 0; + private static int onSuccessCount = 0; + private static int onBeginCount = 0; + private static int onQueuedCount = 0; + private static int onCompleteCount = 0; + private static int onFailureCount = 0; + private static final Logger logger = LoggerFactory.getLogger(SiemensHvacRequestListener.class); private SiemensHvacConnector hvacConnector; @@ -54,10 +65,24 @@ public class SiemensHvacRequestListener extends BufferingResponseListener public SiemensHvacRequestListener(SiemensHvacCallback callback, SiemensHvacConnector hvacConnector) { this.callback = callback; this.hvacConnector = hvacConnector; + + lockObj.lock(); + try { + requestListenerCount++; + } finally { + lockObj.unlock(); + } + } @Override public void onSuccess(@Nullable Response response) { + lockObj.lock(); + try { + onSuccessCount++; + } finally { + lockObj.unlock(); + } if (response != null) { logger.debug("{} response: {}", response.getRequest().getURI(), response.getStatus()); } @@ -65,6 +90,12 @@ public void onSuccess(@Nullable Response response) { @Override public void onFailure(@Nullable Response response, @Nullable Throwable failure) { + lockObj.lock(); + try { + onFailureCount++; + } finally { + lockObj.unlock(); + } if (response != null && failure != null) { logger.debug("response failed: {} {}", response.getRequest().getURI(), failure.getLocalizedMessage(), failure); @@ -73,14 +104,44 @@ public void onFailure(@Nullable Response response, @Nullable Throwable failure) @Override public void onQueued(@Nullable Request request) { + lockObj.lock(); + try { + onQueuedCount++; + } finally { + lockObj.unlock(); + } + } @Override public void onBegin(@Nullable Request request) { + lockObj.lock(); + try { + onBeginCount++; + } finally { + lockObj.unlock(); + } + } + + public static void DisplayStats() { + logger.info("DisplayStats :"); + logger.info(" requestListenerCount : {}", requestListenerCount); + logger.info(" onSuccessCount : {}", onSuccessCount); + logger.info(" onBeginCount : {}", onBeginCount); + logger.info(" onQueuedCount : {}", onQueuedCount); + logger.info(" onCompleteCount : {}", onCompleteCount); + logger.info(" onFailureCount : {}", onFailureCount); } @Override public void onComplete(@Nullable Result result) { + lockObj.lock(); + try { + onCompleteCount++; + } finally { + lockObj.unlock(); + } + if (result == null) { return; } @@ -97,6 +158,7 @@ public void onComplete(@Nullable Result result) { if (content != null) { if (content.indexOf("") >= 0) { + hvacConnector.onComplete(result.getRequest()); callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), content); } else { JsonObject resultObj = null; @@ -128,26 +190,29 @@ public void onComplete(@Nullable Result result) { hvacConnector.onComplete(result.getRequest()); callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), resultObj); + return; } else { logger.debug("error: {}", subResultObj); hvacConnector.onError(result.getRequest(), callback); + return; } } else { logger.debug("error: invalid response from gateway, missing subResultObj:Success entry"); hvacConnector.onError(result.getRequest(), callback); + return; } } else { logger.debug("error: invalid response from gateway, missing Result entry"); hvacConnector.onError(result.getRequest(), callback); + return; } - - return; } + } else { + logger.debug("error: content == null"); + hvacConnector.onError(result.getRequest(), callback); + return; } - - callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), content); - hvacConnector.onComplete(result.getRequest()); } catch (Exception ex) { logger.debug("An error occurred", ex); } From 10e043b289e1bd4eb93d38720f59cf9e55aac2f9 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 2 Nov 2023 15:16:20 +0100 Subject: [PATCH 061/214] add comment about timeout Signed-off-by: Laurent ARNAL --- .../network/SiemensHvacConnectorImpl.java | 4 +++- .../network/SiemensHvacRequestListener.java | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 490711a97215a..2a2d83b83d817 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -171,7 +171,9 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacCallback cb) private @Nullable ContentResponse executeRequest(final Request request, @Nullable SiemensHvacCallback callback) throws Exception { - request.timeout(60, TimeUnit.SECONDS); + // Give a high timeout because we queue a lot of async request, + // so enqueued them will take some times ... + request.timeout(240, TimeUnit.SECONDS); ContentResponse response = null; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java index a01a4f11c1d86..d961802e6c1cf 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java @@ -57,6 +57,22 @@ public class SiemensHvacRequestListener extends BufferingResponseListener */ private final SiemensHvacCallback callback; + public static int getQueuedCount() { + return onQueuedCount; + } + + public static int getRequestListenerCount() { + return requestListenerCount; + } + + public static int getCompleteCount() { + return onCompleteCount; + } + + public static int getCurrentRunning() { + return requestListenerCount - onCompleteCount; + } + /** * Constructor * From a13c8bb22fc7ec0b37f01790cd09f0b92b595bd8 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 2 Nov 2023 21:19:20 +0100 Subject: [PATCH 062/214] add Metadata/SiemensHvacMetadataLanguage.java add Metadata/SiemensHvacMetadataUser.java class Signed-off-by: Laurent ARNAL --- .../Metadata/SiemensHvacMetadataLanguage.java | 41 +++++++++++++++++++ .../Metadata/SiemensHvacMetadataUser.java | 41 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java new file mode 100644 index 0000000000000..5767e9da4111d --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java @@ -0,0 +1,41 @@ +package org.openhab.binding.siemenshvac.internal.Metadata; + +public class SiemensHvacMetadataLanguage { + private String name; + private int id; + private String language; + private int languageId; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + public int getLanguageId() { + return languageId; + } + + public void setLanguageId(int languageId) { + this.languageId = languageId; + } + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java new file mode 100644 index 0000000000000..fbcc48d8662d8 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java @@ -0,0 +1,41 @@ +package org.openhab.binding.siemenshvac.internal.Metadata; + +public class SiemensHvacMetadataUser { + private String name; + private int id; + private String language; + private int languageId; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + public int getLanguageId() { + return languageId; + } + + public void setLanguageId(int languageId) { + this.languageId = languageId; + } + +} From 2cfec58b12daec925d51ee7ddcffb5b962db7b07 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 2 Nov 2023 21:41:39 +0100 Subject: [PATCH 063/214] review channel type handling : review channel type naming to have more meaningful name align automatic discovering and manual definition respect OpenHab naming conventions Signed-off-by: Laurent ARNAL --- .../siemenshvac/internal/type/UidUtils.java | 123 +++++++++++++----- .../main/resources/OH-INF/thing/ozw672.xml | 104 ++++++++------- 2 files changed, 142 insertions(+), 85 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index c635b35b42b17..97465af69af5b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -16,7 +16,6 @@ import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataDataPoint; import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataDevice; import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataMenu; -import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataPointChild; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; @@ -32,7 +31,6 @@ */ @NonNullByDefault public class UidUtils { - /** * The methods remove specific local character (like 'é'/'ê','â') so we have a correctly formated UID from a * localize item label @@ -127,45 +125,100 @@ public static ThingTypeUID generateThingTypeUID(String name) { } /** - * Generates the ChannelTypeUID for the given datapoint with deviceType, channelNumber and datapointName. + * get a more user friendly description from English short descriptor + * + * @param descriptor + * @return */ - public static ChannelTypeUID generateChannelTypeUID(SiemensHvacMetadataDataPoint dpt) { + private static String normalizeDescriptor(String descriptor) { + String result = descriptor.trim(); - String type = dpt.getDptType(); - String id = ""; - - if (SiemensHvacBindingConstants.DPT_TYPE_ENUM.equals(type)) { - StringBuilder builder = new StringBuilder(); - int idx = 0; - for (SiemensHvacMetadataPointChild child : dpt.getChild()) { - - if (idx > 0) { - builder.append("_"); - } - - String opt = child.getText(); - String[] subParts = opt.split(" "); - for (String subPart : subParts) { - if (subPart.length() > 0) { - builder.append(subPart.charAt(0)); - } - } - idx++; + if (result.indexOf("CC") >= 0 || result.indexOf("HC") >= 0) { + for (int idx = 0; idx < 4; idx++) { + result = result.replace("CC" + idx, "CC"); + result = result.replace("HC" + idx, "HC"); + } + } + + result = result.toLowerCase(); + + if (result.indexOf("history") >= 0) { + for (int idx = 0; idx < 20; idx++) { + result = result.replace("history " + idx, "history"); + } + } + result = result.replace(" mon", ""); + result = result.replace(" yue", ""); + result = result.replace(" wed", ""); + result = result.replace(" thu", ""); + result = result.replace(" tue", ""); + result = result.replace(" fri", ""); + result = result.replace(" sat", ""); + result = result.replace(" sun", ""); + result = result.replace(" mo", ""); + result = result.replace(" tu", ""); + result = result.replace(" we", ""); + result = result.replace(" th", ""); + result = result.replace(" fr", ""); + result = result.replace(" sa", ""); + result = result.replace(" su", ""); + + if (result.indexOf("holidays") >= 0) { + if (result.indexOf("firstd") >= 0) { + result = "holidays-hc-firstd"; } - String token = sanetizeId(builder.toString()); - - id = String.format("%s_%s", type, token); - } else if (SiemensHvacBindingConstants.DPT_TYPE_NUMERIC.equals(type)) { - id = sanetizeId(String.format("%s_%s_%s_%s_%s_%s", type, dpt.getDptUnit(), dpt.getMin(), dpt.getMax(), - dpt.getFieldWitdh(), dpt.getResolution())); - } else if (SiemensHvacBindingConstants.DPT_TYPE_STRING.equals(type)) { - id = String.format("%s_%s", type, dpt.getMaxLength()); - } else { - id = String.format("%s", dpt.getDptType()); + if (result.indexOf("lastd") >= 0) { + result = "holidays-hc-lastd"; + } + } + + result = result.replace("---", "-"); + result = result.replace("--", "-"); + result = result.replace('\'', '-'); + result = result.replace('/', '-'); + result = result.replace(' ', '-'); + result = result.replace("--", "-"); + + result = result.replace("standard-tsp-hc", "time-switch-program-standard"); + result = result.replace("standard-tsp-4", "time-switch-program-standard"); + result = result.replace("tsp-3", "time-switch-program-day"); + result = result.replace("tsp-4", "time-switch-program-day"); + result = result.replace("setpointtemp", "setpoint-temp-"); + result = result.replace("rmtmp", "roomtemp"); + result = result.replace("roomtempfrostprot", "room-temp-frostprot-"); + result = result.replace("-setp", "-setpoint"); + result = result.replace("optg", "operating-"); + result = result.replace("-comf", "-comfort"); + result = result.replace("-red", "-reduce"); + result = result.replace("setp-", "-setpoint"); + result = result.replace("roomtemp-", "room-temp-"); + result = result.replace("-setpointhc", "-setpoint-hc"); + result = result.replace("setphc", "-setpoint-hc"); + + return result; + + } + + /** + * Generates the ChannelTypeUID for the given datapoint with deviceType, channelNumber and datapointName. + */ + public static ChannelTypeUID generateChannelTypeUID(SiemensHvacMetadataDataPoint dpt) { + String type = dpt.getDptType(); + String shortDesc = dpt.getShortDescEn(); + String result = normalizeDescriptor(shortDesc); + + if (type.equals("DateTime")) { + result = "datetime"; + } else if (type.equals("String")) { + result = "string"; + } else if (type.equals("TimeOfDay")) { + result = "datetime"; + } else if (type.equals("Scheduler")) { + result = "datetime"; } - return new ChannelTypeUID(SiemensHvacBindingConstants.BINDING_ID, id); + return new ChannelTypeUID(SiemensHvacBindingConstants.BINDING_ID, result); } /** diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml index b2a2fff286963..b428328af0372 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml @@ -30,69 +30,52 @@ - - Number - - A numeric value - - - - String - - A string with 16 characters - - - - String - - A string with unlimited characters - + - - TimeOfDay - - TimeOfDay - - - DateTime - - DateTime - - - RadioButton - - RadioButton - + + Number:Temperature + + Outside temp + Temperature + + + + - - Scheduler - - Scheduler - + + Number:Temperature + + Setpoint heat pump + Temperature + + + - - Number:Temperature - - Current temperature in degrees celsius - Temperature - - - - + Number:Temperature - - Current setpoint temperature in degrees + + Room temperature Comfort setpoint HC Temperature - + + Number:Temperature + + Room temp reduced setpoint heat circuit + Temperature + + + + + Number - + + Operating mode heat circuit @@ -103,5 +86,26 @@ + + Number + + Status heating circuit + Temperature + + + + + + + + + + + + + + + + From 00067b704ee1d2b71f1dd4f2bcf6436d1592fe43 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 2 Nov 2023 21:43:19 +0100 Subject: [PATCH 064/214] spotless::apply Signed-off-by: Laurent ARNAL --- .../Metadata/SiemensHvacMetadataLanguage.java | 1 - .../SiemensHvacMetadataRegistryImpl.java | 1 - .../Metadata/SiemensHvacMetadataUser.java | 1 - .../handler/SiemensHvacBridgeConfig.java | 1 - .../network/SiemensHvacRequestListener.java | 2 - .../siemenshvac/internal/type/UidUtils.java | 1 - .../main/resources/OH-INF/thing/ozw672.xml | 102 +++++++++--------- .../internal/type/UidUtilsTest.java | 1 - 8 files changed, 51 insertions(+), 59 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java index 5767e9da4111d..8a384a30bb724 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java @@ -37,5 +37,4 @@ public int getLanguageId() { public void setLanguageId(int languageId) { this.languageId = languageId; } - } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index 65dd677cc9836..14e101134579e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -352,7 +352,6 @@ public void ReadMeta() { } logger.debug("siemensHvac:InitDptMap():end"); - } private void generateThingsType(SiemensHvacMetadataDevice device) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java index fbcc48d8662d8..0cb112c3db7b7 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java @@ -37,5 +37,4 @@ public int getLanguageId() { public void setLanguageId(int languageId) { this.languageId = languageId; } - } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java index 796e907a231be..9e80b63d690d5 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java @@ -7,5 +7,4 @@ public class SiemensHvacBridgeConfig { public @Nullable String baseUrl; public @Nullable String userName; public @Nullable String userPassword; - } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java index d961802e6c1cf..4788210583034 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java @@ -88,7 +88,6 @@ public SiemensHvacRequestListener(SiemensHvacCallback callback, SiemensHvacConne } finally { lockObj.unlock(); } - } @Override @@ -126,7 +125,6 @@ public void onQueued(@Nullable Request request) { } finally { lockObj.unlock(); } - } @Override diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index 97465af69af5b..01fd6750538de 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -197,7 +197,6 @@ private static String normalizeDescriptor(String descriptor) { result = result.replace("setphc", "-setpoint-hc"); return result; - } /** diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml index b428328af0372..d4b74d87e91e0 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml @@ -31,29 +31,29 @@ - - - + + + - Number:Temperature - - Outside temp - Temperature - - - - - + Number:Temperature + + Outside temp + Temperature + + + + + - Number:Temperature - - Setpoint heat pump - Temperature - - - - - + Number:Temperature + + Setpoint heat pump + Temperature + + + + + Number:Temperature @@ -62,16 +62,16 @@ - + - Number:Temperature - - Room temp reduced setpoint heat circuit - Temperature - - - - + Number:Temperature + + Room temp reduced setpoint heat circuit + Temperature + + + + Number @@ -86,26 +86,26 @@ - - Number - - Status heating circuit - Temperature - - - - - - - - - - - - - - - - + + Number + + Status heating circuit + Temperature + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java b/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java index 19ea1b2495f00..05bd76b4b4643 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java +++ b/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java @@ -31,5 +31,4 @@ public void testSanetizeId() throws Exception { assertEquals(UidUtils.sanetizeId("App.Ambiance 1"), "App_Ambiance_1"); assertEquals(UidUtils.sanetizeId("Appareil d'ambiance P"), "Appareil_d_ambiance_P"); } - } From dcd979f0a51b9f4a45e2f7e4eca75df84d425952 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 2 Nov 2023 22:23:54 +0100 Subject: [PATCH 065/214] fix build errors Signed-off-by: Laurent ARNAL --- .../Metadata/SiemensHvacMetadataLanguage.java | 12 ++++++++++++ .../internal/Metadata/SiemensHvacMetadataUser.java | 12 ++++++++++++ .../internal/handler/SiemensHvacBridgeConfig.java | 12 ++++++++++++ .../internal/handler/SiemensHvacHandlerImpl.java | 2 +- 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java index 8a384a30bb724..f9a7eacbb1804 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.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.siemenshvac.internal.Metadata; public class SiemensHvacMetadataLanguage { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java index 0cb112c3db7b7..d365fba3af03c 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.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.siemenshvac.internal.Metadata; public class SiemensHvacMetadataUser { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java index 9e80b63d690d5..71afef2c223ab 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.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.siemenshvac.internal.handler; import org.eclipse.jdt.annotation.Nullable; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index edc9c4ae006cb..0a4de28460691 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -192,7 +192,7 @@ public void DecodeReadDp(@Nullable JsonObject response, @Nullable String uid, @N .atZone(this.timeZoneProvider.getTimeZone()); updateState(updateKey, new DateTimeType(zdt)); } catch (ParseException ex) { - logger.debug("Error decoding date : " + value.getAsString()); + logger.debug("Error decoding date : {}", value.getAsString()); } } else { updateState(updateKey, new StringType(value.getAsString())); From 550b571bc682aaba606dd32bbcb4c0a255052198 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 2 Nov 2023 22:23:54 +0100 Subject: [PATCH 066/214] fix build errors Signed-off-by: Laurent ARNAL --- .../internal/Metadata/SiemensHvacMetadataLanguage.java | 4 ++++ .../internal/Metadata/SiemensHvacMetadataUser.java | 4 ++++ .../siemenshvac/internal/handler/SiemensHvacBridgeConfig.java | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java index f9a7eacbb1804..affe9614d5c42 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java @@ -12,6 +12,10 @@ */ package org.openhab.binding.siemenshvac.internal.Metadata; +/** + * + * @author Laurent Arnal - Initial contribution + */ public class SiemensHvacMetadataLanguage { private String name; private int id; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java index d365fba3af03c..e75d0b16f3291 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java @@ -12,6 +12,10 @@ */ package org.openhab.binding.siemenshvac.internal.Metadata; +/** + * + * @author Laurent Arnal - Initial contribution + */ public class SiemensHvacMetadataUser { private String name; private int id; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java index 71afef2c223ab..b12b7bf6850b4 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java @@ -14,6 +14,10 @@ import org.eclipse.jdt.annotation.Nullable; +/** + * + * @author Laurent Arnal - Initial contribution + */ public class SiemensHvacBridgeConfig { public @Nullable String baseUrl; From a82ef01735a1e3ea6db4632db68dd21dc8e15aeb Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 2 Nov 2023 22:38:48 +0100 Subject: [PATCH 067/214] spotless:apply Signed-off-by: Laurent ARNAL --- bom/openhab-addons/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index 5a075eef9e2a5..3b99653a2c250 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -1570,7 +1570,7 @@ org.openhab.addons.bundles org.openhab.binding.siemenshvac ${project.version} - + org.openhab.addons.bundles org.openhab.binding.siemenshvac From d1b57502d818a1fa16ba5eb66d01bee1c80201eb Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Fri, 3 Nov 2023 17:50:09 +0100 Subject: [PATCH 068/214] review concurrency on SiemensHvacHandlerImpl Lower done update frequency to 5s fix state description in ozw672.xml Signed-off-by: Laurent ARNAL --- .../handler/SiemensHvacHandlerImpl.java | 20 ++++++++++++++++--- .../main/resources/OH-INF/thing/ozw672.xml | 10 +++++----- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index 0a4de28460691..2be90254cf48a 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -88,7 +88,7 @@ public SiemensHvacHandlerImpl(Thing thing, @Nullable SiemensHvacConnector hvacCo public void initialize() { updateStatus(ThingStatus.UNKNOWN); - pollingJob = scheduler.scheduleWithFixedDelay(this::pollingCode, 0, 60, TimeUnit.SECONDS); + pollingJob = scheduler.scheduleWithFixedDelay(this::pollingCode, 0, 5, TimeUnit.SECONDS); } @Override @@ -100,11 +100,20 @@ public void dispose() { } private void pollingCode() { + long start = System.currentTimeMillis(); var chList = this.getThing().getChannels(); for (Channel channel : chList) { ReadChannel(channel); } updateStatus(ThingStatus.ONLINE); + + SiemensHvacConnector lcHvacConnector = hvacConnector; + if (lcHvacConnector != null) { + logger.debug("WaitAllPendingRequest:Start waiting()"); + lcHvacConnector.WaitAllPendingRequest(); + long end = System.currentTimeMillis(); + logger.debug("WaitAllPendingRequest:All request done(): {}", (end - start) / 1000.00); + } } private void ReadChannel(Channel channel) { @@ -211,10 +220,10 @@ private void ReadDp(String dp, String uid, String type, boolean async) { try { lockObj.lock(); - logger.info("Start read : {}", dp); + logger.trace("Start read : {}", dp); String request = "api/menutree/read_datapoint.json?Id=" + dp; - logger.info("siemensHvac:ReadDp:DoRequest(): {}", request); + logger.debug("siemensHvac:ReadDp:DoRequest(): {}", request); if (async) { if (lcHvacConnector != null) { @@ -253,6 +262,11 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { private void WriteDp(String dp, Type dpVal, String type) { SiemensHvacConnector lcHvacConnector = hvacConnector; + + if (lcHvacConnector != null) { + lcHvacConnector.DisplayRequestStats(); + } + if ("-1".equals(dp)) { return; } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml index d4b74d87e91e0..e1029158cf85c 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml @@ -76,22 +76,22 @@ Number Operating mode heat circuit - + - + - + Number Status heating circuit Temperature - + @@ -104,7 +104,7 @@ - + From 8ef0f56cb19fbfdfea05daae855afd3fc3a3faf9 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sat, 4 Nov 2023 10:31:53 +0100 Subject: [PATCH 069/214] fix advanced channel handling review state information Signed-off-by: Laurent ARNAL --- .../SiemensHvacMetadataRegistryImpl.java | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index 14e101134579e..93c9a6ad29f2e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -53,6 +53,7 @@ import org.openhab.core.thing.type.ChannelGroupTypeUID; import org.openhab.core.thing.type.ChannelType; import org.openhab.core.thing.type.ChannelTypeBuilder; +import org.openhab.core.thing.type.ChannelTypeRegistry; import org.openhab.core.thing.type.ChannelTypeUID; import org.openhab.core.thing.type.StateChannelTypeBuilder; import org.openhab.core.thing.type.ThingType; @@ -86,6 +87,8 @@ public class SiemensHvacMetadataRegistryImpl implements SiemensHvacMetadataRegis private static final String JSON_DIR = OpenHAB.getUserDataFolder() + File.separatorChar + "jsondb"; + private @Nullable ChannelTypeRegistry channelTypeRegistry; + private @Nullable SiemensHvacThingTypeProvider thingTypeProvider; private @Nullable SiemensHvacChannelTypeProvider channelTypeProvider; private @Nullable SiemensHvacChannelGroupTypeProvider channelGroupTypeProvider; @@ -98,6 +101,15 @@ public SiemensHvacMetadataRegistryImpl() { userList = new HashMap(); } + @Reference + protected void setChannelTypeRegistry(ChannelTypeRegistry channelTypeRegistry) { + this.channelTypeRegistry = channelTypeRegistry; + } + + protected void unsetChannelTypeRegistry(ChannelTypeRegistry channelTypeRegistry) { + this.channelTypeRegistry = null; + } + @Reference protected void setSiemensHvacConnector(SiemensHvacConnector hvacConnector) { this.hvacConnector = hvacConnector; @@ -394,7 +406,10 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { ChannelTypeUID channelTypeUID = UidUtils.generateChannelTypeUID(dataPoint); ChannelType channelType = null; - if (lcChannelTypeProvider != null) { + + if (channelTypeProvider != null && lcChannelTypeProvider != null) { + ChannelType channelTypeFromThingDesc = channelTypeProvider + .getInternalChannelType(channelTypeUID); channelType = lcChannelTypeProvider.getInternalChannelType(channelTypeUID); if (channelType == null) { @@ -490,29 +505,29 @@ private ChannelType createChannelType(SiemensHvacMetadataDataPoint dpt, ChannelT BigDecimal max = new BigDecimal(dpt.getMax()); BigDecimal step = new BigDecimal(dpt.getResolution()); - stateFragment.withMinimum(min).withMaximum(max).withStep(step).withReadOnly(false); + stateFragment = stateFragment.withMinimum(min).withMaximum(max).withStep(step).withReadOnly(false); description = channelTypeUID.toString(); label = channelTypeUID.getId(); } else { - stateFragment.withPattern(getStatePattern(dpt)).withReadOnly(dpt.getWriteAccess() == false); + stateFragment = stateFragment.withPattern(getStatePattern(dpt)).withReadOnly(dpt.getWriteAccess() == false); } if (!options.isEmpty()) { - stateFragment.withOptions(options); + stateFragment = stateFragment.withOptions(options); } boolean isAdvanced = false; - if (label.contains("_Y")) { + if (channelTypeUID.getId().contains("-y")) { isAdvanced = true; } - if (label.contains("_K")) { + if (channelTypeUID.getId().contains("-k")) { isAdvanced = true; } - if (label.contains("Histo")) { + if (channelTypeUID.getId().contains("histo")) { isAdvanced = true; } - if (label.contains(" QX")) { + if (channelTypeUID.getId().contains("-qx")) { isAdvanced = true; } From c16c6dd6917211141d19e19ff8204e3e56a29f75 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 15 Nov 2023 08:47:08 +0100 Subject: [PATCH 070/214] fix typo on label Signed-off-by: Laurent ARNAL --- .../src/main/resources/OH-INF/thing/ozw672.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml index e1029158cf85c..8419e8dc307e8 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml @@ -18,13 +18,13 @@ User name of the Siemens Hvac gateway false - + Administrator User password of the Siemens Hvac gateway false - + password From 9e371181329b1ef31cd29d6c10989d9cb5accb08 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 15 Nov 2023 08:49:45 +0100 Subject: [PATCH 071/214] remove public on getInternalConfigDescription Signed-off-by: Laurent ARNAL --- .../internal/type/SiemensHvacConfigDescriptionProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java index 691b2935b47df..4186b11c84919 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java @@ -59,5 +59,5 @@ public interface SiemensHvacConfigDescriptionProvider extends ConfigDescriptionP * before */ @Nullable - public ConfigDescription getInternalConfigDescription(URI uri); + ConfigDescription getInternalConfigDescription(URI uri); } From 3a2432dd7887fd2d0ebff57cdc6205586e25bacf Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 15 Nov 2023 08:50:19 +0100 Subject: [PATCH 072/214] remove unnecessary ChannelTypeRegistry Signed-off-by: Laurent ARNAL --- .../Metadata/SiemensHvacMetadataRegistryImpl.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index 93c9a6ad29f2e..a99b784ba2313 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -53,7 +53,6 @@ import org.openhab.core.thing.type.ChannelGroupTypeUID; import org.openhab.core.thing.type.ChannelType; import org.openhab.core.thing.type.ChannelTypeBuilder; -import org.openhab.core.thing.type.ChannelTypeRegistry; import org.openhab.core.thing.type.ChannelTypeUID; import org.openhab.core.thing.type.StateChannelTypeBuilder; import org.openhab.core.thing.type.ThingType; @@ -87,8 +86,6 @@ public class SiemensHvacMetadataRegistryImpl implements SiemensHvacMetadataRegis private static final String JSON_DIR = OpenHAB.getUserDataFolder() + File.separatorChar + "jsondb"; - private @Nullable ChannelTypeRegistry channelTypeRegistry; - private @Nullable SiemensHvacThingTypeProvider thingTypeProvider; private @Nullable SiemensHvacChannelTypeProvider channelTypeProvider; private @Nullable SiemensHvacChannelGroupTypeProvider channelGroupTypeProvider; @@ -101,15 +98,6 @@ public SiemensHvacMetadataRegistryImpl() { userList = new HashMap(); } - @Reference - protected void setChannelTypeRegistry(ChannelTypeRegistry channelTypeRegistry) { - this.channelTypeRegistry = channelTypeRegistry; - } - - protected void unsetChannelTypeRegistry(ChannelTypeRegistry channelTypeRegistry) { - this.channelTypeRegistry = null; - } - @Reference protected void setSiemensHvacConnector(SiemensHvacConnector hvacConnector) { this.hvacConnector = hvacConnector; @@ -408,9 +396,6 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { ChannelType channelType = null; if (channelTypeProvider != null && lcChannelTypeProvider != null) { - ChannelType channelTypeFromThingDesc = channelTypeProvider - .getInternalChannelType(channelTypeUID); - channelType = lcChannelTypeProvider.getInternalChannelType(channelTypeUID); if (channelType == null) { channelType = createChannelType(dataPoint, channelTypeUID); From e681f8897c3c3337e47ad8f2bb2161f748fef810 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 15 Nov 2023 08:53:46 +0100 Subject: [PATCH 073/214] remove public on addChannelType Signed-off-by: Laurent ARNAL --- .../internal/type/SiemensHvacChannelTypeProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java index 34254a73cc5f5..b3e4b4ea15de9 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java @@ -29,7 +29,7 @@ public interface SiemensHvacChannelTypeProvider extends ChannelTypeProvider { /** * Adds the ChannelType to this provider. */ - public void addChannelType(ChannelType channelType); + void addChannelType(ChannelType channelType); /** * Use this method to lookup a ChannelType which was generated by the From 4fcefa29fbc7aa7ce2a9575e23d52958e366afdb Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 15 Nov 2023 08:58:22 +0100 Subject: [PATCH 074/214] remove unnecessary public Signed-off-by: Laurent ARNAL --- .../internal/type/SiemensHvacChannelTypeProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java index b3e4b4ea15de9..fe822e315eb13 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java @@ -44,5 +44,5 @@ public interface SiemensHvacChannelTypeProvider extends ChannelTypeProvider { * before */ @Nullable - public ChannelType getInternalChannelType(@Nullable ChannelTypeUID channelTypeUID); + ChannelType getInternalChannelType(@Nullable ChannelTypeUID channelTypeUID); } From 3460285e1a4057c6c880da92b87fd4049bbd45d0 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 15 Nov 2023 09:30:32 +0100 Subject: [PATCH 075/214] fix some typo in README.md Convert table to markdown format Signed-off-by: Laurent ARNAL --- .../org.openhab.binding.siemenshvac/README.md | 167 ++++-------------- 1 file changed, 35 insertions(+), 132 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/README.md b/bundles/org.openhab.binding.siemenshvac/README.md index 1207d4044c33a..29a7d217f8e4d 100644 --- a/bundles/org.openhab.binding.siemenshvac/README.md +++ b/bundles/org.openhab.binding.siemenshvac/README.md @@ -5,7 +5,6 @@ A typical system is composed of: ![Diagram](Diagram.png) - There's a lot of different HVAC controllers depending on model in lot of different PAC constructors. Siemens RVS41.813/327 inside a Atlantic Hybrid Duo was used for the developpement, and is fully supported and tested. @@ -15,10 +14,9 @@ You can also find this device in other types of heating systems: boiler or solar ![](doc/Albatros.jpg) -You will find some information about the OZW672.01 gateway on the Siemens web site : -[OZW 672 Page] +You will find some information about the OZW672.01 gateway on the Siemens web site: -([https://hit.sbt.siemens.com/RWD/app.aspx?rc=FR&lang=fr&module=Catalog&action=ShowProduct&key=BPZ:OZW672.01) +[OZW 672 Page](https://hit.sbt.siemens.com/RWD/app.aspx?rc=FR&lang=fr&module=Catalog&action=ShowProduct&key=BPZ:OZW672.01) With this binding, you will be able to: @@ -59,34 +57,11 @@ IP should have be discovered automatically via UPNP. ## Bridge Configuration - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterRequiredDefaultDescription
baseUrlyesThe address of the OZW672 devices
userNameyesAdministratorThe user name to log into the OZW672
userPassyesThe user password to log into the OZW672
- +Parameter | Required | Default | Description +----------------|----------------|----------------|------------------ +baseUrl | yes | | The address of the OZW672 devices +userName | yes | Administrator | The user name to log into the OZW672 +userPass | yes | | The user password to log into the OZW672 @@ -101,97 +76,25 @@ They are organized the same way as the LCD screen of your PAC device, by top lev Each channel are strongly typed, so for exemple, for heating mode, openhab will provide you with a list of choice supported by the device. - - - - - - - - - - - - - - - - - - - - - - - - - - -
ChannelDescriptionTypeUnitSecurity Access LevelReadOnlyAdvanced
controlBoilerApprovalSet Boiler Approval (`AUTO`, `OFF`, `ON`)StringR/Wtrue
controlProgramSet Program (`OFF`, `NORMAL`, `WARMWATER`, `MANUAL`)StringR/Wtrue
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Channel Type IDItem Type Description
NumericNumberHandle basic numeric value
StringStringa String
String_16Stringa String of length <= 16 char
TimeOfDayTimeOfDay
DatetimeDatetime
RadioButtonRadioButton
SchedulerScheduler
TemperatureNumberUse to handle reading of temperature
SetpointNumberHandle the setting of a temperature
RegimeNumberEnumeration for handling mode change
+Channel | Description | Type | Unit | Security Access Level | ReadOnly | Advanced +-----------------------|----------------------------------------------------------|---------------|----------|-------------------------|-----------|------------------- +controlBoilerApproval | Set Boiler Approval (`AUTO`, `OFF`, `ON`) | String | | | R/W | true +controlProgram | Set Program (`OFF`, `NORMAL`, `WARMWATER`, `MANUAL`) | String | | | R/W | true + + +Channel Type ID | Item Type | Description +-----------------------|------------------------------------------|---------------------------------------------- +Numeric | Number | Handle basic numeric value +String | String | a String +TimeOfDay | TimeOfDay | +Datetime | Datetime | +RadioButton | RadioButton | +Scheduler | Scheduler | +Temperature | Number | Use to handle reading of temperatur +Setpoint | Number | Handle the setting of a temperature +Regime | Number | Enumeration for handling mode change + ## Full Example @@ -203,7 +106,7 @@ Bridge siemenshvac:ozw672:local "Ozw672"@"Chaufferie" [ baseUrl="https://192.168 Thing RVS41_813_327 local "RVS41.813/327" @ "Chaudiere" [ ] { Channels: - Type Setpoint:temperature "Température" [ id="1726" ] + Type Setpoint:temperature "Temperature" [ id="1726" ] Type Regime:cc1 "CC1" [ id="1725" ] } @@ -213,13 +116,13 @@ Bridge siemenshvac:ozw672:local "Ozw672"@"Chaufferie" [ baseUrl="https://192.168 Items file `.items` ```java -String Boiler_State_Pump_HWS "HWS Pump State [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2259_PpeChargeECS" } -Number Boiler_State_HWS "HWS State [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:2032#2035_Etat_ECS" } -Number Flow_Temperature_Real "Flow Temparature Real [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2248_ValReelleTempDep_CC1" } -Number Flow_Temperature_Setpoint "Flow Temperature Setpoint [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2249_ConsTDepResultCC1" } -Number Hour_fct_HWS "HWS Hour function" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2263_HeuresFoncPompeECS" } -Number Nb_Start_HWS "HWS Number of start [%.1f]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2266_ComptDemarResEl_ECS" } -Number Thermostat_Temperature "Thermostat tempeature [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2246_TAmbAct_CC1" } -Number Thermostat_Setpoint "Thermostat setpoint [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:1724#1726_ConsConfort_TA_CC1" } -Number Heat_Mode "Heat mode [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:1724#1725_Regime_CC1" } +String Boiler_State_Pump_HWS "HWS Pump State [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2259_PpeChargeECS" } +Number Boiler_State_HWS "HWS State [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:2032#2035_Etat_ECS" } +Number:Temperature Flow_Temperature_Real "Flow Temparature Real [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2248_ValReelleTempDep_CC1" } +Number:Temperature Flow_Temperature_Setpoint "Flow Temperature Setpoint [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2249_ConsTDepResultCC1" } +Number Hour_fct_HWS "HWS Hour function" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2263_HeuresFoncPompeECS" } +Number Nb_Start_HWS "HWS Number of start [%.1f]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2266_ComptDemarResEl_ECS" } +Number Thermostat_Temperature "Thermostat tempeature [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2246_TAmbAct_CC1" } +Number Thermostat_Setpoint "Thermostat setpoint [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:1724#1726_ConsConfort_TA_CC1" } +Number Heat_Mode "Heat mode [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:1724#1725_Regime_CC1" } ``` From 81627e7e6531846351505d0c556aad478e425e3b Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 15 Nov 2023 09:52:47 +0100 Subject: [PATCH 076/214] review some typos in files Signed-off-by: Laurent ARNAL --- .../internal/Metadata/SiemensHvacMetadataRegistryImpl.java | 2 +- .../siemenshvac/internal/handler/SiemensHvacHandlerImpl.java | 2 +- .../internal/handler/SiemensHvacOZW672BridgeThingHandler.java | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index a99b784ba2313..c4aff8582b97d 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -895,7 +895,7 @@ public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMeta SiemensHvacConnector lcHvacConnector = hvacConnector; if (resultObj.has("MenuItems")) { if (parent != null) { - logger.debug("Decode menuItem for : {}", parent.getShortDesc()); + logger.debug("Decode menuItem for: {}", parent.getShortDesc()); } SiemensHvacMetadata childNode; JsonArray menuItems = resultObj.getAsJsonArray("MenuItems"); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index 2be90254cf48a..a0bf87b7330eb 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -201,7 +201,7 @@ public void DecodeReadDp(@Nullable JsonObject response, @Nullable String uid, @N .atZone(this.timeZoneProvider.getTimeZone()); updateState(updateKey, new DateTimeType(zdt)); } catch (ParseException ex) { - logger.debug("Error decoding date : {}", value.getAsString()); + logger.debug("Error decoding date: {}", value.getAsString()); } } else { updateState(updateKey, new StringType(value.getAsString())); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java index 42ac88088620f..baf96900dd1f5 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java @@ -29,7 +29,8 @@ import org.slf4j.LoggerFactory; /** - * The {@link IPBridgeThingHandler} is responsible for handling communication to Siemens Gateway using HTTP API + * The {@link SiemensHvacOZW672BridgeThingHandler} is responsible for handling communication to Siemens Gateway using + * HTTP API * interface. * * @author Laurent ARNAL - Initial contribution From eb84c905de10a71adcd03a69aa9e52e35b511522 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 15 Nov 2023 09:53:05 +0100 Subject: [PATCH 077/214] fix translations and type on thing file Signed-off-by: Laurent ARNAL --- .../main/resources/OH-INF/thing/ozw672.xml | 44 +++++++------------ 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml index 8419e8dc307e8..3041ba2e8526f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml @@ -30,37 +30,27 @@ - - - - Number:Temperature - - Outside temp + Temperature - - + - Number:Temperature - Setpoint heat pump + Setpoint Heat Pump Temperature - - + - Number:Temperature Room temperature Comfort setpoint HC Temperature - - + @@ -68,8 +58,7 @@ Room temp reduced setpoint heat circuit Temperature - - + @@ -94,18 +83,17 @@ - - - - - - - - - + + + + + + + + + - - + From 2eb899007cbc0b97a571e8b8029af4101c81d197 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 15 Nov 2023 09:58:44 +0100 Subject: [PATCH 078/214] mvn spotless:apply Signed-off-by: Laurent ARNAL --- .../src/main/resources/OH-INF/thing/ozw672.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml index 3041ba2e8526f..770ac7781d30f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml @@ -95,5 +95,5 @@
- + From 89663c1cd0eb95ad9dad665f603553fa015f4df7 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 15 Nov 2023 10:15:11 +0100 Subject: [PATCH 079/214] fix package naming conventions: Metadata Signed-off-by: Laurent ARNAL --- .../internal/Metadata/RuntimeTypeAdapterFactory.java | 2 +- .../internal/Metadata/SiemensHvacMetadata.java | 2 +- .../internal/Metadata/SiemensHvacMetadataDataPoint.java | 2 +- .../internal/Metadata/SiemensHvacMetadataDevice.java | 2 +- .../internal/Metadata/SiemensHvacMetadataLanguage.java | 2 +- .../internal/Metadata/SiemensHvacMetadataMenu.java | 2 +- .../internal/Metadata/SiemensHvacMetadataPointChild.java | 2 +- .../internal/Metadata/SiemensHvacMetadataRegistry.java | 2 +- .../Metadata/SiemensHvacMetadataRegistryImpl.java | 2 +- .../internal/Metadata/SiemensHvacMetadataUser.java | 2 +- .../discovery/SiemensHvacDeviceDiscoveryService.java | 4 ++-- .../internal/factory/SiemensHvacHandlerFactory.java | 2 +- .../handler/SiemensHvacBridgeBaseThingHandler.java | 2 +- .../internal/handler/SiemensHvacHandlerImpl.java | 4 ++-- .../handler/SiemensHvacOZW672BridgeThingHandler.java | 2 +- .../internal/network/SiemensHvacConnectorImpl.java | 8 ++++---- .../binding/siemenshvac/internal/type/UidUtils.java | 6 +++--- 17 files changed, 24 insertions(+), 24 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java index 18ec8edce8f3d..42f7598fb0f54 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java @@ -33,7 +33,7 @@ * com.google.gson.internal.{Streams,TypeAdapters,LazilyParsedNumber} * to avoid using the internal package. */ -package org.openhab.binding.siemenshvac.internal.Metadata; +package org.openhab.binding.siemenshvac.internal.metadata; import java.io.EOFException; import java.io.IOException; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java index 723de41f1257e..d4f80ddfba68e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.siemenshvac.internal.Metadata; +package org.openhab.binding.siemenshvac.internal.metadata; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java index 95972b402a26d..952c04cf23201 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.siemenshvac.internal.Metadata; +package org.openhab.binding.siemenshvac.internal.metadata; import java.util.ArrayList; import java.util.List; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java index 676cef38e25e5..592b02fe9ccf0 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.siemenshvac.internal.Metadata; +package org.openhab.binding.siemenshvac.internal.metadata; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java index affe9614d5c42..b38fceb03d194 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.siemenshvac.internal.Metadata; +package org.openhab.binding.siemenshvac.internal.metadata; /** * diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java index cb4d4c143f635..e4e53f1847421 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.siemenshvac.internal.Metadata; +package org.openhab.binding.siemenshvac.internal.metadata; import java.util.HashMap; import java.util.LinkedHashMap; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java index 139321e54e7c1..6e27e4dfc1b33 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.siemenshvac.internal.Metadata; +package org.openhab.binding.siemenshvac.internal.metadata; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java index 5ae702e1e0657..60f9aa075f7ee 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.siemenshvac.internal.Metadata; +package org.openhab.binding.siemenshvac.internal.metadata; import java.util.ArrayList; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index c4aff8582b97d..39d0fdcb69611 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.siemenshvac.internal.Metadata; +package org.openhab.binding.siemenshvac.internal.metadata; import java.io.File; import java.io.FileOutputStream; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java index e75d0b16f3291..ff0b4b7a05b33 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.siemenshvac.internal.Metadata; +package org.openhab.binding.siemenshvac.internal.metadata; /** * diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java index 0270a75af3604..f160db6438440 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java @@ -19,10 +19,10 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataDevice; -import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataRegistry; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeBaseThingHandler; +import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDevice; +import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataRegistry; import org.openhab.binding.siemenshvac.internal.type.UidUtils; import org.openhab.core.config.discovery.AbstractDiscoveryService; import org.openhab.core.config.discovery.DiscoveryResult; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java index 2f88844d0feaf..94a8fd53954c2 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java @@ -14,10 +14,10 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataRegistry; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacHandlerImpl; import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacOZW672BridgeThingHandler; +import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataRegistry; import org.openhab.core.config.core.Configuration; import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.io.net.http.HttpClientFactory; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java index 173f64d6376ba..41db1d9cb83a4 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -14,8 +14,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataRegistry; import org.openhab.binding.siemenshvac.internal.discovery.SiemensHvacDeviceDiscoveryService; +import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataRegistry; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.net.NetworkAddressService; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index a0bf87b7330eb..b84333c437c6e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -23,8 +23,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataDataPoint; -import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataRegistry; +import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint; +import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataRegistry; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacCallback; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; import org.openhab.core.i18n.TimeZoneProvider; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java index baf96900dd1f5..fa00fd9d91c07 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java @@ -17,8 +17,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataRegistry; import org.openhab.binding.siemenshvac.internal.discovery.SiemensHvacDeviceDiscoveryService; +import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataRegistry; import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.net.NetworkAddressService; import org.openhab.core.thing.Bridge; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 2a2d83b83d817..3d6cfc6dabf4f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -32,12 +32,12 @@ import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.util.ssl.SslContextFactory; -import org.openhab.binding.siemenshvac.internal.Metadata.RuntimeTypeAdapterFactory; -import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadata; -import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataDataPoint; -import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataMenu; import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeBaseThingHandler; import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeConfig; +import org.openhab.binding.siemenshvac.internal.metadata.RuntimeTypeAdapterFactory; +import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadata; +import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint; +import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataMenu; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacException; import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.types.Type; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index 01fd6750538de..3cbfec16f2143 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -13,10 +13,10 @@ package org.openhab.binding.siemenshvac.internal.type; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataDataPoint; -import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataDevice; -import org.openhab.binding.siemenshvac.internal.Metadata.SiemensHvacMetadataMenu; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; +import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint; +import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDevice; +import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataMenu; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ThingTypeUID; From 8ace7237085c43b17647dc8f502dd90cd0803bbb Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 15 Nov 2023 10:34:36 +0100 Subject: [PATCH 080/214] review coding style rules Signed-off-by: Laurent ARNAL --- .../Diagram.png | Bin 181348 -> 0 bytes .../org.openhab.binding.siemenshvac/README.md | 2 +- .../Metadata/SiemensHvacMetadata.java | 6 +- .../SiemensHvacMetadataDataPoint.java | 1 - .../Metadata/SiemensHvacMetadataLanguage.java | 8 ++ .../Metadata/SiemensHvacMetadataMenu.java | 2 +- .../Metadata/SiemensHvacMetadataRegistry.java | 2 +- .../SiemensHvacMetadataRegistryImpl.java | 135 +++++++++--------- .../Metadata/SiemensHvacMetadataUser.java | 8 ++ .../SiemensHvacDeviceDiscoveryService.java | 4 +- .../factory/SiemensHvacHandlerFactory.java | 5 +- .../SiemensHvacBridgeBaseThingHandler.java | 2 +- .../handler/SiemensHvacBridgeConfig.java | 2 + .../handler/SiemensHvacHandlerImpl.java | 34 +++-- .../network/SiemensHvacConnector.java | 8 +- .../network/SiemensHvacConnectorImpl.java | 32 ++--- .../network/SiemensHvacRequestListener.java | 5 +- .../siemenshvac/internal/type/UidUtils.java | 16 +-- 18 files changed, 136 insertions(+), 136 deletions(-) delete mode 100644 bundles/org.openhab.binding.siemenshvac/Diagram.png diff --git a/bundles/org.openhab.binding.siemenshvac/Diagram.png b/bundles/org.openhab.binding.siemenshvac/Diagram.png deleted file mode 100644 index 300cc2f85838738d1915c87989e2724fa6460279..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 181348 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ǤI0X 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

Tf%`~6m*Ej73A^xXEZ*!7Q zeQ74I%Rt5OK7MHTUxQB->hehdnoQOyaY#MJJk%gI=H-@MUYK zvor?!s}OHHJ~l@-N1|@ov8{R9Zo&M{O`S1^N}d__fETSk%O}rUcTVv$>B5DE#n~P@ z2y{6X-KBu{AfKynY?*3yw$c6WN#Lh6=7WdL$48Iv9K1&&#wP0?Gs;pBztn!CQVi2Sjs-H_;dfb9@Va7Wjz=mxWf;*pUZ)Mcvf&(2Q`buFQqi z&+2TH%UX6-oPBRIW^cBdQp5mn2B&thKIiH;VY6P~Q|SK2%FIdt>bs$4zr`rSJ_MEb zW~pTs%9{UYMl1GCgZvBc9xw){VQcXkDsi)CjbDB}$=~g;m`>lb=f$ksR{3uylB<_L zsyyd-x%UUtMwUFyLf4HLHQ#Am;~7QxP#LM5U9UO!oYLP3+tthQ;1^M8AL*#<3jif9 zbd*txhFC(gdM-8lO03nN?iI`bBv5>tlUp;0bgd&PuO}@F zA+h<=vU{4%IRvroV-`28rS130Ynj-4^ZQr^GrdJl#dwhvLMRLQ`ZP*q=qqLBM)t(e ztJ-@{%w^uy$Y;#4FP3-O1ooJQ^z7W1*yivlC|+R4K+f3I^WOVf(E`rKl$4Yauoroc zIc4^Suog$8i&c7NN^`o>tn$8#&`^o|TQSh+r)S$pUp?=FYv{hC)oUcc7|X6fdg2_~ z!0v%+jM_if99V8y(pUsqcy{J5Nw_e#}DBi}me3mt*?+ zfhQ>hdkOZg912xT@2Ekt*{>3C;#sN8UVhzC2u$`iF{eQw0Uutybk-~f&e$Z> z1mTMUySFlLdz_Lk3Oeu4Ctv*Bam`CN7RouBNOQ`tl26)eo1N7!BUzZOb;O3QQZfLG z2e_JO8j^6_*_qbO{u}UbOpL~D9&E5gbbTN*p239P%?+Qc_qXQ&u_jtDZyYKiYgEWb+t9$8O{S= z1zq9&oi*IH%70?~{jO~j>7Z_+GnQx^Mo_r>m?^j?x+2*h*_^vqt$IwDQGrJlCx z+sB<=y!V&nuy*O5+aS4mEA^yQ=Ju&uw3SO%WjA(04{jbeVF?RX$^c$eGtQTPar+G( zj0l`-8s0!3ytTm*E864Uas3dmWR%D|pMY`SWQ_x4%U;fowe{ zQ8hi0f?2hmnmDzAJ>Uz>X4&Vf*)zO`fn?FU#A?8=oQXP1+T}eFXgmwjJxc5_p^>G1 zjH79Oe?`5$YsgekV&WNVnRc?I>IysCHYhE^DpQ`u$7Jgfp}4>4f(t)_W1rQqqOHpM z3$bKT?*M19F6bfXDYNrEZBW8+6Lv_lb8Y!Y(HgbP4mIls7cyZEweACi;JzDx2q|zz z8~}?Y)YHURRWC0=tG~Kn-G2Z<5dQ22NgP|I#`mA?d;bu7eJ@r~PR+>ZqkAXBuq)ybgHF44L zScz|VaJ;v8dYmN*`^m4m)q+XyHx;&RX{~*mHJcBdqul#yn5!D_rcuA71oK@J(UL$3 zFIR`IfdOVpA702DFLPYMuU|?Z%IEq$|BdrWY+X2348`z#$ba|V{GMr@$~p(7^dev@l7 zl~O+RaPyQ!YK zqS)l!dxN@lYOGkn3VC44uKr~ASl4YWkXVTb*|32Vy@L^UCLfgZ{p)Cei*!0%iiJfo zX?UpwKTgZ7$GeJY1WLjY#UQ5pJ1ae*v<7TeFhlodg z%hV3Gkk;sS=Q@Lt3r8UpJh-|gUdR~(BI$V7$CQe7>xh8AWj*tmmT3_w$p9DURr%L-|?D&&!M-2lBidXH@jMR z&yihvRR(U2C>IeI<+dfeC~hlFG>t#5+Y1T zLm2-Z8>)muke!U~R85&0`r+GR@y{w_uvdXnkzMZM3mRfhiDN4>4m7Z+`1r`)T&>CB zWqNf`%TYcY1;aba!}Hh5nnDT7Szkv?(T>&0vAKe7?peu%_Ey(G%KT@;4yB9Md=8lM z-=oG({&=daLJZ`^(ONhMa;!ib_+tbX$e_9Zf#nlT(~`LL3{ss~qu^31B-3tgT2Ojq z`SAG@(x~P^#Ltq~NVXfVrle@?sOCAdAI+2AnX};Ya+F5JG$lsKuz1*ABI#RydyQ|m z=NFFORT$i{){NYkE>9W#dQb-bJ-CMcat4Ry4JX}FcIR{uoW^I0UXb$u2ug2g_n4{4 zfryKNCg74gQe`E~lyuV3E{h!cPXl~6wC`>&9T);szA(Bh&j`%^9gHXf=Q1?wWFIg96<*ykLXL2`cbiN|* z3Hz_cZ1-R3Ii(}b+ zH0jIzz5$+=u+AGSoBrBO`{3i%`nIJHE9kL;WLGebjKe+PSeQoe@$5`fO# z5(Bil4lTzFI=!8qkGyohmdSYOA)}kyq%msBd2-DZg?w9O>Y`p!P?c4kCL5VEZ1}?u zVE-*34MRqi6^+z5V!ebJ6Sb zJq!VN4@{dn?^+3{58q=3XD# zEvJ@ckwf|;^KT3ZxHfsTJ}JcZ)tE71$e(Uod~b*(Fb|s8nZ!%Qk^yj6oFy0Rx{qf; zV~4|4azp=a-WMz8+gH2Erxs3sjVoxEJ-3Rg|NhndsvghDFOL3CL&7uc*?6VgyD-6b zr!v)G#E%6Q#lH>IHOuL!gZ<@!e?@08b6s|`)` zmh~1iY0b@BTIC^VsjtxZZ`?g=8?`Xil6(e&uI>mhHBTo082o6heB|-6^s65-s?Qm= z^t4hnH(|e;;kyGxK6mLl1u!#fgz1CL8F?jHt$GJ z#fkYiUk1S&fq^rBtadmXaM-lm1a{$RKC| z1J9ubBI57x{P}arOshNZ-&B+B$IL4$Mz4b>j9wBu%w7^KInODV!nOHZ69xckI-~m5 zr1PA;)O6T!^5WupvdKy0U7PIo)qwS+-~s1$;y!uRz%@ycNptA4`6dSp7cZZTrQ4}S z@Hx1J`Qb5}W9HQ?_W8dt*>i^gp{fn=n~Co&-YH&BM}4B@b=8GxEqbK(8Uj^|YcRY^ z*2Dc1^uszC8W?G-tb>GsApU{>0&Tzytx`(Q=fPjna9Jn3U$}I5dw&dpvx=hFUoNEj z)*d8&+)D{ll2;>DT6Y3^I(}2QOun;n z1Co5^P zWy9J5AkkT2DoTzv4IK{45JD=t$|t?*B4+}Ao!-7Qn;$TUS0B zbQDze3G(PPy#CEUVldbVBIBS||LyY&#DLDbPqcSe2;f>@RK&}Ip{u}70TL*m=&B3+ zb^l%$hkCtPwp)VQSmeByQLZSBINFq*Va8(3{3jg`PO6dXda07!84@%RY)wk|`(2o4EwuaQ7_cTE5OzfM z&HApsUWyVJOq}T4qS4_14#<}~Mini{>2_@K^YgdC5)1+`iHs2hm>kuWRnGY(FXl&_ zX3C0V7V9*owP^#Bj?XrBpEWi2$%>inc^e$Z4EGF&A1ZxaWR)co?0c`S9(g{-t{Fwb zAsiya(g0)lAKP_f4c3GjSP!erUItErO?>rKM%wxjB@+e_2DYay2?H*~a6OhMEcjD~ zUw0hm+aoz|6;tx4>*wh{S0xuIlb09Zbw|=OH@2VVmzbcA#Rknv6gnG-v2`AR!pm|r zq&fee;tAAZffnWR7Gm`L1z(3Y8Q=Yfv*6mflYeatG+;UQ0bSB>pB7UGg!4-<4#a7OPe`_sBE zX<8{Is+;qGNzh$WXLc1MbhW}enUWKm|`}Ju$^)~cCQtG1bG6ggE9*dVO zK@He*rh_INI9^CPqZX@tY0_tz>dJ6m(JmhU!`438>ro%;sp7K`t2uNe(Bcawak66p zU{A>8&_#W^VP}0=AU=Jjotr4oVGpRe|mG5{6o(Nu#xQ8F^C!k zN6M2HnK*p0;O}gThWSvuWY`SYPRg5)4N3fPNyQYITvEJ6S;wB***_`KDE;YPo z?|NpCu8GdtGloJGVZ{!sU?@bUp^leOFR*()El|IpA?*sPRdcuBlQ~<=WTf}YW`)|k zbO|sL&MZ_w>JS!{VJSD=^7YG}TF^OyB{cbzxRnxOCjU`J>oeB0!%}lqqzjMEZxZwC ze^zvE`%kDu+yp*teu}ZMrf^o;HJfKSzz%f3Bs6qYQj*|nzIuJN!Yq*Ei znnOcuh^oQu8}rvcR$ALHyxhzgNcv-LgS5;^$V2rVVu!|nk?PAJe9IF_>PQJ}+Jn;d1n4M8tc_fhzhvez%Ij073FOPZBYxl=Nju-pc>EMk}I zdX2To`C3T5VdZN>ohsMUKy<`r&^z^jT3$}hj<094LCwU?1bXAv@*ySk)=O!SOEQQ@ zpv4&~T3-S;rwupr`+~n6u?!VpAiu}dhT_s;``$i&yzq~RyWZqMcT$u~#P&ap}P5o&#B0FBeqg_*9zNMPa|0c2E$wT+-ZCV|}h| z8-x`S_c4epf+|Uf6}Pt5JyFb;RF+oaL2RxalWf4sRHC3OX~)Z?;Dg!d(&Kukl%Zw?M*Lpkj7^9x-rXmxCOw*s;3OMvCVmkDvrWiKtri2++N&zjLu+kvH z{@LV-)m|;0-v; zC#yiD-4aU#Z_aNb$mE$>jJJEsC(tRuyyiLUz`(}37V41U_>%gE{KJl_;T^jr`8>?N zst0b@vTll>L>V!b9?r@}iQCvuE*^R;I@M#*!IDw~5lxf0ln>#55}{wc_nTB9eAl|U zMtMGR;H&%dww~W?*uMmy4Q8Ng6dXN!cp*P87GQVL`{&3i0jg_oUe=TNT0nOd+O!6W zO!lLQ{|u?neVI+=d@m?N!BCM-^m*i?@O!-9o$s&#v0tXLG>PdO)XC@rxsm^=msuDt z@4oa;NgS2X{Kr3pE&@FeH><7~=gas98V++y)p;qOVlHm(!RkHa-Qb1=H7vKvx0oq3 z{QR19O|$%G~@LVDbL^m14Q1X-57OOKB~RKS3l@=4^VBEmru zc-Z=#z@74B`G_o|?0+|IAqNg&Z!8ucEtoD`0}c75QW+6`Pq^%8XRGw6F{Sh<4t+BH zOyf6>{^UvTMgAdenQ5=7#nJR#Pbs3OGwk~GtFS9Ehojf!r1-oDlp*hbw{S)*X#5cE zua%(G0#{%75$6LNLmb~n5f(o&Nqs@k35jItz5SB(;Az}q`MFHfB`POzD{Y1lgh^iZ z&1S_SwU5d$)$~Ea1$0d{CEH zF0=}LHi{_$c9U&CA#Q_o`A=)yWND19rGAB^`~8VF+&`7VI1#%cN}r7)b*UX%KMsSe zeZKUNKKM&kbN(-UmM?UTRiNQ(!@98fm?>0hA(Yc6-J`TGmjk;;^dp5Fo;DJS0DMk1G znpl}nSYc{Qcu^3gK9J)rJJba1ojRt}Ym37nYZB^)AwJgN-FFc6rC;4#`=Dp$bEp#> z-2fcR{?eU2rWB8cqjnBt*cQw^W)9wbC`Ch{%kAEv48&LdU=D4qXDbB>ZT(>4YN?=x zst1mXPm?{5Kn;YPkJ&fcZTriqqr;Rr*eT?&dpA43h)-9$8V`MI7e`T=FGm9_%bzEh zuQc@n$4^Duq9#frp5zXZxF>35>T7*zy5{R_384!W?w6(~NZ?V&L^?I+d;D_Kh(Nu! zy2lzJ`A)8G#XAoyih(U5Ztb39m%gifLLDoNBxh2Nqxo*7i$~D;a#*-u_a_8_aAt$` zj3_xJB|&Cy`|^byVB-6#G2V-*?b{pHLP(GK)RSX7Ra2&EeWlS{E*wT?xh*T}D$C z$9t_!;mk742DZsID!vi;M)#DewL zya!t4L~2-_eUtF)CD@ z?ct3f8nQ&nzg#yGM&Fd{Eoq?kQvMf%*Og)7i&XLun?12u`@c=Ei<+%a7B?|3-B?$&!xNlv=ikr7yNuT$o zP`NG3h+k+^F2=0g1jm>j3AnZhq36_+(3_NFHAs&Gi0YGue|ZF7JSPYE_RTVN5c^^p zx+ZtFfC?+umkP^Peko&24>wPI_P|EQUqWi~CDN|I2z{^j2~ zB^J;A?YYkR2NGWbtW-GnrPzZqRi$-&P62IgMIHoY+lq!Cv5iOHOLp zJh9$_=^ZOH{?*t}F?3RAk>A`Jl`+7!Q4(NGj}MTFlbqw5v7&i~ z>lI;+1r5Dw8-R3ulXpM(2mHKbYz6YQKYv1yNaX)KM!iiGnbOzyoOmwiy3?d$W}C>m z(~OvEw=5r3?4dJ$u}gGrTn*QeikY`9zze|h zVwW0i;P-P2>yOIAJBnQ!0X?(=-cBvKVdWG{QlqJ^pz8dfBWa>#EdZBnpG*z;z zH%+zYP(*H0SGjKNl9`$yMd7%{_jg^8OwjC#yf?LiiY%|UHFQWKQQ8~*lDHo4`tw{s z%B~Oj#KHxKP3@j8VZ)N4zUP{$+u>N0{Wv3E+kV~+=Ywb#G}cUdF+s`^GwO|p5C>=Z zhP+K84pn(I;7qf^3ICsc!&`5h9y-2M<^!Zb81nQS1%5z3UHHlM+6jjFW3H0D$BB{R z%`en1xoXMT(I|{`bB|m`%ET?|jB71uY|2c`tTrv|wWqnxviUje88!+yUPh2Z)mv7;oWJvh>QLD$ zY$45#FjDRuI2HGw0()64@U+vR(zfwlWBPPDQLrwYxqA7OgOMKW>DETpNr47YS48#cjRFNt zUb|jF=?u}Jh`INc*u~2U*QFPQCbilWavFB;pKr%0c7}21EO9Bzn$q_9xy|o2eg!=P zQWrH(GAUUUu8ZB1rhyy(oss!2eE78n@q_CvQ^a8Q+G0=Ko%;Q32S{`AVsbu&5oo1- zs`b}^Rc{5$6Ihpbh?dvL1zhU#Y+ew zqUUP*m=H@_s}n@@wc-m|_Fk(CB?npQ{hAj#mhq+lJ;E>wSe;-S$HyZ9YRI9_A_@kz zREyVp31M}|aXST2@Cm$hBPy-n3HI^#SN}j3&e3+rnpP%nONEHS$M}WwF!Y#wsn^zy zgu^<;YY9HCpD^`XA|p(f97bthp)f6x@yvv0Gy)F+Nw_TMo0l^|##h!nzKb3k5;8l!FO_aqi(sE&-%H zShe>dzM6NqnGQAS%fw!9gv_j2F*W`54NuFED~`%!c>@+(QqfL=!_$FbEj0;V+pfBu+o_l&QI{(!+wv zR%!G1CJSn&j)3!yZM>y80-7%CfYmQ^pp)v>1@uQTOZ$S2&kDd04=PTCdb&*U;7Moa z?6_Bu1Opd51UKSdCXatr`O7F(|GWlV5QA{mNGNyH9c733$ZmQ%D}e)5KPeg?+DdtT z7Nd9KJj7INsKGmtRf>f$^Zo@h8}>dH+_cU|NkaKJI7kSjHPWSZ(U7%{Yww4?E8U$D zn))?S`GOF*s;cV$rz4KqF1We?cLAfaEo4*f<-fS5$&+E^zO)RX#_Z#Er&mqm9YRh# zw1JTn%Y$WJ($fV;Lx_g!0=8^ZiJp#3?=6hqkK?}=6_e~dzF60hXxTRs#j&oOQIv|! z6Ej0Y;_juQcWP>f-?WRXb+8}zUYtAhefXb3#M{=toFQhG9!i9RxsPIgHmM)V->-Dh zb`6z0ep7Rq=69+Z`Q*4fn}o)Vhb}Nm=_18#AGk zdsyFYVOjKW5*7;*5$`yGzYT7wf5l&y&uo|4FKh(Ot;&b@5m9<4(|&anRThylwzO%j z-)mrb@Gmy};MHvN`uFht&4Q9JCrTk95K_2`#gWoPpy^*e_1%k0I<83u32j<7QldUt zX?5W$gawrbwf&O7q{rY|ICt==Q^SD0z@8j_1Z6ng9*b+Hh^FzwO>7t&hcSF~#OD zAiM6{Fn!+%Q?O3h1~H?)*>CM@?~ht$EJjhDwgao>vo(IU2Bgb&5FL7)vp_>N z0(9NFgRUv7IkL>cYHt7H`2-?QXS6Hp0s{H*$_z~8MM^_F&-FtQ*FY(5p7HFsyYNWt za4c!8H}~&vzV>zw{e|&tj6bD(Z14G{+i{&F-Q!s5(KiFIdWK4kMx!}x{%qeWAEX3l ztLO=hM;%)QAOw9%dZ&j_pdpo`8v^T3xu9d3S@8_%oLzID!nrg(7#^01pJ5~G!` zb+zqHmrxxg{UbUB!q&(sUdJdB7JZ@0^_9hFmRk}>hi7;ko4DPDPF2N1we$Ix%Fz%# z2}`}g1AjDp4o&sF4wN`Jc}YPckJEVdW@8rMC7IzGQrH!%y$|Gd&&F8g0=e9BMknM$ z%00Ym#|B`rhAJfm30uS7qoy+dboaEszB7~EZEbB8fu@UE)2#L;1p=pVE(=_{=v^?V z4j{->Gos))N>Y`z{7*<&D)C^cpgm!!0QvOg$;n~XWaS4EefNpQFSxmj(<${B$Q5lP zrVAwHN7kkrn(h+gZA~L@&esNz=8|Tie(oC{N>aoK?XdjCrQX{`e^2v2qDYVJchb)y zo-*pa3+Hc}cv*WHr)#GB7DS372|?%SpEXxzef^+m_M6YEKAnFg_Q^$vWoRQ?@>&HK z-GF-~xC32I>7vnB$@rA&ca;d!t~1N_C(?%!!g1J9ocy!9yCd(`@5-OQoGGS~tCBu) zWtbU8@w$YNn3U`I{oQJBRSLH|>JtlhqP)+hD!9{rm-4KE0n~i_n6R)?oj808S4xhp z{W0oo669I4b~0aVOixhXaRReNd`!pv^B3U2HyL{bHM{*~-|(Fm*5F>C|Iw}ZjGHBQ zY<=Bo;`nLxuHPY6z`_?zLC^mC5p=@TvAMmi4N8{#odlQ5#i)!9(}Bj|EwIOn&7nk! z>#koB)*?-24<=J;oX;(50P?q=5RO`o9q7cj=vg;b_>h}I_1NVbS`}8`f+B!(@r0jcMBC^=%ez7IO^LK-E}ZmPzxA8t)Y`8$MH%Urwme#_abKld#firdDBJ~0*E zkk_@rbAuGWu_!{Yn7ja+Q(TxlA%jN{tkM~k<=Mce;6a>h}aqa!YPScpVp+kaL)o7X|hW`!zyy~ z)B{I4yFXEbc>~xSe4NuPAbC!E;!XCRANPK2AkiPrymYWzn144#of0m=zB`QEXuOp9wpI zR|2>6mch<2)nHdPbc_(x1|z|ntmbz}8Aeq`cMlA0kt+!#?r=R*QkLq&1bItO+;`dN zOL^lX{)f(DC4Zi_<*~dGR6JgQsr}ksLq9n9zZZBO14d6%@a-ZNxj7Nzch4}2Eavb% z`N9-#Oj8K+V6|9sNGkSBbnKwOw&ELNnFo)3Z)E~B<>Wn0$-Hhv-K5d6W>5L#bwfH9 zDCEa;K&xJyb=7ahmGU+wHg=a3-o|8SU0v#Q&s_<9kSR?)1g8Jqw5zM5f$+ExT_fDC zOF?$|sbho5-Q0#u5bGH)uLJ%&?^B)!PkDIH0n+T!4M|n;rOE80^s(h-mY!&4YD!AT z)X{^bSW;6#hvP@R$<=hrY<+*>)T#pO(^7VA*2K&6DmPzdc0C^imNXN$*|?KWTwhBN z)?NtC+6@r6{X>ET0Y6J6L^!A`&ZR`gUWDVx+>P&-Z1PD^)bc>bF&bQIW_)HM%6YN? zbJ14M0Ij_Enu=rOm_uDPzA5TmX?MzFW?ebaSfDaB!tw>^_}k#BUv+_z<>`j;FFiL4 zRCyD8=Dxm6*e$3oK&3oq)BwKRwAq9+I1J0VT)%n1qc-VBhNNg^lVtpEKWs}QKrx*i zZACwE0GB7h*)5&(xMo21(a%xTw;k$EWorM!qUza4eurh9Bpd?(AnXANQ#KT6g)e?I zJW~W9=E#@Mr61sB%O;*Bt{$k?lh;+2Cgb4O{|)`oU`&Jz=c^-8Y7gE}y6F9ZIXzw_ zIyyRL+*R5lem)pzhse(}zC7OeC(M2giN%J_zudQEJI%4Fh)a5o-FRtV<$wo2aX69A zN!VI*?lyldyFfun8Q%5bv4T5LhUzqVx#K|R8{8dnpv6T+mH#=O`QsA_L^5zE`TF@( z82@tNF6Fa(q9M?LL}F6nkg1HKbN+#q(Z1mHwlj{Eiv02(nYWXQf`B4hLfi(HqwI^+z#98)}r2D5bSpGMi z%(5#~ZwO`abIyz+{`9`Gu>25d!l+1LB4ty*+oLiMa!j;^w1|S37(Bi}Ah<0Vqb47><#9K_~`WBItRA0HP8@17qIG zSm#B&Yo67g1n=%scXj3Gpc_dk^J-(wwB2oeKoCShXfuWbyw#PR;usN(?wlbLH+Av? zFH^>mFSjo}>O%Fcv8Iopq>FcOn$eEjoz~QcBNqb(8V$!;{mY3I@xmSvzRAi4&c7T! zT@8MKQxrr#iQn)}{K`7xF1+F0W!b%&J(2guxAI=&OD!{4d65+K8 z)=XhWhbth_+fpJbhxveG8oayYzOozV{&JCPi>T4XttD zplwV~llwdTeZ$0@7%gnyF!D)=Q^z`9!D&LI0k#WrYDl@%WelIAC9xWxQc(anm zIRCk-<37vd&p@S!ioEj%ojqy~#)s#JW>Y*lQ$J^Z67(zK{Z)Ey=6O7Ug zL!+QcF^C9tA20{Biy^0&zm35kfG2ulFnNm)I>qnTl_YJPIM0v)C(mWCSA}ksncA*UYKT zQDsa4!W{ZaHQd0d>A}viG$-Fmxd8dy3ONP&02m$*tQJ1aP(5Wd^Y&a?cJ`_|#cm(G zhBc;99kUX`L2{I3;q8PEc>v&4u5ZQvAnH2{b^EBsZJ=D5m$bShz(;NdpYC4-hO&P9 z2r6T@K*3g0_#r6=quaiQ-B7zXROhwd_$m2pe3Q^?Nm5nS$glFXWo76S`i>Mt66f-% zPKRF-;ljEr=IYfgO%;AKb3p&0HCVt++xxh{9GC+NzU^$vazS@(2qnE%j~IQ?Ut~&~ zv|_CwktXAXy3ELSP{^f7gdF46HknJVoGkw+9EOko>h0^W&9E1vZzq2E3;oBpwwd9; zcQ|+Opg4nNZdfiiTU@_!=5RRFm3a1mbZ2@q{9b17Z^|JOmnLuCssVR?`kaOt`K0AL z;d!~O*#qb)?xJfo)8@SvN()8tf67niEk^H25b!3@M-JD6w8;5qP9pJ4+Xrn+Uzp(* zfjxWveQN=2dz9gaJ5lmUWMno|{8>}`OKnQ?9~3^k0LHh!u06cf#vp!$8toE=1FyBC z_a52cfFDgQ7EqJK8z*=lc}y0Nig zwb&ybXb6ee7-4J44vmz^R}z{64h}>etLmLANsfY&=4|sZjEB!AeqAMwb<-u$X6W;EoI(vf@$JpFVw_*dnvGXLw=M-?zSZLgT1;a;eqr8C|50 zog*nQcKznWNj-f4C!pfwqYGb32A?padtr4ph63m_%)i{)DOkHl%Havlmz&Q|iEi*B z<%%g+`_h7c=&14g>&&8ce=m+d{4Q**Ld4&@e2^qx3V9yL_h+X(Ix4> zq)sP4F~dM2xwRo++c@ECSjQkIM*^XaVRkYX3&T@=>I5{z%deo%-Gte?+F#IM{4Qkm;Wz^RUF|w}Vh|6#j11cF-cD*vw!y{&- zk|pyz9=ub|zsiF}p|-M!0^r+$^rnm|+_%aV^FY=S+2$3CVL4+$!hw?y zFBdLsHTp(JPYZv4cB7w=S(6(xuq&uDf4h0H+Rv-ZQ6Y!oU^HTdx&$^p;Lxiudzpk6 ziP?RR!*-^sFGWuG`I8L!(l;T2Sml5KIppaSm>cb8#hjk3$_raa>4;8M+E z+l7C2bJnkjC}?i+R|=z6$v|J3jpdMcvSXt6++|Hs_sdl(N4f>M)swBx%Xi03>S>s8 z+nQ64FHqTyzosON)q~wxJhTU-k33%s*Q`L_I)n87SoVLZBkC=jA#{Fu4Bk3^<>^mB zzn3KREhur35SAinzFIKq`t0E&pXAHJAYb?7xq!+o}Rt59(+B9h#C|&X%sRnEMI>Pbr}N{k?}6 zNQ$0#)4pG0hMBz(*bS`-3%p2|b}1LAqpjPiG93u3n#J&?-Ya8jnflS#c&!n^&4Y;N z;kHM)edkW|^^>oSxIPMqx>ha5_Cl;ambebT7tvZ8skN1&J)urHRO-8j2G>(YuQc`B z6r7YW7pZC%#^+#M4&ADfpPClv!$9_{|8{oCs?WxgNxNrZx!3VI@?^bhK*@Pp;kfc~ zR$~Ei8a+ML`mUnKJN#XrMW$J=Gw-hc?$(YQ8;~8HT~uTsE56N#KZW(TEk@69MV_?F z=3iUrW*)QN`4T8{o+k}0d~L9=^8}{MP9`dxkZ_pA@aZ#IOkvx~?IS4LBUYz-kLD7M zboNRDjWD#w^VeZ8tX4B47@cG140q$ruA4clY^uR>LZ{o$0}%{Z)mF;J5IIzoNzmHG ztbkHfQ>>wikiX9EXmWD%xS%i!5iC`Aw3(-?ID{^L2QxXZW2Qg)3 zi4PdUHnNryDKmlw*LA!Ee7Kn3KQQk9PL37Q$!w-By~?L{-%j!eq$+%?8%(z*}y!KvQ7unn>vQt!6xyCiGMD~`wWn7!=ofVgC*?uqY&-eEi zf80OrIInY_=P^N6V3c=mq0)0&Rw^#Z+`?%RQ|WL@6zLalj3%TYMlv8k7aZDTZKQMx ze3i*n?lI^*eoaT0HoHbSYFN&pjAU-lm%*?;Z|u5wwC?8?zy_Qs``!P}Z!zwSY?3d$ zYfvug-(t6C68TLzVv|bI)U}yq_^b3l5{K~wOei>!z1D6{2K{MJnNZcg^Fg6~j{|mV zi_4_!d3^Bi{Cvho;#D?N<@6vFX$lf~Q0lWS^J{$kfw;KR8Q{XVovZWOh>MSZYgjOj zsjOsjp4u)enLQ4~CNxDa6+e4sIhYu~>@FrV zE#uLzP}2E?_%9C)mUQn^1Jt*XCtUO#Cu|blLBJ%r7^lS~;=Iq98ZiA#>K(5Tt1orh z+}vboPuzYRLQH|H=lfPqW@>y~ly%yv#xq~f`CKFzLVNq+ZsA)q(>PErt2L9PyDD7& zL2=`;6SG(M6RC|AS{l0BTVU+h`-^2MnkLTJu!{T=83}#uKTzqRyT}H?F$q)#)U}SS zG5VTvSA$p44z!w-boYbNF=$mY2vewMg-R#{rt2Uve%KHDZF3t!Y*cf5akkK#?%<3IP$@9jW(cG7nJAWSGK0P!APQ>v_7{ zPKBVAr~gp+N%#~%Vm>=R!Y}8gWluTd4lWmWZm-I`fwh$fK0+N|l7w>DbMSN1eXhu& z7IT;Q3U{~<;U_FRSbM<9M^>@c-_`Jw^o!#2HWWHZOo0`-#`F5YXHqFrIrd41qO$jr^9-q?6C(z#wL?rhY1ea~Oetja0= zq`dVDR?;^Hjg;uk+n-YN?mOuPbeTI{TkMyhN|_ZXKDO70pMnHM zFB=aRg3X8JByi8ZUiOwc9D3{d`$HG}tnXeR)O3!Dwk>&m&BE=-%~_f&!aIX_Z0#8)#HB;xKf_ReL zIEyjuSnQ_ruS8MMYQh3(>D<*ZWXnvRXhz}gJC=&4aEAs0_cDLFapRL<(afDH$?QdR z;>Xgu1~$X~6y2$so&vI%Lu7@e!up9gJ;CeMBE)awi-(wozg{*+5Yy2Ca8?;Iy&}i* zVW--+a=e{89taNqU5k zQEbnO{fvHl^6e!d_$i5+5j4^FlTzDjc^&qHl^3lbIZ{-3D3%*a`3(x2BE*t)445ae z*8*^Ct#n3c#oBAyTey*P@W=#deE9y05a1zNU>(gYW0Ix(B|!({-l7T4?V-rumyQ}r z8Vq4>{{lr1cSg!`({UWSI>Ml}YM~U9#S|hbCi}DPU++09bdSrxCXHB z<>zzC_zG10=r~AS`PQ&lVW+PzW@g5WX|UHK<>SjIWE2BXQFWeGl_yU=<00C9W3f&^ zUT9Akc6BxW&(_uxKfjzvo|id6EZ^!Z->?rkjc;^OK4Q7$Jok;gAmv)GxmeyS)24wf0$Sj+31^SAXVxPYn(``8jZ|$1ZvRp=CZ_*FK&x(9yGU zp~GC9i)pv!g|mh6u)QJKwoLo7uQ-@esPV5=9!UGv@Y+jRxMB=v_9^|=U{cp$;^mR= z-^0xwf8K_q!mKT5A(6ChO{c0nil-g4!iX~iHrO~vinj^Q$8BKMsQK|mtnoj#?!@2~@e)wWkWf)20<}&-Gz<*kC={KYy`6fPm$uOl21toj zEtU#!zW;jOGot>?&;fxsI*MqXbp12wB%iQVO9QFC8u)OPCTymsv4v%gvUKjWnZSxn zUadK@zX2Be837`jn6>{WvV?AFDzUjn>Z-L1>DgZN!m8N{W8o(a6DFHkV2rYS2M_j! zmjZpyFa@H=Igy>wd0D~Z6#}py68z^S_}KQ4PQGNRSVD+%%GMrF6c7aUy~R$^7&NIN zSgYJ3I(YAAuX{47zv0zm)*zY)mo*g$gyM7ByEwgA)9`+aL4elTy9pVSn1d&idux$F zknpC5fP-fAyG|l=5MOf!=?5+J&?H^ZNS^<-{EDIGAUJ3+PfV#% zIPr5|p+3-nXCTqK2V(^CB}?~HOXIm7py*-g_iit}YX%lQ2NeS(MIMB2BUwf@(eD|y z>$a`FmG!j*`0)1IGYCJ#7;aPECo^OmGZtM2Fq+?;R%q=R4W5ym@nj7=r zC9x;*@$%B%%=9ti<35MS#~M!LAcX4OiWm@OW)7__{;CUng!7&VHbuvwH#gD zI$f*hx0E3n{vd8G^j2@C3!V`$fLzYk_k=aBnb3kB@?xB$JqeoDnDecoh66U`L8rei zlcDI0?iaxduD6E7 zh#;OEs8^~pBnapB>K%UAct{y2DoQ_$Dp4#6K3M z+s-XKpVpTP*XLFqSJ^H2q$|!pm2+{hv|8518V7K+S^M1j4T3psHcdjI8zqd~H-pVL zM*MP7MFe9r>^6p(C91U4L;2}o+u?>7^_@E(l9Hw((!{959Nu&IjDy17D?gu~{TXp9 zBPzGsL^Fjk^rYsIW=5x$NovDSd6O-I4#J{#aHpH-{Ssbr;`IkEh6lH6$~h;~CB*u0 z8byY!)}tAMw9F}@=P4s0r71O^mwtyCXi||qwRm3lYCH0H?RBVcI;}*-&V|Z{a-VUe z|H_sxD4wB%=RvgxngQa@(=v^?+3O$t)Wo4!1 z`o_1wRfQ{rE@|y(MJs^(KYvyOYHvuMyuCGT;ilfUQpn`e$qem=&F6KQKV#X|^;NY| zRKJXO9MV%<^{QVykZ_rf%t^4f0IF4`4i^dbvh;Nny`DETC=ko?3ehS1yzK!+kfJ1u zo^BoH-7NO@7f+cZd05{-gUg$EGXP%54~$A&Szo#yxDudz{2PXk{n!$|80AV~YEgi1uP`WSB zzMi*Qd>6@k&GJl5Z8xU@1_g>br=IzKM)T5O!M>-!-IxYTh^-nE2gMheR!D^Q$0^;* zhL>Kp>uO)EOl6fmu%r9YxHgqK*qvGUcA-SW3ezwir{p_-*X4~jolhV0JC-tSvRlu( z%6x7O*@To=Euq0!K_C?WX9n+_|3Uve1Rh%75HjdZ*ku~>s?Z1g5AK;jFc%jAnkml31l=3&Vp*_GzdtN*_1eGSbf1f5v&KZBL zL$K|_cP~kn_wI6+R%-|8DD^#>Pm@#RA`k8HpMPD4U({@<=?u0XZrDbANREl{Am*l` z54k`f6fGq4PDyOXr0}s7Ydtys`VL_n{GvwFV`lV+;_FP&ect{wdEHLnpBsr#bc+vLcavEPk@C};D??{meqd;58>%lW?eZ^lWLgR*+>ncOJd5$E9@74 zD&xQqUZHfT|?vQT2zA@nDOk;4JU_Py`D7z>QfyC zWCdGZy&ADC^mS8rm+VRb80_rolp)Djq0#X9g)Qf^Uo-SDZB0$O8@R=7XdR3-9mw%l zB3Ly8hGA2t4ONWA^<&J7Tc!55e3T=7Y_?5+o3FkN261vDI2rK}@J80@RDWBl1O9K+ z2+{`@@|qVq12>75Vw~xUN^nHDF+zraMZr^#ZuZc-z~%6hW>TS^F*Toc(B;vW+ihXM zgA&&7QV#_7(J(Xfvnd+5A!wP2zmUdip{b>*VMe?{f8tW04S?U`_Wf4x{39IGURnAJ zS(3QZ)(`NbsFRzqny@1YrG*7iXy{i$FG_Iy5mFKzJC3?{9?q_HXCq5Q=r}NE?P~wyf_#*nJyUUkAh%56V0)*TgI5Q(u#jA-4 z)3?r=Q?*#Zk+E=cE0@a&akbO=8JM8bq2CcBg!cf>@B)@@yVV0fcz0FM~-JVB`T-m?% zfkXKLQgF?jJ7PRU6bU&GR+VBiRup62yc7Yaj}%`uav$FiHcmUJ8hcXA z_Dj68wkLHobx>BaD-VfevBJ!bg!T&7(}lD72?xHM=2g@7;y{h%`14VfQwCkKR}e#< z>Lf>03`&7{Xdw#wLY1$6^fKpXFrg$uyaH|U2jtgZflz~T*E$e>!MlVgdVWKg>4?MY z3zyiahch07a<7)UvtnBIc-Ug76t2{Q%L}K#ck7?c35h2%<&NRr=TFSg4H*iXxW6K3 z_G4oq3bKTv&-cRK?J+n5%E#QBjQIXa4iMk^UGpQi6`2}6|+Ns%XtwPF?g=nfuaR6hVT)BQZj$DG>_e`8Fq_REjn)+*f~fE>z(*N=4+1K- zXz0bMbafAC?#MG#*mv1()!a*xkh^_9v+QKCz327X)P!(=A7|)Whx-x|l%GEXuajUB z5&1;TB4!=D4}+Oj0gqjh7CLrz(sM7@pYms!cS<*=1oA&Z)0d*k(Nx-#DdBm@9&h;%&h0@Z}hGBdCj5+ zeNJr3fiI~rig~oP4E=bjFR@RL7m+OZ5Ba{fxwSfLZ)V8GMl87wqLNo{{YYhUoLDfPqjk z&_iHCnFvNz-V#!Fb96ao9pwQ431$ripm%#70dk6$e@laP;n@O8Crfmj?P-A;>yO^S zPSF#WHDl-|Ej8yK#Ae}49pd;UzFQLGhduvPF~^y913GzCpA>YkpHu}DPrJTngrdF? zHZx;os3lhM059ldaQ6q=-TG0TPTp{q;4ao$DpyMuZAt_Md;O}m(R3w3jO^1Vj{&wp z8qCG_gR6+pul_D`bwA&*Y37v$9BN@2UObq7JngyA{P*YQF6Ix*+A^#=K_?-S;pH1$ zVWrlpGAr4$i^Y_$399Ogo<$*<*wmDch65m&zCroCygU%FLo=*#Zop zg_Ln)+yVK~+kHhB*oP(EI;ln_R@+Be?B$^ye2P=ECNUlpk$OfO{D*(+&M@wwmhzbY zU=mz$y&a2F02#+ktN3o)F1cV0)J9ZtarrIzx&f7dHG}32Dz8?R8R8Y`Qp&(6Pb{YR zoSLPcL_a6|zSA+N&m0wYuT}Sp#P8$!ou_tv{>p>sn7ud$c0mz}Cz&E36;efS=oV3Y z_?LU1WtlfHhNr)0`D=OEkW@fg=Jvafct%5}_;A|&O;P+~V*zg}sl?C)!p=ywE5}(o zQ-xM3T!SwL&cH6hMSF0Eiv*S~7bJ3r`x^=D>Seu_uPtz=eI$0P`tYr zuD9U?AWdOK^xzBswPpK217F{Fl5^mn7hRFE_SjG32@=01Bsm^FHg54U_2o)7pWzJm zQbAO`(w86TmnJCzObc9X=q8eHwF#-IvLU@L?yjzJZDeg=y&8iJ9!u_|r7Xzp z6-_*Z;3P8URcS!U!p2AXuJ&wmI_%bK(|tISN^bAMGxbJnW+DD68_;LNqv&fx@F4E; zLFAk=ronrZXbLq*j$&(EPzWz43Zgd|A0$m9MSV)RvrHAlcnj6|5Bj$Hch_P=ga*vaWIJ>J>fY2;^TD`6caB`B*tr*yM84ERP%Qr4&Hk?em}X=jKyE$B z90sAkit^%0TJ#=|2HbiHME3Olta?nW-D>Y3`h&%pm3s&U#Dw(zLud zJKHc_FOfvQL!_(t}n>e=J zZ{p6X4i1SY%hP^u&Q}_~e>eVGfWtA#Zf|?ZomFj46i=X`hT-4cT;fEGesrP~E6l!N{G>sXnBYPan29tOYH+MrE&si;*_N)0lRS53id|07d z?4*BM1Cj}FGmp9JK+6C%f4^%Qx8`_A>Ab_Mt{focRBZ*QED(YLS?ZwG!^oE**n1lr z`@Y&L5acyepgg?p)_*Jc&ZIFOu#!b+spMs;;-#yfC#vfVOA$RL@3L9{Iy`w#k`Z!1 z+`Bbl@{)ixfPb8x511OaagWGR;iRZBV)C=qL3SSPRETdpsmC0q{3E43=Pq!?7y8^9 zRuk5%Dbm`Fkhj@gNBNt;7ENO0$A3~$_}47z2*KfSFs%7(vj8Ar&OOHrjc*zWN|8KTT3kqC4J_5H}LsI(Z&E~Jkns%7Qg z{fJaKhKe--WrHo*hLh=(y9(ZX^69dqnZvpfXay?$(6`jLY2-K!B(gqgp?QThH#}rw zrK&%EG+Jift%J4jX9e)+kRRi+zP{D6ZvR|6F2pf^JrpsOM8kZ4VsB?+;j`EHtjcXt zR#x$+Bi0_5VciyDA)>am>H8ej9{1#@e(3K*%rO@WuKRV<49DgOZYc?UE5h67$V%dFr?Vkdo-|hBHrDEu=hp^o&KOWkqrX*%)YRWDKmN=M6I7Xy_A?szd#Xv)j(5^3zugb$)fjQ=?q5!-PpGNhK4(mYT9kgid{G1;;cFQq@Jdr##W$tBmcMBmW`1)aB6ZvW-|IAY zCxackaXYT4Nh}pPEti>FR}obsS$=J9+vgG3>boJqBQTGtEr)SlPu9TJ_CY-~8VykI zgC4-B6icH(Y;R|ebpY6xuU|QUC5?Ccpyr?=;lEAAW2S-HQI6b^iQXh_9Ud^o^?D86 z3Pq>vSO#hEeR-bVsjbSTKL#&FN=Obdy<-tmB{i5g)E3^R3VkIWqdE}!)U(}{_qPKz zEbt}e8CEFnl(^`baj$7h4)p=H(3ITqS{!OR_}K+Z!AM2Sp2D;alu!rWWsh7XXcZfj zeGr>aYV-twT$Wd~`nY=v6e`p2o(~3H>Feps?sq>Z_nZf;YEe}3CwR+UFsbG+|mcMf;q%-6Utd;coStM#^tB>>X=Wxl#XaurmN5 z!?nJ)h?cd zMRy(gB&~7PQgrNOB+Mu7qmj=i822w-5BFCzSbwV(WCml_3XzsKWb>Ch4Z9|^Pi^^! z1kocYnhqy-vZKC@Z=-*xF`BVkhj2P%Ox9o4%F3#*jTHnGS^!?`{?x&kXL@?(l0B7F zxL+LXBw5l`v;$)-W@{9HDO6~OjL-EP!p~q`n@t=Z8SbjjO5qv-wLLBz4{cF0+BfM%@o`1XfiQY_pluqj|}KQiQitvM8KZMooMb#-+?C5oQBj&>qC= z^RbQJPyYeQ;Tx}an`#{+>R2?4Bfg^|aRTdwkNG!rfP#gD)pDPPZ=&(QbX*2NO-BZrvAu-}A=?Uwo zn=JlXGN-(-mG!Nf2JdN&vK>xusHnz*Vh8aqXA85>X)lg?K(Zov9p>Ntw+4v% zr8^qlo>2@-#!@g|gZ(e6lK|9M7R#;XLx7c;rMsJhk5Cks#1x(ZY1@O5go@mFW7$ed z*t~_U5HB2;Vr_E}bW!*%)t@+g%EJ;44Ek$maP%cJ^;}ewd?@7XZmj3roZKIqsnNH8 z)7^Nz9rH?bh33AMq;c(pqur+)?5kxe`ccw>I(7run6H$kzQt#_A;a)kzef3SSypv7 zWG)gp_eO6J#b9jZ`wnkaOxuuGh#9j5HM_b{p0B_ z8_m6U-QA|?mE;wKUZUBcENfB?QlWO)7aU*h`~Ru-;n?COl4v+P+* z5w9vS51mi)95{i9M`G!?+Su>h>dL?D&|1H~b;i9WP-~fai!J#V=Xz@5fyJJG+0@wJ zJV^=jqxGW_!S+vL*rDlz57rAp^{UgxOUunYO(ZZQfGAjE&ZN`;B3cN8^Myur|HQ&h z^}NqSMhTOKbQL5+5@n2=0)&SXla-!GhC;?Nyqy~4wOMf4f6b$^f7QkJ88DnBduQaZ z1!%2_i>VtaIMh=n{G$`aApu?CKb<2njmGAE_`E`6KWg4sDE{S%YxEe-+&l%CC$+=& zHvlWyO2=!Lsd7>#3E$vyi43zuMj_|fQW|qfH^mcFkJNI~1JA=u;>F>oY>H1Es;wyz zK(27&G|GMIu$IA3A5$gQ>eD`ovhYTja2Fx}) z6IMq+R#h*U3|A5*cNFMdvhBSEn;G(T;$P7W11dWeiHjTcUoV39y#ZvqbLPuu z`G3zampahiyIUkoQE3%PXU~OYeGJcQAs>UNB^T29uU=vi)YcdT+7}OMy4&8s+nP{W3)6~YK#=5R~PU?i1R($+pG(O8gacb&J`JAV^;74SBo2Q=n_ppH^A+wF&1bCpEi9dhN3|s5nM#WL zK$iWt+G~F`kOd;6WDKs#oxcUp%U`ARg>}?xys`zYX6ji{w}3%d;6Qm!L_*C;Lr5d2 z53mxqP5T^VFQ;M$4Ys!j?EAL{eJ*QZt9EOQhYq@HIosPC@uwtzWFDt6_okY!nj4)5 z=gP#}N4d`a3yoD*k^6Tn^Mu05>TJ2r&+m=htu8MX=5-6DPm#7kXST8UT?Gc7TkCxr z%=#`RMMa;R!r+HpRe5C=;S~!{-_8mZzx`(re(skyfnzreA8=}5v+>5?&$m(lM5tMv zn@yS<0b1bvSGdvn`H#-Ur#zI8FJP&Go4jB4L!nbC`-(?n<9X*cbmu%NKiKU6fuGmm zlGJ7?!QLNu3?{3hiyWnu z?UM{tvh|v;J>3CvQ10!rwvse_XJ>M9y2vMlN^ zx(0Bzf6cboPrJJw_*n8g{M5h;z0}n|u9$MetmVuUY*vkoZ{1@5?|MK8*SwCfK$zx|K4gPK`1K*+ev>_O-UI58 z)vpk1t2c_cf40`Gi*op#*5F61>f?n0`u2(|a>Q6&5{geKi?&F$gxudrwV-HvWy*=g zVuo?mfdqMGxT2LlH@gHP)JEU_`pJF^ToQ;BZMgkWOc8Bk8e+G)5Shyw-B*?G2Ttn0 z$^kebbz-a=YznkmGDt^_$V>UyfcRGA7S@2ex9vRbak<&4<^X=@%|r>4cp|ti77&OQ z=R4=~zmPy?XdcAiioc!$t1Rlm&4D=?JwA7908EC+s6s13eaTq_Y3^a_-F6>jMQvqy40ti(bO7&pjX%EU76o`7FPaP z?I`>Q8ChKwdifArk<%7LQST$9ExRS(H*M>MS)7nw#iUtMxs z?34t32(s`YO%N37GR)ma+1JG#_GntuR3M4~%^r zMd|#CXTV_`?M2YyhJ^g{D42}Dd?nwR%ku;}@o zd+Ig!u6uk+S;3(D4FFzyoE^L`@S^6jm~chF@9v+IIAyLKGZCU(PyDWsUx9)~IkTVY z|9)VAx&pNOb(ksUf*ub6GUvKXJ=lL6DqESDfK>toZ5{$%w`VK+Kn=K1%07*#H4sAz zLOqRQT%`{YJK!p~z>=|6uw_2N7wuZ`nQ?u+^o~IoD0rA+k7biOSqeMArTpC}efCA?1nIC6iVyzGY^3m;#B3i#c? zkOEgA?$=-e8=Ey7n|~srp(Y`^5n*tdi$`56^vR zG}O0XLHt>sD`s6;ke2#!S4y>u>TX}2)Kby|j}=?uWuO#J#aO2r;CFz#_Pctso~D9{ z;#OM0 z)F|bGaT(wE_q=B05)Cy`kjQk$z>$&Wm#{0%$v!w3MNUq>iewMj2L|#6sOc(<8@5Ii zK#27}UTJ;`#P$JKT%JJr3VUD{5?8=nv_V|n7>y#eE!i>tmjZb;$2qq;p33cfYY?{G zOV7Quup11zH8G32fAyXaJII7v_5+TJV{jj^(E=BCJq6koVv3_ts5~?E*?y*|j0_{Z zE1JIh0sz^U{?_kw0v?DDK-&|&W+ z^x$)RJQo$~#bO(7>1cdnf=V53iw*0(QLYFk{r?$=pP#HXQ+K|hhi~8HYfL+w@GL(9 zfh3RtA;dVQQ2X?1+%^)3Gcm-mr0vmG?I*i5LhS5{5GTJ`i_1!(FA2}2cTaDj9mr0f zTk}pW-cpG=x4{qSDXszvMFGwYsKFCU&~&CtO`td2i40pQM1XoxI5Fzz51bWD#Z>gX z+dkJO?y8Sy^Kqy7zFR)74}Q)D?|B55Qy$R~iXQObQVuqt=+9mKTK#`Ewx2qi^O%{< zfFbJ)bfh+1C;fv$9V6Mj*%m({hr78zf%l*-5L*yJemQSlv~BFivCoHn#3iuQ35kp* zc1;V2TwPgy!>7vQ9UUFpHpiUHoW>X_Ad=(&t6idOl}=DW8gFhs113QRgkgJ0y?P&( zc^#SYPI$WLlb0hCFh3Fo!@NPQq#uln%^tWi>rex6OING(h9H!0qj=R(Zaz0mP>`IfxCruR3*8|U|~#YcF{qv&7; zm*HRjn)htcs`*(XFR=``Be+{3&~r-l8#wbj0ZDwF;!%>6*M zFXb_lg8cHMRIib}*XwDL!wN&|WT-~no(3K>N<>pv0nW)|`)FdRQcSx&-q`wG6>eOe z@7B)U2U_nL3oi0WuJcB}FEKQqaRr_!D71I<#5Kr&YbHOu%+JOv;cc7`Yq2LR6OoDC)AVC7PEOfJLr=4H%hR{n9C2^@0bRFs z-<5N4`Hkw8{iNUv2&+9`)AIXmSZHrwRu>g3Ko$48r=Nb0K?=mSNw{B8())JN5HS%JID+}d%64BCXq#ax_R+GChCiy##j?cEf;K%ltJ z?q<{v3I6j9d{lhoH2OHy^aofyK*XXMYrrY8E~52hER&xa=JToe>Z|iF*BW_HQH*_Y zj4;~UugM977+qHr1)Lb`IR;Fl&xQB(!~Mx1yqK-TeZ>Wg`vLb$?^n{$+FEeqkDU4e=N2m?nW(UsWRlQfinY5M~22_!S&B?&H`?zjdm3dXQ*eW85(`c}f z{@e|M{E*u(y|E?R|Y!>i~<*SU(iGUrB`0g7*Dr3IXcH9#aj< z|C8m&`Z_#3_Kt!{5^i%zbclp^-w!V5t4;qh;(fB1MYVSo&Ss<4VnI=#h zRrT4~3g|h2Y>@zh@q#X!i8_%W}-TclDF$lTK-ZZt-`hf0V%wKvv!K} z*Wdnc$Z>* z6XWD~#qN%%Kt4sZaBXEFDBiG2JIu@St+E(WHd6#x4AZ6n!^pgjuzO@HrXNNPm<%jK zy>Wb;yCIln%6;2 z)St~i%I3}ml{h?8Vl3I!S*wjsS{&Rr&fj!Ip_G+L!mTaXrSmBb-(z+&6{&)k_v+Zr zRu?pMtV`5d?ZBl+xZ#juQs3}=wrI_o0;PFqfXR1`2(1$wcVmosPypqs`A!Ki;6jkr zERq0)&ULL&X_rDmhb9s*P>OFniT9%UhbSdtPTx50Gv*zr5-xGO2PP09xJ4v%?$}l16 z{OCu_zi*AD|7Y=(i$!;~p-5xB_wYlZrcV&+V|yk59J^Th#XM~9?*KthSi|+R|ASh9 zA23!k4|%024KRuizEa;|UF`=HyB1=4(y^K?k1c>`(44-jcA889Aeqw2qllJ!Fjf`| zg3Fc8gf%>P#tl3Eq@6CuW;eODzyY{`TwQ1V-K%8(9UcF}R5j8^_4k7e$;lGQ94#CD((n+W zb~zdBlU#WAR?mqDg!#}z5zP`=LaGc#Muu5sJ%B-{74VT*g}i^ST3K27f$HOKQWHJQ zenkv-HxmdW;f5y58x^m}%n*{C2bU8xN>e>SdTTYd>~i^ne=7x-^TdN^aDdpbyjT+> zeNGKMuu$PDAVH`~0ax`vpyIDknqfT~P$`>F%W(Qle<%hf58BdPy!?B^OloeQdr_3u zN65xBVIB8ZOoQ;T!Ncs5S2~D+Wr=i}eL!WC_sveEMCsJSyqft9`*tS<(_YWGj>>GV zj>L%AlX9B8!tVvJSTvKa==vVfRY0zq9Y~jg`#c08U}ict%+rj?Lc zx_%k)PG%g@tGUIJ`OYS#BhLf)@hkIi%6xiYDpr)&5rwu2tb(5a(obLD0F_wPr@>xq z+5IQ2pCMWW)MR#+eVG>{Lr`?x-VXy{G9aGWylQxpJ1a|8pRD@8ehpW18olzQi3lIS z#68TJvf^B^|*nj|s5D zv#~waFJl~VE;OUY1WJOBN1jy zU$@;@MiWVbx1+*sttZdHalF-z2p|tOuurRi00DkOICKwc%Fuc@<6jcNP0B$4ft=%x z%TuKDC+Jvc+iGrMeq8tbY$_Kk#)91@F~2B}0n>Labk|gkUDz-Pt%LRyL{L?lFbNn}a* ze1xH^JbihxGr}&~fll;64k>EmEj&r^ZLBHKW|Z0Ih!_}~9TB_%@4gl(1y>7MKAmE3 z*(5S2=pn4i*M5fow4#5jM7^>3ZBS`_qCIFcp^>%6JuU(s|<)-)O zc`{scW1IUzT%6fDWV7$|?CcDQUg%}e<|6Ouz9R66)Qpt*WE z2fz`h(VYH=$I35@>PAAaU-W$|i8*6@2FwSa2K8hoom`pB`Z;`!6bq;YArifP0dGf@ z?R1a;Od@mFVjr3!EmZS7stp8^^ zK$yuAukcR7-LUI9oolN2E!!;Hf0+I#nLn-n)@Xwz3#p11x5`y_)cAwVwQO4j`uiZX zAYDR5!Y7BuminK7e9Q}I4q#V2ca>5eL$Y%11ZY4kB))trYdJ}Dx(z5o&rl2 zHdBG%6Rx3^{bMX#RIh5(6g8XW^+ueJ<1vEVs$ z|GgG-SLGcteyaD~ITs_2jt6<(e=tYA>Wyw^WqevgIHK48DhG<>V2!5V2fQN+l@Mc{ zgE1FiB~8spm%jDHeGV@AjTak$D4!K5;LWLVpuDCi`_Ew?zHPx8R>!Cw14MjACU^sDX9qFzWxA6)%U zN}5tjojT^>6TT3>rHB7&*a?4M-_VMYA6G=vbVka%IeC3ehGZ zu_EhD5!HBP%Bh7a-+qPeIEl1ZcXWJxrk3=C3Okar^O^6xp{cM(u14aCfW=%WHZ#^EppKdh_d0MNaQ+x; z8gmzzFF>t+jn;t(f&3{7VPX(gcI|?!As_A?GZ#&tJ2rzl^!tB2odr~t+1thGR60Zv zknU8vrCS(h02wDrmNhAN{MNs!1dhjcNH=c~afNT$?QqZxyDCPHg@IIOUWrgxx`Ki5wV|b@^x_jaY^UORT%{Jo72CVcq&{5 zLMp$yvOyAz3an9Lm!GhTm3y%=TU9i6oqi9C{IB$?k6eIw;v+*Z-$w`w^=Ojd0-9fi zif%|#$U@5~6Xv@Zr*`P}A=wp|IW=&Y#;LunNyT{&yE&ZB_36w+)VPZh8I3N7aei$> z4j_=-7UmW=!xRq1ZG1>?kZv|pN*VuJ&i?vEwUQLq$;5O3XM&HlMeVWR8ZjE`iL`n4 z$<(ws^5F1L!o>x1etv#@VZnc7#QNE@a!C)55MSfy!tCg1W=HG^PR{)NeC?YL<%V-0 zoOX|5hw9MWSsRPe^}CzsJleitFtO4ht47%*!uRLtHYKVW33|FlS`K z<*=r;G5S7}J}_n$qPq1awM*YB=PGXg6#mvRSzeV1>G0c?)3lsUKiJK9WIi4&ay#1s z6DbUcN_;|GToSWzsx1!}4YGJpkY=?fqRe!KxVRf4JyD?!kNxA~ca!0lR)o&#cm}^c z9(%qQ`-E7HKGKnCKHT+=hq@j=m)5ZR#WdG&nDk7?XYVjS>Pq!ovYl%oDVPYeB}7sG z1&7)5=n)t4uc)2NZFqb$1!vtgZh&!zSoio6cmP*(jkbXQ33vfbDvWS(FsXhJ4;0DS zg49|6GC^qh|JEb`#f?+u=B}b(ph_y46~JtED)G2u_1u3o7X7GV;?rIY8U@B3uyEL9 zD}sPe@=P8RExC>m1SSv1@80hS!9spHx$a%T(4MIQ4SFy&{28bCPib3oI5q!|5|dw_ zZ{z83o{?boSNfNqb3p9McNC>zjqrEMx(|doGu|Q9u&f1GsKID03SF*OXW3|Vnk>@n zNz|lb%{`|*(**raoFK^#K}O2Ru5I2_;sg#6I~N;F;A<5RG{qU;Pd;+t;Iwv>aVj6d zzvCzj5P5Xy5}{!^ERxqoV8`A6{+(WU@hE5r4F&U=rLd_67pL`nWON1d+aGm#KiWwT zGBaOjw!1hmwS7SOjV6^>`L0DYo7d3*zlqSh813|((Q*(G z-cx#s;4W|e0V+dZS%VsP0U^A5bqo)s_Qinl8*UPwxCNok{#~6;Ew4ysu56Q59ur05 zE<;@6GUxjYI+mdaxv*|C!=gqd5GREYvgk!Z@MuJNsOX5y=}LbNkBEE_d83X)Td5Th zKEbY=Nr3+TGj5-kVwbV_a>e{kakKmSg646%yI-1*8BlNHp^eAGM072Rt!Beq_{ zK(HRqu-0DpXFI9P@c1rjo%OK-xoT&;!(xTnGyCyY)DqJjflrpbdhqOeCaqqe9;DTQ zbq`*g3?%6RFAB_@Q;%nXFwq{xnp$IWkj{=vo(|?Uit56tLVQNhh_Ncxz`#HXZbrCH za2dpi5wq3A^Y1P-jZguYrd(`4d;&H{D7wrRN=#?@EMjP8 zGfM6!wiNz%C<#RfZzvhju=jCE)?s_9;Y!^`{%kIW8HPa6DW~xpU+uJ=-_RDd#yY;1 z+h!v~C!`^7HzN{U{H`KgbC$*GSHnALY(@DS$jESVM6gx|zpbdnFen8;4ENMD+iS}& z54blQ;)IJ84W3RM;wj}5I*yJmXpDxfaNmmls0h*gQMvdkAL834DCjC{P&9&?JLN#z z$xabZCu&?YWw61XAN-r|H{zfA?>}`43hMa3=wCb2_YEa*vC7u21aG#dwgBzS?pn8?Nd;YGJxAl&k-Rn;w2J5h0 z5$0J_@XKxW$}5etw5yJ9?MoGv8qy*C@vpQ*i~va2U>1Urcwx9@acO~TzWuUJtr3E% zd~x$whd1O@<}i6U?}8jV%=v>D41qT{jEss9k)WG}#&8K`3jD42}ek5ssn`-65W_gtK0dC-h8(;hxoc3=Aj!ru=Fd#`#6g=rr z0&%PCQ-d7KaN9`Rq*4|X)7B2H01xZf6gtjx&ew07{X~4s_5ubG6cO6w2s<5#BIUq= zpCX({ORj2&c5A6k?^(HiOvNE{ol6}M(S5}zubw~430QY;qS8gK3}J07Yt{z*A9Wl3 z?Ap#(l~D$&R_7)iil*}X#SM+GCxidO8=qyBSa9q)EpK_TeWL$;e(|R*@x0Ngv7XMS zY0C44|NS-R-tYzHyXh$<3%fIHv&=6U%dS=d0s_05d9k2g^qK^R2+K`Lf7I3H)$KP@ ztahA%eQ5@Y37A^A`tD!0eLKRlRxPN(@+UT;M;88{TC=qU3cC$ogJ}t`62|lA;*O4W z1?^-?3ZJ17sx+I3x%SQ`1mR#T{dvy5T&w=&W?|t!kg0DR5@q`c&WE$#oDUqmiJA@j zD=Ou{n`%p^VvX18II^Av*c|`8j7jkm|Dzv0g{0@6HmNpVKottXrVR;xL&*=<35zFl z%m_2v>bPQOka~YVSV;)($WXMyzM(W4^0(AVS?uqmqUS1Lm~U$pqHfurv;c6#LYvZn z-IRjc)H=o|JolWsOm1*m;%liRAq5XhRlNWgE-`~yd2usquRE!dbIvJ}C@a%u z&~FpZa+Qmv5{0my#YlNeE|_c#++U3yU!EtWv9IRLSzqQim>@nw?fK;@z&ktcj*KC@ zC;=NtOd578zhuI*jx7*zPWKqwb~N+F!Lpv zqrO?tZ{F()s_&leW(n?y$zXdos_3*T_ZCtkJzL;UI)iD9zc|PDuF?WH_7|-M3w;dj zs@af_%}GU~$gR}pYA+SiwMl>3l<@v=mVq^{9eGZi?lo173Xv~;K!EN|f5Xio1{w6x z#DyFi&}AYeBX_=~q%PdZ+72_A%#54!ln`4?83obdCo75clRs)${yI3`AL?fWXQG0} znBEAv5;MUtR(Tpc!)@gn!EMJ;f;UK>msXA?+*+mjm$UXcpp6*$!~qpCjhv=Wl;}3XcEik<)A`7QUWT z6uOAOpte-$pamg$_DTH`2o~=OM`x*)VyZdo`NR7t4*Z`nOW(y$iC(Sicn)YnVRm-8 z$6mCbApLo}H`VBtX<|P8fm&5<+gh+a)baSs$R4s0{JF!KF?63$YK!|vwcnv>&co6r zjIdTR$WZ;R`)O30O;WvYbvAY7v0>!^+fn4mZSj5Qz~o-(z&+Ey0WiNNI$4e?=d1zf zra9r|%+eAwD5>lULP0}|SN&35U2QLjr=-{H9`zI;c`~i_JlWU5;VrG6#bOyFhabp) zEdv4ew9PO^DstwXpn$92VT&2A^R#ZK^97$ZdyzcmNnzJtJ5a(P82tlV+aj{jwNSokzLySMl))t(%81EhiX~zUq&Z@1(TC#|t53I9@&pe`kNqA6e za>CXKj?75@PEiaP{E~s{(_Vt|X_!}qdjD5{X}j0!-KMC?&PwfOI@}jW3STndppjA_ z@BUfTAKW8+06nQB~>}Z_1zFqWRi4z#MhF ztP9J2VkieBqzB=# z$>hD`4Hk95DYo06)x1{)HODuXKk;did~=qncbnc0T)gzve9iD^(&|=T*`2Z6!exCW zd(FDn7JB}U;qTXW66-aO?gPDDL%1l7(S-wMdlJD%8E^H4@eR!Kq>*q|1A*R(Lo#T>1M&>)TH?ywpn6 zFQOwNz(@#GO`sbnfNk^wW)lIKmT+SeM9U$GiQv&1etIM&3jX^<@|z=vI5-D}+>%7* zNqx8^El~1bFY*0nmm}G;Cr`*MF?3QOlg%NG zka>dd*dLXNv;Bi>_|XNPIvM~=U^te@c|S`nJcmfqEDdYyR{`hhwc_HkGWneH#`F5l z?cP17;?XlJ^t1fZJEF4`_R8gEBzS5t?r?gf#+sJ(OL-alZl*xeI=#&?b-k?wk8;C~ z45lnvt+a9(!qW#yvndg5vT?GE9+&0X@eWS2V5L-7{?M$jWYm6+KQG}OU{He;bAJj% z+sI#>d${d-FwG=;xA&dj%106ZU}#X+z55L(Vo<9WWe4Bk*GG%ETQ+~50d;Q;SEA+z zi7{be9=bQ(Gh68)W{*dxD(XSTGz zQTom=+j6PcZ$V^bN{VZBsI1fC=G~1{Cg+dkG##={YkXSX--tMThDiDmZeD>_)>SG# z&po#Tjv|(5VyE^>MscOkKJMQCA_n_Xn7a>`eiJ6wa?{j{$&j6hZEJ@oeuK>WZ=LAz zTvo9xBwX?OXapPOV6HIk={3+00S$QG{LI?+$oB+8#a$z0@3fybi(+m_aKG-}Bw^vnt z+PnW*q)Go1vqAujCdlQZ@_VK(cJcB#40^n=ju2t{!46*f}s+lA1KBGk~LzC zlD*_6)1HArqc|x;I?#oTnAp^uq#P{~ZQNa?cnn{7*ws}i!VtAm^wU{CrBSoW02>W0 z984d4zWp<8Naw zg_9p~*CAYkTdojj@jkUf+i+In-S)A5jEc=PPwawPe~o)DoiJ@(FFrb68W{k2Yjd1Sv!9 zt(_~}v2s4^^cY(W8?iKk*6y4aj9x7vC477IEDu;uggoKY@Js_NabnOAFlo!cI%y z(-Xp3GZfhOejr`jEuCgF2ssJOl1l0Dh%n{5@t`^gQ;0){5jwQ*V5{J=Ad_1O9L{Bg zFKy;(h(JMSXOet+b)vA_B!T5GPu{b?lujgn`0+eB`}f1no%pr|vCJ2sxy15JJC9QPq-N~q?C{@!x&P30A_tWkCG)D#yh*+Ha~y0MU#xo~~u zXfkh?^zB%LpP%0O#RmcJ-2-FY>}<^+&F()8yXKelvd=M!foU zb#=Y19(KhvG(sf#4@H3jR+66&Z)If#EN{Zn(x^DNxVnBd^N<*9DF4WyF1fJV1yqfe zK`Z2++j?U9qt3^tmnzViOa6Pt(!3Ic-E75bN;`9074dOM-qF&7*=3cCgGk3<{B}7_ zm9ZH6-iyNW2|e19a)Y`Zm1O@(_e|8F_DC=WRr&AbDM6eLR-I#x92OXWb~QY-35LZR zZG6N0N=H^KNqZy7z&>wl&`R+=jMG&{abpu{YY$)OewKA*!{I`N->16rC+>LZmIizO zPF&2_#W2tPWSx5VW-$r+>&*ZsO54%NL$MFLyYES1b0ooA7{UEP*~xlr1o`OP8F}pD z+&TZp4C(yN?%|=ggLWe(Id=$VUVRC5vS*?)nE7im|b0M7GpRnk@%3GwdB7)J^%jW2Wp37 zbfW8F>7H3~HNUlrQen6fJpuD4SW3-cL_qGIJukoegQ;nHNkn0>0%_$|9`C!Ii@ZzqIF1n-!(X>-=#0ENb4te(hH-v z@hN7RA-_<=Cppm^P|?^asi|4;V03Vm;i)tlxU?^=G-{_WR{dH+E2$>VL(c#$aEu=O zprscaeyR+qEng|JGfo40x%@Jw`W8-}l>Qb_)YUXK`g`p9~L>bvqoO10S z>OF=|ddf8tb=I#p>-a7#s-2l+FSTh36~9EU@=H5+))N*cSTj;gAze7Vu2bkwA z;61+FQi_hbdP)qgRLi)%!3JCM(+lhADW;fsVpoGt!vJl>5#Zmhim|ykfG`w*e-eaG zOt6XUB#%vu&W~}{vBXM>KwV`_$)3v|1HuD=VUq~lqab{`%RPO*v!!2oOW*lqnt0k2 z=6`gzeCTJ!0%It>s=I4W7Ntvo?rFdVSa-88CUsJYT;K z-l=M-g+rmr`dZZ(iJMd<<+LJh*Sz55nbYEr74`XbJpn%WT?1V$RH)31OlfrKgp&eY zf1!nvx&-T{Z&kcgS!=&4-C+DLy~J|e!q;F&zMlD}wwablax#m(H&d?nM)_c?dg9Bt zJ*WU!aM(9{J}MBuo;bx$2Py;UKepP_saMeUK@%T7zM=0r#qZT@%R?DVCj8WU`ng=E z8jm6Aw#T7Zp&tLXo^Koc;Ska21dU?4db_3hnL2>B;Sr}FR8wNQ{Q2?lw+7s*Bk5w@lXf-dwQ6FhM+U!>-_ZW- zR-PtQR`7`{u5`grBz0n57Xa%o&l3-CD|d{-Y3Q=+j%w*0;l_6O>6v}3e(WBt5TV;O z56LDu3k#ua(uNezUsqIn2Zy+Xga8nzHZnGLlXa3aHZi%8uHXCIop|k3qjQx)+R3;{F16hTf_}-D zHSC$ZYBzU0&^-HzxB)>Pl5q{^roTB;uGz}`6EP~W(#C~k6bUjC66)bP4&Jaw(!u45 zR|%=B5Tc!|6QY5iLsBB@NWWbVhdQqtG4wPf+}YXr?`MS;9hhYlCEU|glr3Gh>~6M> z-)YAOoevom7*o0x-Z+#$x|(JjarZV8=*2v8$B+_bQ#y@Ym>qmobg#L(j~h5C4f`o4 zFDenPUR326J*kqwKM_3{ADfs+e@gupi3T|9{lJ8UmC&_DAhhb#(Y~aB$*cV*@uf@CphF!d){UJvRtF{rv+e;t`$>So^%*gl>DP zq^1~2$77uhSr;Eh$HsjOG7txe9_Skuz=FJle}9k&us_HgiF65OubOr&T{5Moh7uAlse7ZjOTN`%Rz2kUVJn^Qr8@0~;Tw^5OIC{7T4%Yo(&vwofZbAr-V$ULm}@JVk0Ko9{Qe%CCDF>F`%?)pNei zguuJF@wF0K4f=;V` zfQCXt)$g_$)FVTZ9TpaWjg1|dDx!j&U8Mw5BgjnW#ugt>>?)Ts6hD?o?nos}6Z6S561=!y@{i1$DVPOcfBN;7%dKUKVkLnXtqCC{pNPw_Sn8R-|&6pZ< zpTd=@OveI9CI7#<0YuP0n;QpL4lzD{kP_TxDuz-P(LQX41QFhv-w#-s+IFq9`*tWh zZ6K(T)bLyEG`5vO@E7AZ5v#>yuT|6(dzNQ9oPdWjS%kd4UY)>d4ch0Qp>e>}q}xck zw_;Qw!>+JUA(nUQg^VA-Fm{;C?R_)>3p^MTDjk$I7vZrt&~6 zEG#I|Qt9wPuo1Xn5;a@_gDMFT)`LKtWa_E?X~aPHa){! zx->I=wCeAYG2>5DEGR(Z*w-}<0iw#5JN}2u8KL#=t7TCn3Ikk15cpO2$heM&ATOuM zxT3jYUIVYMqKtYyiG{-tpZu`?dBJXPSV}rn(jARLT;O;L9pWVQ&F_<*3NP&D=Y_}0 zX1fPBA7R_Ex1+AnQG~SLguf9&-S2p3{)EO&;r9Ck3f-Sr@?ZB*u&e9VrfCl}Vn{C4 zyZYLDE|A}yeH}cxYS#^oT}k~ZrFhY`vC+d_aGZiQ%aLV}e4M=U8@6-gUF&sU*$bzK z?Bk7u7}?Bn?Y`bObW|8&(h=g`N`Ss=TwPaO(1&i+zLmysvfUmjs_7crBJ_*95@$U& z_NO5Y`y4W4UEo9Oi5~49V)NP7XXe4{Gu9X<^ew}OE<{Z-FT9P-7hM~@OA}$rCU|f2 za}(lH{)nM6*nWF&Vag1*!IQ%0f&q5MsM%Sq6h1EX-8*ufI5fe2G&3nER3`1va{GRTLjsS zYL%wpV(7{WYhbVhH#ws--Fs1TES!D1`*{j1Y?1%~c?gY+6mfK9?CQ#6wO#-|%yHMD z6zssNV>2Q`V!Yt0F)1-I)*2q1H*iH1Sk%|46jV)f+QGL_TM4=lNkqnOQGs|v`1wy8bj7*IU_WMcKW(}je z6un)M2u8C*BJq~AeGFt_d}KZWH1Hd+p9nw%N8l?jNZ=q9llz~BrtCB*zuL1Uwl3L*@6*ynT$qvmTQftaF`No4TZ&V7ROBLi_53yXvLUZ7A_e$)*Riqmiw|haB@gG*xIrd zDP)Gn#$v%jGeWM&sHnu14JoN9=K*%&X}EHrZTR1%xgRAC^@SGwPpQ8@rQ%ms$c9qb zR95WBcvdbW28Rh(tK+qG&CD7!iiCW2yik*_3eM@s$z||J0M!1^TxfWB=Ts>eO)ZGY zrSjvyA$er%?jq;+Le1~SCU|-1_Y+&cp@39N))&~8<4`auRH?_P<+jrDv88i}cRcAj zFs-kz-DVr?5?cB^TT%1D7vp3f3hcL@$WOVxx_yGui%sVzXkk1%&#$f)v8S#4*lHQ$ z%g!am;a3BV0K&}iUHyz+7ivFgl#h;mTtNiz<=y8er~tIWoo|g;3AIet!dNWP-_@Ps zl>*$D?#P`4L_xG!JT~_fVO}D-$d<%E+Q{zgK>?8S%y`-j4lZxR?DqfeHTvKQy%p6B7_GdZbp3}s;XbkyqO%a%Xd--YO~DTs9g2D2S? zyz1UYR&u6sX_0OYh9})1J$cgqI_hf4>y=jOjvnoyP6+0P@&g@h#!thxtsl$I=ucIk zI&Qtj@w^m?Wb+O_xbY}~UP`muqp-hF_p{SLD|pdrB!@yjDoutjDsDfT=eDL_KPoy9 z?O=@)O*JeH;e=|3`2jt8=4m; zZJ)!b@0nZ{zg9=hE2SjQ ze}-63Dv(Xse{TY22{V!P!v)XdZEE_+o_B9*ULic>p>2#JK;1Jj66Jj!PD4iZRRik- z2k=CWKtThW_Z?Whyw7t#VYr$IRb*R$MD$g~IBbu;+!n7~sW)7rfHKy?hd{Bkvw&AF z+8SgsKHtC@>Js>iV;0{6Mzoyz$98XlwDfc(_3tBo0}K%Ybj%sVP&9fLZFM7TGsbn{E7 ztHQTwr{53S#w($@>@u3UW;o>LBldYgw7|~c2Lf^(uYF#k7j~ykNP=lfTqgoFfjYXM z2oMO@qXlumzd;s?&hmD8x>6EFwwWfI^=PIm$l>HiyB?+RbzBJPG0ZZzUq89OQ8?B+ zZ&;iLPN5v)n5jCvmb+8gS(`^M+mR!mkVXd+bh0E>ym$=%>tF%wE|S=C_{}M!rDUU? zk?6QRo~=I=$9;R1yuw=IoVYJ!9jh>rnqiIgCgr(wW8g6tNFN(EK2!GK8c}m!vC)m7 z2kl7une!yK^hbjm8LQozScdbwIrnPdI-B)fKin`b4gp2hhxHSLfsqF8(bhLo>g-(W zDhBE?O4A}W6AO)KYdDN$=eP<`R7AT(o7b9Ue~geR8J;6S9}MP@6XkboQ}cZ4k-V%y zK8=kR>A|@sx{FCpT{LJ4=I;5oZy3N28_#eZDvlY;p_u2 zE>9Vh4802(Fac#0{n?olD7mVtO;3}};M8LvL6_X^6lc5V$KU&%BGuK`=jG)A2m%wB zjDjnk2iLj-=b8fuWF?+`(5&leev_?@VGOq!8@u!OBDK9o8uX!pN12R)Pu|zwgrKtq z_nRE{G|s|}h=~M;Y2oLGE}n)J|Ff#)rR9DK%!O^=k#oudlzk`8M%-!z*iWFtuAozC zd<;|Ij%C08>dJO0`@#46Y@19#0H4l$faH1bPnk$>1>> zj{=MX1LqR>w>ecpQq!q(z`zv3!|nHuWL!#EgfaGOfB-WJ zImD*&S4C9IDNK9$D*odh@Z=7>+C0_3QWRkj>mV`ZBw`pbToHwm}3%; zW@k08AG5JLqw_Yr>34+Z{DuXwh4=c=7$<3Zo$N@!_i@XNx*e#uIyln(dz3P5tkdX1Y0uxQTyW?YWcLap!UE);$mNRW6_n$_kJDh zE~*-@R%{K4)1aMSo_r04)Z8R80R9cCQ)jD0$RT{Z^B;6|!+u$)8~plOsM#1lK7asK z2&`d|nS&h^7e}Mz!-l#;YH;?pMlypwY-dMwV+g#ce7H3Z(-|3(!7nv47WguUciT(F z+@+?W8+?CxLsHRn{@E`T?WIFLNA}c~Cx#e7&i)*C^n9jz`!UJcJ{Yn!94%vN;@Av-%68S#kR+f* zZsfX;_MNo*KaMbMPnKEmuu5~j?OI46l<^{K(ntlKswfJEgYSqX^f-WtjZY9c1QD|d zM`sbc9DE1Jm7^(wh(eFq*=u8<4)u3sJ2mJapDV&sB8W}Lp}BG$&%18eSZhaBYP+lP zL0Q26^NRFEn@rnkPf(WUjJOxL@!1OjF;x){BnauSyoF5S@Jl z3q@#;RCgv1amv|`Qr26s?2)|gG}`*$JYZaI6ItwA;}5zDf@NGp$mD!q5fRaahp$y8 z5rYkM6m?&c4#o=3L^qOE{R0$@tZZTUYji9qD7OMYR>G2Uc;o3>0R{%y6%Np<0Y(2; zvjEa8=YO9Jd8I%aRgLNc?T|-5F%+Q?7_7Edq37#J8So;T0&%EeAq;TZH>bi z$tyiPWS*K*2Qc?U&H68?$h4Kj2oSYE`wt&r(IU`3C;>@aw5V+?KhXgol)iUt74*2? z-P`!|KHMGz@L9Lx&++Ww6oL(2EmJ0BbwitaU%mqnPJ-bt7tz}v4=S;Ln2}5X^7aNi z)WoQo`NsXKmCNoAm==Y;$LO32!;MXhqvlDOe36?W<2#fA1xxtFx7Qs0oJ<0UwWgBm zl0y8iZ|_!aU02#Xz~8zXCF4AyW`+$NqLP!M>zMS*;y1mH@wvml-M<&ecr12c^;xpq zVeou;(BN|X5S7?wy$|Y9|gy|fo8jmb&3&bqBjnFXSkPNbtY4HN>S2y zUbo*}KN>uGUG+^W3PzVZ(&TAy(mDDKoR{74#kHPj692$@k~d(XLq0=OFXLbOE6quv zuOizKb;Zi%-u|SG!8KiFj!51!Pi1-W@Sw z$1zyfyA#PxC&P5#&x(O;lbN(R`^aI&?*&X3eQtk?1Bg-#Nf09X7Sy8yxnJ$jP?6b4 z@dmAbTU2_qlZ75&8`X$C_Sy2TmVFd=*Va&CQYTZ!n?_F78W1(>QmUL_37E)*O-4SE zBAA%_Zz7@-8Z6y)3ePe5u=UfLSwNracJ^V6lfd3n8twF_d<6sye366JY$wlypX$F(;2Q_R^px?_8418>7*+Z8yD%C#*1b<1eIiu<2$MlMD{ z-Jr3(U7uVMy#AQ0rUHNJBP(GbBrfjCncX=_ix|IS>0}c5R=cWOxA02T_zM(bu2)rN zP=i_=!T=gW*l(W&Lsw68CPYPt{2KbM(ElGj&Dt;Y(>!+fKxM zgdw_;A!QLGUHXrpzAr}T1O-FzFWHb^k)HWQMuK3U;@=mNqz(v| ziyka354-Jgon|`H=Br>-ex2}(+i4%>ka9>LD}v+kDmgylI?YFqZbj9qsGlF|o@Kb1 zK!=B-m-y#w*P^m#$-@iN^!|ti3m@Ab%n6Eh#W^tH8hOQ{5>PgM+E3x4c~6yyea^96 z9}l=<6SLL`hvjG)&n9iY+DwXQDbjPn%88pvVLeI15n5K%=l;r}i`cc`XH5EkxM4**ADT|q5cSe#EpfG)a zD(nR9pT}j8VQ=*pjcE-bV9mX|~)R^2IOn2`=rd!)%I&;+Du7$ocLI z8^c*5)ALpRid3CeG59->P^lR*0D+q@2}j*b_5xXF#Bj9%{j8u62l!9n+h}k1J`6p? zK@mFeVt`^Zsn?*3TbX$k5ZCXC^hPO*-z{%LDGQ%N`&5{QrWi$TWI&7DoDHA_(@+1q z5dn+t|5P%=9(v;w*b1O4-0u(48rk%Il+HfCb?W;fNFi6;zRWf>gQ?R6mDuV`U}s^Qd>U4Ems7ISEu8P3m!vL)(V0iA#p;OOY7~b2Q15l0(tZ&SI0Z zBknwrnU{SYTe<&&BDXXByJu%xqw!NO0tY}3ud+I)tbPyssJ_<$XuKYZi{~|5n8l3C zOr`$-b$?sSEHsvpMd@vVBoA6YvqDKdA3Yk5mONs!lGgnCUcP-^#g={AZ8qWXc#eF+ z_GQXVvw>`Sj&dyF&>v>@Rbe@KCjQS07`?6oZJXu~t_c+2mUO`@>SuWok8KCCD^e)Wp38^Tyq`(!VVux&O;+X}ZWux%aM`DBkyF`k#+l z+hWQd-KfBLZ14CddVvax)w%%PTNF93HdLv~mNj z-0-sBua^AO_16w(yey}ZLUevc0eG2T>LSjLY}^vkoi;SKR|2> zXG|(*BEu}$4GJp!U!mRhfKD(T)AJgZ8ZB|=GAZD~d=0!ZTr-95kHGmKP1O{_KEvv~ zuPK+xj=j2?oAQcJ`;e_2W|dIQP1Zgx1Hz3$Td7;y_wNL(mXrLyS=d<$a1%x%;D0e} zTUyc!vowA?I8{1HrBOBS7)_P$fa;8Ii9rb#B0Fq7U2S{(E87R&g34}$d4z@*E>h3q zxD6F*RTKU1*yp?Vi$+}*PloHlyxc^VEDn2KdU^@YEp#+AQ4tYX5W?Shw`pO?webYW zOht{lLZG!-Y*K?CcvWS$?ru5D>ihf^@|e(AKv5AdL<3@Qv6~1Nm&`(=s(uYgzTQ*< z$CqC5AX<86i2O}>IKEPm{RgGkh_ymQ*ONk1yB_S2m4bxm?kRKJLQJlM!GU2h1%+Tz z9rX!V2L)?hL6Tco1%V4AV#k#S*PBwB*o4eOL?}?d0A$kMNR`(6AtD>?*+YM?kIRH^ z0gYSdP8MiE61NuqF@xC_qW`ewgY_Dp*;Zrpi9~?Q=)iO(WQ%#ZxP8lg(vW|$#AoB~ zS0hH?;mdx;RHpi}6il-|iB-_tq&T!aE@^W=fc}anso>%E5GP0e=dQ4#J{mHMSdt;8 zj>0OE8C^X2+K+ZY+}pzoFP*>1=#oFfVaShUO31)!OB;LO01lr zVM0Qtv~5`ELtN()f49t%;|mNWlM%zBuTMwNlcnY(sVq~L^9$hQDDp&);s;BAXir{P zY3vzzmf2=avs>-I|VAofV?$T!YILp*LSoQ;<>idm7xS?*{oEys*Yo@Z*FcPFt?p@9daKKu%Ys9bw0d+e~Fbn+?Nn!d)b zlYlEQj2Zm_E^)4p*Za-b>@4kZSW)r2YT)91x^MxODz%cerDex_eOa~h0X+~Uh|6Xe z`_t-yqett>!J}DgQZGhZb~ixJn~DXfGFE*z7dj2*V4-vqHhqzFf9Di^BH=HW31@V?@PQ{G_7lto zXlLKs8j9-V*}lu}Sb4nv#eyKS6W2`*Gu_X=h$wq$Ov&Pv;o4=FB51|V&XbKydwWWE z+ikW;>IbeqD1& zr$@Z8p}TfCSpl+X0G^EkyHH1PpDu;4N7Z$mrj%W)WxbzGG2_1G0@?YP(% zF(A6SvR~^H;&nNP-hO{zgV4mWrfpzH{OOdRQRl6OnDBmq#fpSGI=}Fo^rG$FHMrmG^|l7xva|0ji6`bMj|HkpT2D;XmRAEdm}!?N$rpY1mVvXetmIZ?m@7P!u&A zcsM$8dwWb*Rv!s^tS$&;QXD7oKmgC1Z-;(sy(@wl>xJh+wWML^U87)*Wr5pR|9G~; z{+|&(>jXUP5Mq0>C7&y%Faq_)1TE!wq;8J(S4!U3iQ9H7u(vBK_g9_oua+a~-frDo zAUE=>8>+TKq)*@iXyh||RTOxn?b#mhj|ia`rNdiz4Qey%vbw9G<1K*`wV(5jLuwx} zJ-n9v_FHHaNHN8xPYG|M*C|D{tU{Bq+OcNz&J@G$1kYmwun2f334bxptdUafUksY< z_PAnGxM#akJFd$Bz#Y8yd8>!$2TAdEJ}I>u*T2<7k<(RD*HLu&XW^V zup-m#jzuIH27-&wXjYIS+T^bT<_ znMu-K84x9E9S~?M0cH%0Di(s#H80w%`%Uy6GQ97=oNGOdCaYtBCOZ^FBBi~@&((Gz zDhdScGdd3MrYrT2xPCM~EPk7tcL9vOe@%}lbW=Xj{98U7fdo*&M^C=_-+acC)J4j! zSBqDO%(!2oUva^bdcEb$nJRM9S4F7Pmk4b3D}Xsx`b;WA+8=5}<*$R^IApk~a&#tc zTrq=p)CzYaB9aS6^nz&U5#!U-eu{$ELq~ogmBkNf^C545${d|K=1O}Fzj*;SyyUs3 z7WwM&O_3Z?Oz9*IIjtFe661h`xjEf*g>JyFGWE}emLorg&>^W0UC2ohRgP|cVsw1u za-8Ie5y?5OTCfC+83D@=%c*dXNAHs$DMdw`*%nHcSF%Y)JjQxOp$1w5VR{OLFs-)Z z{E;M1B$vNkY@P;d-7Sr^v8g-1J4T;Vr(;7AA~f{9f;s?9lotkER|WL(6W* z#D!u6Pq8lNZjoz}wzIrs@*=3{Q_pB~GPW=vuSw|uQgjBam)lQ|;EbML*{(j_7qyHB z+M#T?g=Z6*$mv8*(Qy|t?fT5&eva~8VW#leB#BM%b|jIx*wSN?>TJ0uVDs@I`P_6L zJ=HZyn*Iz9u~$@8t)UJ>O1c|RpsdHFW$Ai8qtCDhCN1!+=4+Ze6vvto*6K-omNCB?8JEs0iu?pZCTD}14udegte6IF?dJeA-(Pqb$C)Gh831 z-tW>RWBVPzr}N?$Kkl11cR_e(Y?4y+&$>2KUP#c5N~lgTA|QvXG;U`lEc`Ofme29z ze7H*R=q3>HvT`cpjU62<5$dqM>?iSftH8U1Ky( z{~yA@;q3}Zy=JvpR_GYI%$)sh#m8y06#4J~dwzMD-|i#O)Z{)kN2Iy9*!mu*DK=7e zS-Cy$o)uo~aUo50bOejs%U8$53ns<+d3lZ8-OXKZ4W}0vwZ80M&o!#(hcptXwD>S` zSvzhm*KFc?Ue0l*l&4gdL3_Un(Snh?l~D>RXk%r0Dx_`~{EKKF!jW%MB`q zfH*29B9aB4pi$ZNHIbp}OCmITzo1=7Sj)UrjLxGD;s!w%gG-7a#wR2sXA&SH)N_T! z;I$RX@Hix8^?4xPNoIL!QI{6g1fujlW%wLy|w#e3Tmc`nkr6rzy0W|9}f7iWI%Y2JplM`W4rOp39o zsZq%~p5V}@t#tS67IbTF2Nkm3_tX0d1~ZJF1S?mm)LsLTFEC;_?)3LB?+>=ax^#Vi zMQ&oVvi?RA-$NWm5dRL_Bp!asi0iri#x0wK)5!NU9R2BY_FXy?3NEhiEwkSZ6tN-& zsrWt%=Tt^>+^j-8gN-%y1mDHar5EVUK?<^La{0mxRp2<7o{^>yFr_+Ti{qhBK^cGz zx0{VzlnS^OfIbaw+VI3Jp$LuR6(Wp@#|)Id9F06tBCeh-+@jyUuA5{GzV==6{s7q> zWHw2CSJbbj8|;^8fhP-F@-Bx=hAkkI!HzbF;?TIp*R>zsG(sEtjoxz_81gU#pTJ`= z)y>==vab7nos7B%q$0nJS*WC+_xGcG<|-gUg88~N7P*Hr&$F?DC zt`T-?5*G8=|4-Fo5!R~@ap=0QXM7y#+E*v7cNCA9yz|%fd=6Gi2PZfXsy}?EnjoqG znsAWo&54gw&e)KCR_^kd7&{eZP>~cCIqR=)KN?xYH}NqUR1vg+U$2if_NTAR8GJM! zKplHzuWM&^Vst-LR3<$*8W}1pnluvzHjtN)>=q*nErc-+sKC<$yX*|7Y!~%Kf*5+m2KMsUk z`(JnQ;5a05n}IHVx_!D78V=8F?`^ZTxBK)**Hf_+JZ7{}RpgQKIGLw~avk6KvLu5& zN$V*wJAYm>1@(e%wZ@3+J(c^_^2z*yevu)~xNwWAc5G;TPXe#!S$uf4JS2!yKR?gi z1G-p*SIh<=$MY)1j3EIg+NSkP6vuO_gZ;U<_j1PJXFdVnpv?{Mrz9pup6QX)VS8!v zvSXk{&kN_zM+ZcLP0JCdp-}-WwIL-uG%=ZkH`8AZ<<|m2Dkj{Ewl`8loVX+ zG|CFv^tCA@H#5M~_d!JPV8ZtX>t{T{Z~VC`c#C7%chXCKxP$z(9$i$SNN?NE?T_9V zdFTr7{bBxPH}r)h6bn(z-8+RDEMBJss+rVLY){jgCz02s|HZZsvdHbWLFrj1%g|cNCLMrzD>Yz7MxAv>m$_e|Dl)V^U*j<%f<+%*17- z1)YtPJ|`^ZMDREFSdW+hmz`g}{N@BcN%Z8iO{oT>= zaU5gJ?LGhCYt!}PPV^oBzqqk9P0;=P$=Kadc2zlu?SJ&(5g!bt2{7)6Mm_MG-dU~H zR;!4iO3r%KkZB+3k&DBczXWfpMjW%PS2C}h?D1?fSWvGOM{c2iI9V_JQ+WMzb*9gJ zfbPNfWDZYG1~QtbU0*|kua}nwl{z;B8h!tDj8K@-sZ?l?*h7w3 zHz#5!vEEgAuT$~i@;o0BT>*k3iOsw`2=uS};bCE8GH;%?9CX=6w7^OL$MIU~X1|Sr zw0*H+Bch|zTy2|-g-n38aXSr(?F{(6c6kx_Evq{h8lQN|e(uRz&{?HL_{8`(#n#Df z;uYQ%_xNm5Y9i@$zZkw-K-Z+h#8al_zF^sFS{I1o3nHtIQ33J?p}PKcN7 z^YyrhWb(dp5Dp4<|0jNAN={ovdE@#!U`TCXV6t6aH-R2AiW41yJU%jlyfg-53_ck* zy39d}jv-Oc^j2hWl^6KuvZqt;qWz%-X(K2D&gjEYu70fOy^ zv~ZXhqN}qWhm?*#w{;1qX=#!;;i(hx$Q?U<#@R&n9FOxgH$Z;@U-R%Dymg2sJ78hc z<~ug_<*K`ZokTI5KAXisfsBAdxl?!IP7dh%j#5zAUo(nB$lTb7Ef?r zVED^3uS-E8E-sybjwv0_z~#(JXg~-==>^49>w%9l`4<{|CQ34q?w>nS6jm&hxi`O_ zlSpCAOd~(CFO*IXp_nDp8>xq#c$$-#F*$a;cNsBbDspALaL9{KElW-R0OZ{1;HUt< z%>a4P@lv^)&inCtV+R}){Tv4wk<Kn3S*M7Py|M@i<1AB5TuMNBgft)=Hh7x( z4>)#@kp5jySx(tk?~PqYdRt^X$7dZVlkgyQp?e@ZwT@LcE!`5#7O_Cs+O z$moD2SN?|Pc&#Qd#=nwDq4!>l3?`7B9p7eGmi!VteKzBA8LYEv>B^g$U;Fpz6I&eSgy~;oLB^u;&m^L;tmgqa)e7|S&EdtI@dOXmryrGH7 z4;mT-Gc%~gsHzCZXZe`*=4?cMm%KtO9?5E%m{FPVKZv3fR4^6(w$F(c(v!#pDX~dE z2I<-_2@zL5_<;k~_Yv4%X}ef>62IdW4yl=Vy7QyN08v1DpJtUKupD^zCn7~eIel4( zYGLmbakERzhQy^Fw2FuXHU0c^QUy?RJ-fi6x5W6KV}<8#I=BcjufaBybiz$M&C`C@ zi|=V+nL#8>;yZ!gFMlA1%Dq3FR1tBpX%g@_SV<9cDyATxGvS7N#%^hqIc(x??z1RZ z9&k&VUH1!>DjFLT&=BDsVPV-@hFJ{e6?8ZGCtcIJVwkI!n0tn z673$jL^}iM*%45ZivchL9B{aIZZ&nj(M<5ZxUd7guG_CSll?SJYayrQU*?n-M_z)` zua{x|MU5q;5+oW6F0O;C@%Elh1e>dpDCz*}vrZK|-tSKb)+&R?Oi`RFH)RSJqjXoE z{7=Mmawux~W8L*}8~SQj@h5P-<`Z;sV$smlFf!& z3fB3$H%U_UV-^dU5g0?!rJC!(00_)JrNYN<)V{{|LLDv+Y7W`Rjt>} zEs-4CN-U;l=Pdh`5%yOdBb2EEFL`5}0g_}bKm5}>$kV2ecObWN z73iip15ZYrGTgj%9Pu56+ki8j5KmS@1SWvswP?`8sw$Y1tSqZSY90}f3>a17gwqwa z`ZYFVhDT=X#Lkzt+ZieL{lk2_-B>YwK#d)T?eD~M4+cZexr5tt&cG({>?i^oVG9!z3OY`}%>)-xQHDL7*JCffKG0xaD{J$+8a#e4 zWxCV8y<});q^DfHhW+5@fkCG~Vn1wAmT$7I2X1QfvTUD=2BQK@JeI(z?-ZegSsu8x zOvj1ixe?`J`MRbX?rwO9^g%u%hz~{Rk3T| z{H!KDCp6qLWdU?uk}^&~L6}=sR#pTxF)r%&ZzW>V0>I@YBYBWAJv%HeE}7tn zosDsqtodg0>2|)iSnQd+?d=uh&CH-Clxs#h6+}rcRP?RHVdSxpP}P_N>>b?OsBTBwAGM`LNH#i`fOkPedq(@NH znhBc4mkWdu8r@USPmY=`LQYOA=xmLM=keF>^(-#j?uw7LI~d=a7!(c{Zn)lp^sIFK zY~Xd`=h^l;LiPAU@@*&1`xUaP{k`S(fb+JY*g%pmsj)}U^Kv`*&uJKy?Ng%a)^Jg_ zsJvaxu5{*1ax@`0_x3ccsQeSCg@GRr`nD{yKtxpp!0kE z&spOtGM7iAmW7T^huDONNV~2Y?e+^bAClDL1_imOpdj4)-4xNE-Rm;jHyRf`cU#Qs zu(0+Erle+SqX<+GhHF7ni3k5$}+i1nRb&Wk_fNPYqu))C9#mhS*N}Li zl%5R~kWH`6`3>wF&#|%Q6jpwncVba%(dXEw)AHePI2a=Jg&J&;F zvA>*dj%@O@k=Q(CGGCQ0g6xi_c(3|w;K05^C!lL|HS-!xGCBuPvTJOFCvtt z$t@uux8AWt-w}`kvEdV3szCNaNFt`P=k+i($f-4cb`z^;G28uE0wU=t;`f-=joeAo z{rdli^MH{GXz74q+OA0l027%fKp0PNbS%t$edr$-e+y`ix;oE0kuJP1e_&%{|8qbA zoJ&q~2`tO}d~tdCZE0a4+}&M+sb#sCq9SBkT6!No$$<>Q%)*k|0mX^@rd;sK3U;Vq z7Pk@MK-3H7G=u!#@b6I808`)b4y4vP=T&vWx4&rvomzq}He>*-SkTd01fQvWrq=3= zAt>ZeZ#E;m+!a1mo>z)vgmD5e_>>x|DufDpC2ehE-y4RqdjOXSYTdzvsGKP>VLl<$ zz|~4AWtp5Zi(16W*Q-B;*enfB-}JV7L~1#H$;jZAmRS^(S16Z?$z+Nh zw>9G3-Q5+Jl*E!7TPF;(HBzaT3v1I1F6J~gs`MdMc$HQOX$!R{G&m{IB++VymE(ve z83R>wi3*b)micpPv$`~(`NiDA4#cv>a#8CI)ZN{E{iiUD+Is+^L|K%qnQ%D;T-oZi z)|qQkJtGC9$+rrU zJ`df4N85qYJFWw#^0Epc5s?Bg3BO?sDi!vsU)nUKFbpRa^4glg$1CnXOJ1-34Adn7 z+6NVwKQ08;;F6;wa@xHtb9tpL3lCXSeGMw}O8K>g*z+t}Vi<5?VX?94!KZBy zh_^_$;VrE#dp5Og%|J2bwH7?UM)n$a}BIsuwRYP zQx53;$>jQk%=&$l`&kUfuJ|=Cw4{;TPj3O}fy!;g$gPJ=_?e)~ToYvgbk-3(*_Smn zEjo=`FIM1dzF7aazWP4_77L;QK%gt2-+SK$I3%2G&st@@3qX)V?iK-vZ$;NUZxG@> z?%N+W60_|Xk$wow&i`{jg+WwK)ayRO@^!LgOrLgv3m^I9Z$=>vP+CLmN*I z4~0!kf=?GVmWOv_fr_u({8sq0vxu(KnBtL%dqhWxP1>U}XP1j(duP{+qpLHR&Dpax zq>bdtbL%S!-M=1F*4MJSO%C_U4@rX)?zj}XEw68n7lNDfvyl@(o~KiNIHy~?c4i(w z@9)ahqd})H9e5_V8!!9K!6V5VqD~&K510?a+R-J5N%oUm)7>FU_lO)1&*#T+i%X$t zdnYT~5e$ocC+NDz?@l9}b8Dy)tJ6V^6NvRLPI(oxWP8yC8YZm=fAU8;tS`=mEyP;I z*LB|-uF(#Vo$@l@TUw{PPYzFoEj(IyGuRO?I^IYJK$my}UYCfx?=kL327K=LJG{=!UZ>D0 z?B_z#c>`Q&-0wuvsh13eve@BfbA5IxtZop6GO0dTie&ox5oR!mX+KKzA1#8<@eVG6 zy*=Q-xOCO#hmz4={|3iycRtTo2_ve#NAepN@^0TTzyXAqxnc*<>Ix^P(`21F4mf02 z*bF@&l;>-{N8pYGn=eM+*>%xMc=YNPt^sg^D*zJOUiEuF_-$vu-wxKgodRZ>=$G?W zq>tz1&WslCf0c|qRo-u%IxwIKj?Qnox5K&)47gtp*5g@PasNMF<^O%03EU=quD>!f z&%w!gz1E2B`OjK4t%m`Hj-T+oUu#}HGr0=TNa2+>syC*e@0R|UK>{j#DrZZwEt11G zEgyOj7-{l0&O;Q}Dhx!n0Z*im95wySJ=X+ZpgRTCIkiaW(3{G*&EK zJ$*fW^rUL^?odAFd$FYAeR)*`pX1&fc%Ee4iT}x}w-ba{Ur)c%2!{)Czw$}RA-_8d z4+038fG~t6JRz9(ppB_QJIl&G_wdEZtp!tqkm%9zQ;l+p!f>4TE%)=F2JI|O`c4I0 z9xthG?m(M-(su{Tg$OE@Nlwe=UxoLXf1E%BPpFXo+4VBBPO&JyRO)ZSS5rA^b|WB) zuz^jWc9);y`SeQY$YPP?rg$-b-Oa`hVFX%HN*oS08h=kp zJHJjeyUpSrOpngzLCe6wq0S+j=b}@jjyVT)+ zzU;hQ%756Y5j!8;iRKOe*>gIUpbt?UyZ*h}8vF-!*WK9QdIrdAX_h(8 zInMQ{3Q*Jk8rmIwxc~X{jGur_QL>)?CXaE%=Ka+zBr-FyxR^W?XV+Lq79F7G+pod) z0G(g8+s$Xo<+*5@?=RIxiukNnj`eP{?m)U4csiR^SK;_cCU3fXP3qZ+2mRD)=>3Ab zV*{IhPA7ai+pQkR`}=ciI>XeupYOTKm)rW>&MHXAxAnY^4%%Diru?Q>1Riag!*g>% z4-d#=^HF(4?fpm~`?L0^D08Ks863FsKb{LP3>;#8!5rEFC|qC*_f?<`x4{6%R>&v5~0X@q>WGlhPpd{Hr~U_C)iBELa6W zqfS+o`a1k4rh-h1=|v&@EGG)@eGvVRS~uOfw&$9Tuj|2!Oi-8(t&KKUIae?~v?@NkEUv&2-3zMfH7 za~rJqb4h1ld&Z*r_kPv7Z{20x&*QvR#*yyV^_$Dz4sUsv;h?MsG)h35i02s->+!I? z9sEJkyo?Vz1?RqB5&{(e!B>5X(SLzazl&*-mjpxKiJYdgU*WSoGn-OgO$Uw@&E4UN znV1a{xDM4y+R4<68p#<=WTL4>W$aGnJ?ZUzeQtgQ-n`MMd`lsi(;n57Tr;*)qJ^Al zJK|Xd!r`f<_LB>FtRNtN#& zdK9W&a=m@R?WZoCJNIH`mHI=~CO(>07-sHS;0&)%nQm2wGuvfJRcHaugL8iR$?Q4M zdWavS%aY8Sb)XYc5jp=x)nz$RFq!X(+;tuP`BR<~{su3t136~-Z~j`uQ1!t>t@+A@ zg395xVBg=*hYw+!`h==^=^)WsgQ2pFLnf!wa2stR0UpDr!A8)Q6A6TN3T<mbnQJP&-nWDqUUo;I1G$;VnXf7bGUaf30XZ@xaY zacH@1>h{Dpy=c*in6bSId$PtQQP2x$j5hqLCS1oB0O7xkT24J7C?bJkKB}5m>zLZL zG12&0!fbkDA>{?#4 z<;(m@Qm+0wBQ&=QYkplmij0#{ahcB9V@Xc7Q*i}_fPoN33#aDv8Td#9r3%U@95vHN z9UjSXpbyVF#JWaGSXaD4Zb35^t`)$G0aq0Xe10F<5=S_=B??SY-MH+g#5ZNxeP=r4 zxnH%2*c{mqZKXuh~J&N{Cn7o4W;=b4p)*paQulU!Bc?4U$^53jUlw+yT@ zeuJnJ-Evj#^m2J^O^G}!!frE%?Pgk3Noc z4Nb$FfuKUs1DzZA*mvHG$;Ky_ihcccph8eyTbbfX#QQB>cl+nTJDzukDLQTY>&2h@ z7OH<*z8%00GJp%%x?0a$6czT?KuFuyxPKmaf7V|^K8Q;&XgtM=;pDvb#Yc-fUf!4_={-ZSCa6??%LScp|81fH1O1(|e4;L9Uq@yi&VYOrh($GBUsH6|4^i)+a6@ETCj+ zRf?*%Hlq+7^ej0ypOD|!0)^moGBqdXyFWmAdxsi;dd`~D=#H>ZIo8{PAthI5jdeVt zn%AgbAk53;*WA#o;bSj^Km<_8*$bqi!pV7Mbtos$Ej7PD_@5UZ^4r@n5D=UIaNt3d zf=MRu`N>npssfDCw+Pg3ioCTLu)-N-CM*Pk#Qu(5fFxz@ys*jV)6wC@%)%BP{rRT{ z9k>nO!Kv*4>K9g)VTN3SUv$2h2MU)_cjY@7r<nbo^_y+*_!HeS_(hkMIuC1wQzzraT`w<@v*OOUJT=oBf!Gitm0gjiP*ZJ z^xZ#%BKhqmHL8FoC@asK-uNgzpPn5h-VK2)EQJ*f&`L{7ndR1lff5wv2Il?AS~H z<9WMi$n2+$k}WL^jNmJ zg+^SagemQNbt&}GlON3+GMMcOHOYO0p5@&QJ`x%ly754CKqi;Pa z3V=UfSfrKZHT{wb;p&-_a!V@FGp$&bsyom$8|gV~U|EzcYOIw}MpU@G%*#}`F8>rN zDQE+$D5ugK0lO48Wo^V*O(7dX9L5An?6-`vlMi@ZJMnXD9_JB zoWL+){Wz(=%grsp!o|%5YBxo-wRt$XTBnw_e1`T(tsNIaec$g07tm}~R2Oq zUvO4ZR?00Z`wrrf_4sMSm$tfMD1(s*;8`Uis%%8@xM~v?20Atyy_}N`Xn1JkH+OlM z%3~fwJFViavv{iJA3sXzPi(|mr_FGNWY^{QBKyb3>d`Aap>SW##k-zA9aNi66LtbYW>KM`0-~ay!@|Q!tUl$|KHCR+tG2mNKYj8I zvotn`#|0ADXulG)JD+I`Hvnl{4Igb3uI4?4H%m~});mGhxhw0yIG+4<=l$hkaMJso z$#)RF`|4jjLN5~ET$vCOkD4Q(rP&H0LX783BFnJN05cWb!+*bYTyva_3;{y>pNpEu zdFImQYZ@*p|HrEMZwj{(PC<-V@2Z7ONnIdBmIi#XA}6T^4V%6r-(g`^r>E2c<7W2A z)#IZ}YYU3YG<;i1m5a-01!QEf%u7}jnCFu7Dw_%gJFrR0;N+D%a1a_uiphj4b%LMM6qpwhb89-j&^RfZT9gXP^xFf&82@l73Fe{M zK;uu=U!(hx#bqSyl?mkfg6$g};J3HVQgil3MuEi@?aKCqOc+6EGHb`7aMtMbOTx%U z5^nr*gXDkjAR*%r{mr0CEy_Nb-ZN0T_kyi(twPzO>Y(Z~#--A$3#j`|`GS-AwN$i! za=Hr$nuqxL0-e$5RR}sfOBy;}X~CvANhQtgP$4XT0YE%D-sh?5jiBchPH^vvaT?6a z^5_#lGl23)T3S@a3BJGP6N0h|2~`&XcYfB*?~T6#W6C2xAAxC{U&O)o&-)qp3KHy_0Sl~tLPe*?=P@ko&8=9U)_evEKdUq8LW@>WZjCvFCRqQ{ z_)?_HrI=HEOU5H@(p`gKYGsymqNl{As7o&ccW`t{h-1y~0Dl2Lt3e-KMJLAgW@}|h z6hn5IFo$_28)fL*9zkqlouE`mqO+_-HG#EF zuS0+lU)qWOJ3WX$Xz-KQmn_>0Zj$?fTF3QBr0x2+%=M;%jYLpAx=mI}N(iX+0jfyb zhek)^#~*jYb7~pgv|&?V*Y-v+kzh|xwP7i0WH>EU zefrFF{TP52g<%Sq$_BSj0Zxx~&11{=m+5rtdHZ><=gyWK=;^0geOehPk+wc_58jTN{_^AfwgZX>Uos zC+lhB`Qq3-t;rIE6`;_9V&cpG~&LQ{;A5>NOsfGjz1NROg4kv?r&e{?4qDI zEZ*e07|VC^gK(Ttw?+F7my)w^8sNSQL4$LpH;N=lq4|!VRf? zgy*7_xI-KXeGG}gI#=O>l@S`+@I@=2@`sTpq>UL^(}FCPe7ov}*kf~C5$*K`P!K<1 zZ4r^<HkdM6kU%xD$}$p0a%|4Z0Zpj+v@nK%xe zDON9fcv-4?3IDct$H8D}Ct%q;XxQ9u@5ZPr58ZsDE2VLhp-FV5$C`jCe9w> zv*~Y3?h^~v{`1JG&|=EfvEBB3DI|N(A^4D2%w8`;hbaw-Mq1opR%_`*f=^+J`{+#& z6j%SK2a{_HMs#~YfGSKcyu2_wnT~TkjOoB#XGb`fe6H!!Ts({+o5Q19=X)(}IGiDZ zbz`jyG0OaT^iN?5(#Q#7FxD<C{pA_+bbKR$*2cymoD$Z7$G96xQ{{5`$za3JSwl)ka zBWSuxRMqh{8coV8EDU;MPy87D5(bvvxt7OHuc7L9Kh5x5Zf0fGeUR-PrTg*h?)lKV zX{89;Vb6CiHKjvW8|Wu zFsDyj4(e(VBHY)?Inq{?=VH{mfup~C7DeD+++w67^z6C)nd0+@j8Sb@Dqx-=$11)@ z7sU|dWJog<*>m%@M>i)Gt&h&uU{VhzgJE!(!Orc0Z`9{ArAqGUlfeub#g)gIg_VjE z#L5!Nb}>r4D2#oQM5a%aAQGV(G;yHkO&lRe9ciHWd{9?D&L>+iBTiaGm1uk;CATIT zruF-2gdlT7;Efs5pMzigCck+vAIG4AMb#4zm%5*TwT1S=3kT_iQA34*? z{pXoxTCKyYTMTHYZT|G27VycV-Tp-#@%i?ujmwQyZX!}jM&p(=&nQmCa*9{_|WXT;H-oN|f_3Pc}|h;mJNzs|lo0uH29$Z8B4e zWmEj@>I4|W9%Y?)x><pA%kgeH#A+FNxL=?^U1t9|0GUs#0)QLU3cL0rBGAWon z>goV_q}HYCGmyjiA777p59hFfCJEnIOc)Oo_J#lYF#gXGfmT!%xG$>p+CZ4&S@~tJ zncwZcLu*>l>oz57c1M2yGRW!#|n!(UIc9 zoksA*PY z^=RzS@uN!Ro#nCCxIb~~#BCzaz@RzL&UEc{=jeb{lBRusS^ho)mYft;zo;`~F!^Aj zV_(%k!T7qdsCzG2B!~P>Pt{3LAUAFiuq513LXpLZnL7suaoG<{{)_C_=+Dg$ za_4Ue-ce6i%hB+s9lPG*DV0iiYcn@_ytn@Sjyld8G8sRB2q*4uF0s(n<>Z_ODwa?f z%@;TD^b@px2X18cc=WR;tl!DKVJjTu(!fCH&>?F`Oyvp`t{|_WM9Gw)JvPXB2Y8-+ zANTX%2yXuOx;%XQ{ju~s+93=Xes$7#ff6M~F?&1$#j#JCm(wTX{Tb4H-@?4MZ(2Gb z63pZ~JaeS-DB*JphyEHZ#DSS! ziSTQVNL!6NIm*5%Q*^l!^lA=E>H}63EMJMLpk8bJi9^S*uXW*si4i|C!+xrMF>}ut zo{2<53#U3ck2wX>|q-yglwg!g5nY-7GF`>Rxs~C=pPfSWE=8I*FQ8v{+1zg&h8ZO^Xe+ zpCL7Od-iogiD1ZJPq{tFY)q@>En%G-6v30lf`=O}qspCxPuBOf?6(2a!Jyf8&sq)p z9(!E}`rg$Zi}CYMRcg+j=e^3zbc+ZYFH94H#IJ?<<;IjFUWogvddT2krBM4|G}Rau z$-WH>oWW?0(c&`*Bsx$RVNH$GK#A87C_!12*voMoz)dzksxR1+M%LIp`~K)96hpl- ztke&sYck{BSDAdn0HX@q8*1IoN5_rx-(gPf?$#O;wXvadz*L<3)UP#llLwRTscUXB z2tu|zKibd#IrU-!6!QKa^v4hV^Ea%lQ>a;Vz@(!nhU#&}qUskgi%}bWpB2pRd|B82 z=sf9$#_y`~a>5$rDinFJw|x>@sx@MI!Qo2QeAc3OJHLO@>c2Hu8;&Qt3fBchrmtHb zU-V|3nyl9uXHCxC9GW;lb^rUR%?8_Rv$)lYakb6_YL-^bgb8_yx$|DGrp$ZnE3RIyA6yf;kkQf@t zg{!^Ss#683lliNY1qMpe1t)38*SxNsM2ch%_=A6)!d`Z9*d2o)(7r}f?9}SK!4H}*#c?5nM+yD($unJFP zjXeU#Yw$yK|0&6roK)I|dL&78{DisMW1|Ym73jd<-{4BX;8w^^0wp&lYb%v&!JVVD zW+0n|yEHyog+G;}|J5NU0mT478LS5Z+I226EWx+CT}g7i{wRd2AK#CIJVTJ0tGSwJ ze&-D~gW!H4q37X|2R+J#J<3Ty$w_=_cALfNXU4;y$+Z?=cJ&+zc3*O{TsvYos)?wi zD}0*4aVW?+AUO7Q41q3*nJD6bCn&fHD99d}fqP~@AH3YF&N)V^(X?MlgMwY){X2t^4_ z^|INO5ZP&z76(qREn@3Zo`(+IUb>jQwfzXZQA{e%)Ep`%*(HvC^ESiCSYcQY3T!k1 zDwY5-jy~4>oM`W_zMfb*DpV0_R0AvKufLww=M-pJX@pZ8ln?BSZ|GAcsgtVPjP1!S zn6Vq>NSkBLx_iZm7hRma9C_dUbC{arZ}?Lg6CC)};p2UQfunzZGynbI38C zQF~PHy7S}3rP}2}=YA1dRzq*$C}Vkoj#BEy*e1J-&T7^KY7YlZjuLUzgY=q_IbIB?E`EFEnpODFw}d!ecbka!%i%(w$;Jk=k-p zTEQ9A5|(dTOwDgFZ^x{f&2K@I=>eBH-fwEd9oYDcW?I7ITX-xur-~z@{lqYrh%02p zF@LpoHwrJOEAR6>0>P||FXkX)jmIqO(JN6uk1J+y2Ro;n$#10&ZZ3tG+p>#HDTS0@ z`bbY1hLm@^%X{B8x_q+{ey#W-?{!Tu5{{VnI`=~9txUvPz@NjI_x9&(=S2KiM0+7xx0N{37Korvt&mb zIQ+{ckmP($$caTrIW4bX$COAvJE7sG#CH-fa$0U-Mtsbgm6Yc5%S%x( z=4Fb@tp$~>&`K@R<%}W(X}s?ScgjQ3^llIa26M8Id*WbU^HD`BkZD^#do{s=S0M;wL;mPQ+uouVg+Tlq zKrzs>SIyW|z=!FxZstE~V8Vcr>l5DWG2DG(hfV*t&LAN3LeQ{poRaykZ||=l*?b>? zs-V>~zvilo=s)V7JZ%M5ghSzFi1e zEqgJm;0c37D$Pe=obmYG81BgUxmsZs@c2chy%N5ii7c0_TOv9YKu;-o!N?>APsw%Q z-zADo%YjfM=L1a%ZPm3y_FD))SHmXE+?15CGa{%WG3Ka`6YT%`>>M6_A$)apMozU7 zIP#aLoD%qAwXS@WKk*GGs{*p{{d+vH+UPhW&a$*TJfX|t&{pLq29|LGC8(ssNu{;%=0N!wRf@K>)7W*k}m5S1w$X!?edg2I>}J#1~Hs_hyT@~|%Zqj_NKb{qpbf#V-! z{2%NXM048-nQ+9%;bzOtb*&EoAPVVKHCjfdC>=FpV_x=T(UFn*mKpaXD2Q$hr&C0L zChn{K53IW%SnvcanGWf)P_ZG;TMn8GmTcX7^u0XXU%b15`KQ8{9Fu5`$Xn0cq1qRY z>|VwD{mW-UQB*=r!E~B%zt$mgX2N|9_XRh~7YN-*cMCk^}p;IMQN|5d@VdxlO=unW7R_X3;MjE8M zQ)k*M*-7 za(v{?5<&7xZfuF`M|-#PeMgllvjl||cwchV!sRoF2|iai>}gmzt{7v7pdS*eCOc=x z)P4!_2rs1n;j@GpQ9GqdXP^Bt@0UG8nk^phz_? zlAV8}q-&6sxj*dhAXM=MvC)K9j3iccSG4PzL%V+ zTXqlltUEalZO4m;?HFdN%oFD+Qs%tZ0NBTZPtq&HDS9s3^XTH?kJk_=9)rA|39ftrT>JBr7v1B*qTR%0779+0mLEO=b(4^7Bkd+--Fo`AJsRENf z(m)%M!His@KrU$`m;Mb1k&7PFf6TJrgmDv*CNOI4C=f+8NDVv2CQM}(Q^x8Q(&6XW zK+`I3)S84hS#2?;f0yC0F>fo)CX~j0d;MmHkq}T3U)Ydb z+7J_1C*atU5beiDeB{eS$mH#IW;!seb;Od3co-8z;?-pzY^o(@%ws|o1MTNyK6J&n z4@9Z_iLqadaPNuy&=YAt6x$z-4an7E4?e|g+|I1JcPb`k%?CxUe=6W!A}S4zQyGZ* z&=vJz0H!hkwI2#{?+JFFjkKSPEsUnLbdZW=m=83=p3d3*&74TT5*A4+o&ufD-tSB; zA(|s$;+Nt2A|vrfljA{_muk@)a@5wx5 z*!bNx>U{cJ5h#_za!H%B6VLd*c`$+a8Tm}*pIgiGn<|A3PsG^|#PyFqQz=5FTdD_P zd-{c;rL9z!vWixs_lAly(pYyx{zQ?{%E2(~4u8J@|9;V;JfWM~aekk7TKR_M7el`9 z?_a)Ds@Dk-KJh>A-9a(bl}zOPOr(`e>CTMQ9{gA5L7(h2Dgq=TW4~1Uazx_Q=QOTi z$f7cR(WEt%VKPOPFg>5gjt-AWA;!~-k04^Ar&WA^$Vaz$!3LB~GUJu1tP+(Ooon+0 zc_-fZ%5eJd>(AksHR0Ij$`~r)*ec4gy74ihdX1-1svVcG8FUY%ZBw|>)>}U%^dyfM~Nyw#A5qDif}jHXdCz} zz0PU`e^u(7~@JbdUPkCq@&1UW!up7v~k= z^YCK6SqG9W?r;|~eL(q6bt2h7b@0Cf{yn>#Mqs41F?YK+Dqw}a^lo$}wFzIlolXMF)EfSJ~yR{p>Th1OTLc$zQ8i$5z z=cnEUPd9DE6AE<|F$7Mq!&XWatn8jl`#o^il^8{S($_ILj#YPYzOtp8ZKkliM=?dE z|A78E9`QDNmjh~BfhK113jdFyNR32_vQM_o6+bV;deoLqyaL1$XWv;E>>#4mKq>?! z-mqclQ&QsP19eS8hzrB1)#P{#eo11`Jyo%k7)tOq-mwy?*dQf4Fp46D30J#BsvnpV zt|IJ-&GcxPBCEfWU>U&(T1)Oc7=Q6|&#KDON!26-y%S5$rDmmU{B?j6orW5%SjzjB zCWnC&X7~XUmKhiE11ehL)3i@@H_YrOB0ys5>}3lMt|sN%7LoL~CXISz1-B%Kx<#oU z$dC6+jIkn>;hbZy7qV#zg4F)tCu>DVjIRz+8L9LcPe(XguZSHiT(jB5(a%SdD%Ecw z#}A2#J{yDA!W8B(V=4$0y=V>k7*#CMQs@XwTsfm75WMIE?hGtSHjBIqQlj6))lSlH zo+@Y3MO8?e*)V+XL!C{eceeE*CZgf}cD{PvkIwn_b~Us1z;L`((!&~W-%kA*sIJR_ zTt?yp5wro-QcpnTR4640tXu1c<8zUse5?Bc)2IC46VwbQlzp;mNd}q=E2_%FxfwbU z-PE|$k&)2FEUh3E(_}i&rJ%B)Kbe1#^81GOnYHv93EE$>)`Ym1Z03$P1x9g!?+Mx3 zG1z7JKKN~&e8ftK6sd3;)ei5GJGt@0QRwEplx5XgoyKN>k2-nL%}Cy$mfd#q8@R5B zH(V#hq|~pIfeiIRZneoaI@JeG#)@3Q4Y^cAoIE|pI&|=sq)^EASJ{?2UzYhM?1VSl z2K@=n`2D(Mh4PVH)AbGuLU%Va!mF|3PQBekD{=W**XV}+TV9-yvsawHcK&6{27$u; zId{{vy2xoeh}}3XWM?T9;)&2N0RVXpEsqbTz5tRzx9&hrdoY=&yGV=U1R!b}ue3?v zcWu%Qyr#lu-qtueI3L)-FQ0h>!OG-TkJ*~qlr$@*d>N!=d2zm%}Lgt(0kW{K18K0Tk>bKl+H z2mC9vw+A_+-{!<-z?P$;QXVF2E_d(%-?&~U;t!6Fjt@~gK>F{04cNpOq?sXi^}uNQ ztth3Zi{roA;{7nzK%qIAWH~wo^eN$2EXQ;b!+gv#6TI);81ZhzbB7g`ry>p~4Zkz& zEgj8W*_9Lxa4=zOu2ja;Y*ZSTA}S|h!zDO$JA;2~y8iMC2n#l5JW**5Q?01(eXNPc zui^9LFK@*8=*5Db>6lLDn2tg5i6`WqfEZ8zS+ctf<6-cdWrEswfy!fnfpXEYk)NzZ zE3sMzxONMdVjpgnsO%-s6avWmz5r4bl`%FS-A$i7tp$&%3`dnq@OP8% z8PV(N?-}J=-r#?xkO4ADtE&j+LI`pKpSYTc|A2B$xKMz#+1VJFK%|OaB~>itM8s2j z9>HIwXG=pujBpj4sD}nDqx*)H!-jbaFvd7Ad2O&vrlX8s^{>{j$K#8~KcRlMClcEk zmJ#nkAEGrE9uY#Ws-@I9$0vQ^Dr|f@}T^gIaYvFvqzRtWcZy(wCOB?thV-7 z9Ev=gN)my0OjtN+u{l58@w{n>ItzAtvVP&bP)hB-j*7r}&ZJ#HE}`+pt7M85V~q7C zNAnWsp`MXenq(i6rM<1ouHWNzmy1D~x?== zC5g^QSB6W7h?83p`#e7kO)-k?{KXW161nR6Cgtlw0m*i*$Pf1bwlA_MF&|K>4Wq|H z@Mv5}(_z^j!Xd*j#v|5{5FIoxCwc=*5@rEO3hd{}Dsha*VQ)rYAGrfxv-DXn4x+ff z-=lEXa*{LJki@S6g%#Xm(86<%`sAl$5&}U&)l}T+bfEmFnI>F!j_9(6OuybaM$h)_ z+RegpwwH)K>3S>5Naq^pX{XD&0B4zR4zcVwF&IP{@|4{FY>cDfau3j4LMIwbTu8Y6 zuThSWHRNZPY$qL(nEv%jLCsf1dP~*n5x18Ez(=C~0UAO1mw1SiFRiS0M~eL9O8iOE ztWRaYAO-#e^AnHT%fp84G&|>w7~$1RamQYCqNOO7%m)*t!m5tQ93>+kN+_jr{UcQ{)b;TyLmr#D@{cuugzb294|)z+2z#r zm2>vMb_w6eAY~<08PNf;k{V>94*33O84W6k=Svo8=VFKX}!ey(=$gGU~s^=TfKx*Z5mf*aHECU(OVcHitw|cQ_T{ zHl1yPPl3Im$5LYJgng~*Z))@GmSESOARl%m40V+bf5d?lU#N43zw>ac67#O58>`{q zV@l0a5l)(Kc?T|3dW{*(9~wD`PDwN_<4qeQ0jcABfV!u#+1Z44ZLKlhI`2XiOs6acD>`>{kCE#y+;7UcjVeb@pg*iiAwE z{aLW|31uWSA`ZeSNT1L$PjsgYKzNf70;?ja2vh#QW*P$YpVtqpiBlp`-pho*e(u_c zV7!KFrfgj(!!A;W8u*efR#p091oU`D%bk6um9{nILDMv>j^C zCl=!`)6d0o`%-_PTg}n6qzobX5>EMr4|V555kE*>>M_=*?{`Z-Vrvc(>Y7ibywL^v zR!aKPEvlcou@&T2Uj?y=m?s>6FbUN;aoWNmLlKBw-ab4v%o8W4~xz?d;OSNG`X$J>hiR#lThv0+YU%-4>Hz+o; zRO3Qx-F_NitUyJ?K|R(3F&}2bs`aEE1SNx|Vqm)WE~Xd(bVTb>d<^`W;(s);9|TEE zXEIdg$5*`RGLpo{R$QqZt5G&w@R+}FdwlYCV1DPQ@&lam!|TQ3P;V)9kxX~ttKFj6 z@SHx0_!@@L??xC}M&t%zlF~zjRz*?yZy%6S+^?O2>0N|#3$PDFPd1M z{ZCY+uOm6iI;M1tLXB1+5=RYZ6yTH9rIf2(Immjy5@M)YeHEbI^$wP|1Okv^lJfS4 zr-lKC7QlOtOTh^t$72(?E)@op*s?9H){M)3o+Qh8zoT9pl z^Hjyd%61T6*nBc8bDQA)&G!^4f; z+_b1wT_og;R)Ts74@Yfga}^m7jk-U zr|DN|hz2I@@_RHqR&mr-(Wpe%07G^jvv#D9hpMv>5h zf>p=EkJ3I-StR-~g(B$Jd$KRKI_}w>v&M&AqA*2}9!b<3Nt9kk>md=BAuv`!epjWnTfBnO zdLqb7YDq?JZzU~CGb#6*BebCvhh65$` z2o*BiT2Ul!tmD~fN7iArAEdwX#DB@KCdmo4-BGxv3wL&^l(&CD?>xkC`Ux}_&r*Sf zlguV(y_?es+E7ef3)jhRM3SCQJBC(#mNIXVE$~+FX1D9BsKe9#ERH{*y-QBd%j(6; z)@<_bbT*FM(nkD828*Un!&6ljDMimI?{k4Gz8R$YJe{hU?9X0}l@0AQZ461Nwll z~+QzNR8pGxK3d3xmlT;08<>4O7_iAkzd70OYD34?4l9KYr ze>^uJ@xR$aIRcyZULAc3{@jd9to}1W?FUJ0?6YstjHxPoy3KYo+y-7-Ne%l&080pv zGi|3kb$#+&3d069U=uw)NP_u~8SmC2yde8~QmcFUAb9ntWDI3y00mDg5^U(TrOS>fZzQ*OT-I1-dPC^Soei!(HdnzbX=ZL+yHGbtz z{T{_kw8}me4LnUY_VS=pq{uU~iMAxg!3;AIU^98erkAMDVP|#QB}3UYdE-V-(4^O^ z2o8s}gvW|dD_;`Qk`T_On$4NUcUnxC^jOLLc2at=Yfes2ZRr3pPjP_Da`$wBdb%Y& zxSAEvLq7y|$)#6%&vEFUY?eay9*Qeg6O1;N@*R8Y;tV_5rkqwgZbDh-ulQ`$>}B8Y zskwhRRP%@*g7ku`KAmX>__B!&XhV&vsr_PncqctD2HM_*i>aGt|3vXC zh3h4T7BrK&%Dg0N)zY}qsk&R}{Km9fwjA4bL%&<0DMEUfiqHtu|GRz3oY0n zlc#6Vb1ZAAN85i$+batBRgNL&fIarUngFUQPEB=GIv3vAcNkyC5M0(yHL(0Vyh%-V zQVYYzlP&S!=9R{Uj?9!1s>0~&bM11a+d`u8PmQ=m%V^Q?7*@$9biWoXB;7l@xl2%8 zz#Tqei9ZNl*AlUvOR!ro9~-;Bv|F-_?f(fCgNtQ+#{a3GL+Gx9B$mvQIVP(v>*(G7l)Sp#}F;7%{Mp;)S@fCiGoLFs_Un#RA1z;Z?SPG-54!pagw-C= z$0~31_sRJyN?DBns!vG^8h)E{lMJ`S85YIk3l;Rab$;=ugmTkc@aHb)Jw{QRDORG9*B146uqI0N6iWFD zdfX^p&IhP?rfzVt(ISjtbOHLfQ&oRL)DFRXbnpL)gyS7|KHgR@Ak-|G)b>Uhlcn#~Q7_W>mQntz&5tiO(elCJFHM!1pW?H=zuc*^ z7z9vigBA93u7HaK*Y{=*i2XSeoi5YwUgrR%Uh#r_>_JhLvn>{Qbq*_G^x~eTGe@6h zRcoz;FAtWWtv*+#$883OQWXr?grJVKZWy(Os|dY-bZUnS(7xMalFL{I_mh2+Sx{uU z--3R1*f~q=ujHholT{ba5>2wvltfeeA5px~FjmD*R7(nh*(i4Kj_eu`)-c)-;ncA9 zks{`7rtev!fg^>LOm_0N%t?y>N$#EbjeX8&T2qr4R`MCQ#Xewn(~)6M^4+4@3GT0P zGBrg50v=atd~R0s-1{0wHqM7B{_D^%0KlJV?R-ph>dOF2_653@{d|u6jy|xKoz7K5 zc`oW2FT8G+6pk668@bI+ji}B&RPn_?R7A;`7iG^3bX37@q-WLjdbimOi0Vd11Q8g$=X+`mG zKhd*|LpWqIu&PTU{?fC<3e;7mZqzj}S+wGlFH6GadZFRsGu~ZhF?nu{Z)^v z=z^|Nr5Ns&&pT#-B6H#Rfk8L;3;o%+Gt#dn%OIlc%hally}bGb1y1@EuxirMlP^$* z=v*arJu3;-ob34C(n^3muGfmErSvV=da$@a6v_Qjtwh?YpXP*Jh`(TEr3O30;;&}K z8adqp^7()#kNj}w)i}gnG!0iuMj!Fmo+jzk~T0%F%C&NSVuN#>)pbMtNKv1mP| zXPb!DN4kO85RTy?;kn>JP>5*q+iZrS;M{x+KUJkMxw73qpIH}f32h0v6&Gr%{jyzX zLmZkJ_rPc-aFX-MgRh^L32>&SN<-QfCw`|cp&6B3@)9v#r+40_lT7hZ3FXs29f zc--?r!UL`e9}q_V;g6?I?Op8#4|Hu8&8kS)3PZ2D)KT{_AvL1aXSvsEf$-UPWyVxx zzU=SHeF<;+5>%LVxhsb)VN<5C-u1vKTiBAL+3l`+gO9iO$pA`<}QBQ9MFIiKWn`b6BB!u^oEfl zDixagEUw?&w9GuR^I5ivTM9vIDkz!n&WM1VubbeR{#|uH`5*x9nMI)oaG8dxihTvn_~b+wh)hIA0QOJYk4mF%NsVkr?9RuouNw zxMC}~9mqc92>s$oSIVNjwrZum-qW|E*=8lVAIqtD7D2wt{)tF$M>BjOPIlHC=X8V# zC@I2n|61i=vjMx0S#$>?QoZd`(CQ ztk|f?e$FBn^yq(h~ zJN6>FmGYxu5o(W&1b*@8_fx?P-OyjTuZZjDWz`Rb4z4$C4nAFD)9F8gBkPBiww66L z{Z@hP|7Cb9D!I=}XC`^v2J(hdv&sz>I`SE=ogHK_oTNXl*BDycl16`^gkqz^SmT$U zL(d8;$Ro}m7uR@SK?f5C!*c1%b8l9gt-)>IEf_rk^F+G`OOu^vRqv(uzGrT|3sKLZ zQuaPuSW`X5myxf#CG!zYUFt6O8+IbKpCyY3TjIjHlB_xIfq@I{*r#gJ;4r_{nIl=J zZtjz!CmzSZ%}|P#-ydKH1#REs2U8yVg9^#?D-im8y*E(fCeD1-y-1*k|B>bQf1u%_dpgE!vj1NS-#SS@_2GA)fsU8 zK#t5W&g^YpuE%^F&0{&4NRnNwG=ecKwo?^*(#f)>%7PPuc~%uf=;Xl^GMp4L&qMIm_<<~HAGd{tM?Uf% zD@4b{RGPh*H*7&JQ3DGHcjIPmDl%%2`xf&yh@;%CG4U+!>S(EZB!9s0BQkhZV(}B zk01W?w}58ey*4|qk>XS71@XzpXVlMG7YS0s<1)*<-D^I`%&r9Gv>>S@$**4|Dn{OR=5;z_mB z#$_>gPrj2>sKa2ETxZzz?Bn%Y$Z@YeV*Kyb`kCiLU{Rjkm|iXAaK7*c@>_l7=|ywJ z_MuOu+x3P>!;J!&y+L4?L)5rt8^NK4zTP*NiNdMbs)Hx?w@7NbMI`f08X;f7;-^-; zTBUCrXQA!gH=akmM@exTCry+tzJuwkbGMB8A+h{PKU?xvXY4zJYI)4A@_E)~FR#XD zT&V`yCJw|d;8MN&3hmu}y;mkz18|LswU=a$exzHs$;vLVq-Bq5yNKSFO0eM-}A>^Rd~_~`oYJ_USEqB?q|sfd`_ z>TLqw(`|{_o3*^zt2sB3sw2$lc4PQ0&^SS-t4fjf<8r#!H}tb7N9eO{2Sl^C{b#f7 z+gb-k!VF0;owbuN;fnQ`=8C;mSjFEBxU=)cnMjq5ByL?R^bb>3_AlpGO+|G%+er2U zgKN4=-y8l|d&7{j{iXQN$AURTwHg=MOr=C_SMUj z?B+b&wT`9V?kOHT+$mgsq&IkE_K9_Uuo28y9=LJ*cuG~r?T3G>BNZ1s?6kUhO9ngr>ygPeIv|&{K zfc^ki9bMC}ZwYZ)dJucq7@tCp(7K zr>Ms5rFmtm5MLWer$)7`0z;AfIq9o|xpq1)7fIr~DTj(*@y+xW4PbQ<|`qg5o3P(yysVEOP(vCq%62Ddq0zhHraHvwW}U>(%t-< zfBnK*d3^Asj%FrS&Cn02<2ZixW1fM+>&DT}M)4(`}v7iSpv zvV1v)9B*f>x>w!w0(&&4`GJ=vs$O5q6d57vhNP=63k}OLfy3E4(pxZ(0lj>>P^kUB(-r zl8^2T%U%de-}7xhR*8nE%&#T9(+TtP>;)vf&Ln-2>kcBVPMCjCCDiDb@oRsODq65C zdmn)oeJJ&aS#YrL#4guUjt1v><=`SB!hpiR5PepLWGg>=uZH zj8aI2&g^`OyUJ`O=h$9}Qm4EJWwk$&O>_EDYlSY1Y5Ab|sbW_;^twSDmVpZt0vG6l zjlU1Tfzm4!@GL=Jy*26>JS*#7r*wp$3LnGskHI0M$>7k@Z>@oUx>Q-kn?QJ3jr2T= z0hkn$E=TFL1P5Z!v7u9XhAg4dHl1pJGDQ?`Yl{wh(YoEW$>Yeekn* zyeO1B%cPeIhlvG`y7%q}Pqx3h^EmaaCkMca%8?SZYdD+)$-j5ldjPHVCf!v&i6qk2Gwul zR_vdyir2S({6BfJ*{aa@rVga*BY^+ zVsJ@tCkmQBgRdcv`8#TM4t5P4ohZO-*+gH42sPurj;4z#Us*kq&TTxVbI3 znG5#D2&Ubq)iC%lv@VE)^}Yr$4@O5v176gmQT>r_bN1jtGPeZm0n;37OHIlDoaO>1 z?2b2LKsN-s%fuTR3G2G{OVsq;h-tTsPo|ga`NA}&bt>XsuMfchqj<~%VB05Xh?s0E0^cO-Rg@R?Xu)vP`W#!JGt zGvRybT>tL+|23I6A9sD9i4rAPQKD|mB~b354Hy;f4Vk0$a|(-Q<$%Q^D=P&{D7dh0 zyi#b?E5Tdl&!ZvWaNV9b<*{McBy`MB>GOV|6$F=Gy)ur6Yj~alaf!lg1_~ompoMeyOCWtWZ zm0$2oRug8u(WBg=-8Z0`d0P$_FCUdtAAEgYpA~HQ4i*%PHWY9}vFLJ7bTWDD z)Ny%nEW_iZbdBI*<(Uni$!sF`TybYSYQHcYYQB8~MsyHBZj)f+8co=;hEG(!h$(xq zoLs*3T)m`ii@SAf$0(MBpfYP=--t(z8MYD~w_l=w8t*A}n-5;~+<*KIZlU(ZWUzG+ z&-7hozB|Q!BuF+wmP5{$X~#5Ns9M6%+_ew;zRWQ6s3!Z-dK%m^fn6RyuhxGLRMsFc zoM3CIUv*i${+qR$neHEk?ezF^d&%cjrY}+$a=quHsaHe$+>rXkQhw!r2Ha&ZX{GmY z<^d;eC4eBU_)>T&A$)k|4yWG86ZPo+5O}a7g9sRQ zS+_vkVkEs?a&GGlul%QcXZx9Vi+&rJ4Hx?itIPU4UE>lQ*;FOamUapsA+FBW`cIL! zPVH224@b$6{UAHUVqSaiTlM7#=z@;)^MWv4jmSt+T9@Nt%8hBefRJX#yC;N)NaDyG z+jGz4wXu;T$ZjmhNi%IgEVkq0OaFk;B*?)U?a8E>@Ww=(A+)5O@Tos$(_g~1wFU0Z z!T+-kB5#E5j&O;tw*>@4`9Cm5(F!VaSEq2*p`GoAXf;QM65|`<7 zbU28=Y$zTJT~NbtMzFLZ5mDGDEB;;#-~5|Ub0A+(Evn~2+JaC|0E*hwRDa~m66QbQ zc{|<`;Ag?~hnsY-sj~^PQ~wtmk|Ahn7=A)8%nV=TO1{YTf02u- zEwED!uUW~W<=7wPdZ4^uqPSqpz&F(xiza7iiOmPKk$V~husJrZTS3;>#5EfLWipX} zY*BD+0jn-7lt&_PC#wOkT=MZ*ozopo*Rr?Z6j}DoIf!nq_Nmio09iRWKx!HC8MK`C zh*9V8kuc^vwo3p#oi~UKI)y|+txg8qJk1_Bh&E2zK3D8xk1x5>+)ck{6{{kQGW4P1 zk@TZ9^jVW>xwv7d@Y~>L?R5ZTo2%6XUD?)sVOnQ=kD<2}9=&LQ@ewtujPmevD6q=O zAu3gMZ;q-V7mc@kk6TI=yD=;kUWf0_&x%aq5xncf-GzHynK4m_?YBhh8NyjzZHHMM zHZ;Me1tc^s0yG>~W=oO6&el8m%CRXfH%P*iSAH6A#ZR{Jh}MJjdElg1E8(AuDNM4P zvHmR6C|_I5p7*CiJeIj2*M-fM=HmkK1g`*RMc1=-nqEwO#9H&{Mj`yDV4PU!MoZrXe&LsuqBIPBhc&Tn&tD{>ytDM2qNQs$V_4RyX|{ z7T5W=pf4g%zY;QD4cUC4MT|)ZuU5jGItA1hW3=8<=KgRd!f<$5Q6Q%2Wy_HXLC&Oj z9)E^x6V+#*PYUT?v}(Lv(EQXyRQ#XA6p>p-pLWXKP)_Y+oO zdX1Jyeh5|TglTaEmXu?iWi?Zdjx?i>O8Hab5|u$`n8RfHp~@OCK;6IA?U~uzH3RC5 z$V=yY1?sA@I5s|Rqpz5Y7R^4ml4I9gi(P4tWmq{Q;r6a1emq=0L>{%=Hy}gN?hZob z8cY?ixj*s1d1fLI8=iJ2#p>cNS8P0vQfu%lipkZiBZJTYWPd5(LbDCYb9wQOT7=#M zKT>{o`zjE=QMtc;Hoo+=7+@lWn36#I(dD3=I}Nq91gfg41x^JD4i4mM8Dc?lFrroW zJ)6ntnVibTCtMDtfLLD1#|m2vhyAQ+>~(fp(3YdNd&XrfVPfL9+}hftPUFa?69^aw z3ksYjJv%kXR^g(5KR~{liJ8cDDw{Vhoj);r->?aQ!velo%B_8uyH1lrAJ{>w-huYl z#IWHQ|LFtusaW;K+4X^9@Fs>VQ5w&s&nSN655F*K;G{Jkv%7}dQ{)lgo@);u+- zA0R%ZHkrQcvZFNXc?tEc0Le*A#Xr%{NjT%PPk6rjr^70lQ8c$Z{4H8*F1oiD3tgWK z5|%`TC@}fp(S4lAxIQCWz9}O%i=Pw?-(IWu&1e6TimF#V9{rz9s_sPiLVWoV&=Pkl?rV8D)Ws|XUdm=Vp+_59vW%`AYOt9M~ zXD2sEYX){S{qEdDJ2%S+Jw1mZqT&g&ghCz8YAwgJeLHz0Pp&YmG)A)8?l;E~*_hoP zn6icvzJzR8*IL9ELei3xSTKbU^GU2^RorIH@OX8+)*@OgE#XBEwmv0v-P%+1gXLBN zZ4OimDPN4lUiL-NV}iSrqo2s@D3+5^qN84-Ozw_e9{TP8a3fxFz+%{-5Qob!v#5Vl z16FdmEnFfW-0(Mq=n?Ue?cuOY3po>Y)bS@yN5J60mw8z{x@#(%WwM#EuU zlmCXg0)Q`U6#MH=u*D7c86Zs+=y*G_rVvGOKgFkfuIIXyq@#kq$*=){sD|;4AC5fR zp)2=CYIRh*_PajF6Go4iJ6S(6Xu^zvmFt7|4<;qTQO*G+Jk$}x#JC;+Q}|ytG<$26 zM;mcUh=qu5Rl8X)uHOAFIxg1&j#mDQHgvpLCH9MR9o%v_a1HEL`M zjzLtC6<3ZM+*-WkQ(W~*q`1UoN_yV$y<>JZ`IQ(AuwIZmzHypBw z(@g$bemG^I$=PxU)*nr2(vE5fqw|O1zs|x+jVCdRzD4wYzyUa`U-QCQYcqC&TXH&ulzO8{S0K0^ zht=-GemRN-n%w{B%JKp8x39F9P;>@f@J?*Pal7jl?luZ@!5wlVAZ1Rf-pL=I{f6*- zMBHfu$Ok2ac6(Xz5AWo_K*o5m5p5%*1ln{JnNS ziU|0!m189t+W%eozu#!%e{ZzW9VUI=BQc)_82kAnm$VnNlot(aoaK9sBKfhsP2>ba zKJRBs{P&w5m@B}d1KIq&#zWkIKrZcGBqREH`fFoT&KLyZLs{Rulx$PK0#!a8LFkxS zKiv;Y4ezF72%W15KqfGZb0}Wv>f}1k+Z@|rm8c_+%&~N@r^v}~#i$DMP9X>5r(>;RMEqIUg4+3z6=9o;UO&19P8G^qvT8?Pyu zzeuY5@WW|J|j)dCCjW6d?95Q)&vKmC=@m?sPy zROQCr*Mxm0mPsayhzEUTqnxLWT&pjp(g0a=1pg|hqcM?iTfND!O-Vx2-OO-Kzad5x zQiZF^m;Qe7Nm;SfIStF?9b&Z_|JmMqQ?PB9{9-I3k@S7);$le?YKow50~a*>_Ci_d zK)Lt8KKersVY(x+m<@};FI0K3Xn`b6y(#Bj2gI9dJR8+gm1u|^w74xPFTwvq*4w-b z@W?p)R^&RFf2n~-yEb|8%$SssdaUvu@YsVSJCK<4yi^i1?Qi{NL-eak@_Rpo*rRY&5WmBWoN06F8GR)qErX)anyk=X zU${PYx_{s&sQ(M)sw=$C&r*GV&gHssp8E?N^f5L#JP(n;{gKCP{#D5Qokz4KjD&W) z^T%G8!dod+7TS796e99-j87#Az}a$M(;}GV<*EMUs;LE1WAgq zvNTqs^Ec927L%(b(}J>y+5#DZ5}p+&IOBAV{Z^r+yRK4{Wx@%S!%^rGc;@BUf;nZ7 z&wrTLCKhu!8d&wQbo)P>u@bNu2mIsk`ADXGm-+^)REh)j0)(b|>I#6`;$)hIxkGDq z=|z_BHfPapT($xJ(6;#{%6WH82GVMXf#$z^AqEsEw8D$d66tnrKpv!3nG3g-t3QL^ zW18?W39#`iZsf&%yPpG}?)+akQyM{H;m^mK&8y+CL9c8u<+IV!c1+gyVf>BJ25I{W6UeSN4zE&$#_^AsxU{GRm%_&m?mllf6uLA`Gm^HSeUS@WWC#t zletl8j?HIvb{II~7Rp$A4W#eQ3{_&V z`BD)b76&lPY(j2&BGlVwrZVKvk>FU|kWXT>Q-El3z?fpD`-NS1#p{sO3M@-6Va_TF zh^jA^>jdBuccJa;zozx%y{c84N2}KiY|x1v4<{jgEzbN-@DT-Yec+#R7s$srQ)7mE ze^8D|Nrgf<*JQ=Ejj2a5ps)*Hd4s3svfk0XhbhX$iOiJs2?|cTJzUPI7&rpi_C2gI z3GP6RAU*aHW?O+Oq(%Av*m}#LxT0+f7l+`40F7IM27lV%E zTIEk2VcOv)(pm6h^t7dwQiy@Sapk=~4d=f>%16p+Ah)bt~~AB-&_*uJlXPA=*{_OF|>&KWGNCy)k@Z(x?de00W z2`0YshBKz|0w;P>e}o82q#_UUFU?-#<3Edd)D{A|=m!05sc{0!KLp%7T_>9$<T`qbFShJt6vR>cm>gw9O>hs0aA9zmu!zcm14AYWQ zXxmQ-y?^P&Hl1x=?~tNiGtRuwh`Gwl(e6h{?M`o`0kL?L)ZWjC=F0Qurs4lXq}{y* zu!YT~^q%vLC{k1t{kKP)fgykc+;AQMUp3-L3lif(X&4R=&dBH?Fz>tQO<_$JWbf00 zc)2nFuweT16zPo-6(sX<6X07P$KEOm#r1Bdkg&)XhS<1@sXBE;+CzHg8(;Rmk1TZbs7_x?AR(uw}0Bi~?VW*GApks!9 z3Mh${A#VB-83NtWLf+^2YU?)vsA)MwMS-fbNbO*l@7XlSk?gi6Z!vepT!w z-r(HO|A3sBknXrv{@ocHU*!9?!$OK58S&XM=m;W9d&8^3C|Yq(y5aNb4wnJ zy?@b%+o{pS>LU9Aje>W`dbYgTpRoJd<5j}~L&$=nf#OAfE)-XOF@FND_t zl+8hopW7nQK6tw{KY6od6Oe~y3Z^V~%`op@CM;)SzkF6?*s0VQ8QIx06Y)U!VL>vf z5$V4P@;A~Fi4kGr%`|SZuA=G~S>oCJ{%PalPgsN#Z{n56V)k_4_|f0DbYFP^D8lrb zvd6m2ny56;&)$Ovx>{ZDhu*gVQOn6__fKoD%xV=nya3@7^l){#jEfsb_fa5P|L>)e z*VFQAUx!_rg{{YeEapBM^F1q<-Reji?kMRe->AjqUjmf{7d|(o!y4D}dL**D@dW>W zT{H&)-FW#T=1x^rl`#AOpmZ$55(SRm9(~0E;Z@&1vMutMZ0a>;NWRbW`5e$4hCqVy zL>h*Wj)DvTAo&1jcxH;=V6*b4Eh+p_BtmLib}=DhF(G6zAATcB+Fya?bVBuro?O0uo84JC z#U}jn(r(^?4D=PTum_zhq$!l`_FKh5SY z-N8dNk<-u$GV|b1*AjZ>qBPROlQdS7EeHWtYKY_%(iepfAH&s{K*`VKn`xEC6#*LT~UnvZ{yTkIX-Jq{V>K=GM&0DZnWCxl?T48~#-vnJ*i zq0$a}6!82w?aP~xgEwtP9>jqg+BR~n3O=xsaTS|V?h~==p_ef0Nsf5QeoA&=->b@g z;JNPEFS4K+uyHUD@mVB7wkP}o0q70cJPOsSk_R-mqsq%6 z2Vmm7rA9K@#COe*oe4SJ#RZHIu!RN0od*k%RY9b2n%ra2KWa3BSL>CE9!m>Ep+T^w zsf@$D@-4u#B~ab%9hU@gz_DPSu>%o8__#?cKv`Mm%Z z-mUvN<0t!0Cn<-Aqkj9zs}}o}ORYb@bpy2B=0P`X{O}HOyh&`<)Aa8P4ILR0`oe_6a{OUAW>j+8>fxB8 zT=s43{Pm#0<9or&Ct^GTt;u@FrHaWXX>D`piZm1HVKqa6n@tz^?0bD%(lqpLg1pl) zewXU_J8^@W71erq8BY#_|L|uBqalO8+VMSDiEA^hJ()Ge_UQE8H)8%7yQ(DaF8jya z^x87g(|gv}DOTN$g_v8fjS_oh(XMbl(|}|#cM3BhaOLIY$4?W-PJ3`ii!6W^sch#t zz&}RtmToMaw>vt+n(V)K;oYt+pmi4h7hYT>g=EOX!mBOydrLB&hbSw7N5=@6f=TRP zAH|1koB2+ocrp47QToFGttE`|Tmr2f7<4O20Zh_Am>5AX<@-CcHEPVK0g`j@<<(e{ zQ7Ft2NNVK>-lQ)3;8OqELc}3|=AvG~326v`8Zd;99D+qzDrK3`Mb`91Q^}RLcrQ3H z@Myp(L#QaE+lcX0&nz)Gd1FDp7ML5DWjI`Dm)*LH-SaF{)XQW~gpBRXxYz~Rz6Y1_ zgoT8^D83;kw7Hqgb*@b&?G^kLf^%~;q2rBUWyn4(e7o2ac0V)XFOwawQ?=Qq<;?Z2 z_d~;JSL$_aDxs6`{4MipTe=XU4r?ByykCUf?`lNQ zmLH+_6ZocHv(l~TRNdZ~zuQ`bn^Id0@$OMIs_5__yn?tZj zuoXA_uT>*mn}1W`8#?8n9u}G!vh!v6yegYdGhIkXp7cKhA5D_2=P{tVk;)=8n~gz`S+;j6q8LLC>P+2E=tkwW-Qp$kVGE8_0{tmeckO14&irs@SNR zXV{&uel`o(HSd3Bp>r=1Xu=3@lUd*-Md9h;%?5CLMf3gY-aM2SDVTz3~1}jg0j}=dMqN^FPfE%*Cu20HswxEriQoAn`F zC%vIOp3N?C=YHgqG00EGJEF)%h#%ZZCo%Jm@1jvjUqCn2=S@#ly9d9ZpnqMY^8Zj6 zWAIcTRhf)&uDC71f|9S)>vtJr=Zl=w4k1~CIMXtFonkZlp0HGCYxu2ISamQh2Q@l1 zpf{ACcC3{0UzD1HfuZBWRLg_^fSTy3$>JNk6C?yLJd zuKLTq?DlDIN05XA>b{h5C;w_424j(w>I48dc%21ZtV~$?)ONqc>wo#D&8gvPZ#=jP zH8`Mi8;Hk-+f@;AKkopnjKo&sh*Py$LgE9WI(mB1K+K21bU_pyGqVxl#Wk<bF%sc<(EqW9;dYvU3S%34NZc51_#qVyXm|J1kr}JG zUaI?Dx`*tL#fkIo*K`yS1LIV(z-KM#C+%!(7cq%!^;NV8d4SyWJ#$QJMvdej#|X)j zn1ay8U+2%8;6`Guz;_T+VJ$29+=^?K`_X*tTMiMf1Bm$G@_j#@fk1gqn1uMy!zpiIb0#dGm6}*~B?8wWSGiqz7SXT1smf+(u*kH; z_BTX~aG)S4u5E|iY~#bX1{zz~mqnn@1?Z-ZwMr&|zuIx+yD7wMIjMXEhaX4#P~ssP zf8d!u&o65t1pY=OQ~s1o#`~6f+oF#Bj+rTvmfXtk%W&X0y<`v6=m}*>sSTL+=amo# z{+8i!Zar7vFLH|LL12va!sVJ|r?T&0EQgi0$G!#Ado zC_#H|WB|~{^5e22n@EJs#@Kr%SCYqB5X5y0ocJR8E?f7H=t5Q|(6V$#I?Va3IzhwI zWM`a6fV!q~tBq2Tsq-;IEwOq5<;(y5gARxnA zD#0=;%0qE=!JI0Pj2!4ep=((*awoDRKx$^(V7sZArzB*nIl>r;G*DoQ?=nM3 zXS*e$ksPtSsIy$^0ZvEgVLCl zqPQ{`2z@v)b-*=*VtVW1#ooZ;^R3O< z3uIiZ6XHl^BB>xnxZP#eI3o6MJP;~tQTot4dX#+cSH6YPK+0u{iPeIK{$EG+wV<@q#RTF zJnp~RyFplm;D(AV)meDsP2bX|5)lEiyFLSb?q+`N2}N!H)FGv0IlhH*K)owj-$4dH z@Q}W}D1r8OC+E@`ki^j`hYX)%N^PLyzdyv$XYK-*ZyPRr6N{-05< zuldf#!2{K&F+?+SyYb-02e9McF#x^t4`jv~=6N;R-g{o@wENry^Poe)^9hhXtc1>a z4VZq|pUur{dY9W~X0a^@yw09S z&6)nbUTJMO^>uPqDJnCXhz^B{ouC#)vK)jTh=3o6ty^A5*rOb=Y@l*!ICl>0tuXn) z+7t(+!TmHL!&F9?GnMM29ju}X0sik)MJPUzHc+msQQ_tfgr=u);QwkwATOvEl{I;m z?hgSd9UC#Y)I9w7CJ(C zXc|ywAE{9ga|NzHwf{fJbSJxB25dGS*@4)ssBT zEYAzfRW=-?e?)4qq>`9TViJZ|8;A({6;D!7iL!SIIqJ5sq+A+s?3x&GjpC0u83~*k zL~Or#Fs-y|#&%#_5FG2V{7Y?v-VFWW_kD+LS}f=q(gPu%VN3GqPwcxa0dvNN)nPNV)@x8MEssuZ{k*84_IrBC|wl@J`<=XCJ<2f({I%ZL(BZ*&$tThgK|eW>!^anGN zP6YXZ+4-rdfKGos>CfKlS4)cOx*umv9yg<>Tk9LdKj3&9zoVJ%bRCBbAOktdCW zyx|Ck-JcO>A^gQcY*Lw;P&o;5WdgE>j=7j>tqd_Kh)pY#<0~~e?U!!s`ETr(RQ3sC zpPJsdPE-8hVij;@ZC(!lrqqZ5$j1G#(YKAi%N-?9rF~?BFU8DLaIS zCBYXoI?(4B(Q3L$mYxy56;X^((3dyx-V2yeZYb2+Q3ZUDBsOD`aIWn(#;FOOr@N%U zZKyKFCHy2Ar-y{oo*ce;tb&jdglpIy(yNDEn=u4o$-G>T7^M5_OcNq3A3``FC&Bb_ z%7|gh*Z~!H5r!D}6(}^4fdf;GxB%`fQ@C@BeM|J&k|CsK>svY@xgmDE&gxbQjMIEG z6P3mc+=9!?$OeDvwSkpDWQ{UwdP*b(A*p1K6wIIK@7pW@ASki`;D-_R5||q$k0AY! zLuXgN$fBW6CPFj*h})yl7y8PJ*PTUUlq~zix;aUSQ&-&w^0xiSc*MRJZ915 z&6R;`k2C@Zhl7*YoR(T+t+Em+_8 zx}%6D!O47sA30>+b(ZhX82Ghv%x6xsuSGo&`_)}wWGsaQjSA1rP=LLw@7ai9B)tdn z0ZY=9+bI$^XsjnAnFxz*{pdvcxrc1g{ z0Q5D~-Yno$83FgZA^OpiVa`293&LV!;L29Pqms?iJz*r0i)~el2#6sc7TVT=FYJ3x zt@E(^#p`Ku<$lr$*jcS@){ehzlyK@cK5v;lTjj$Dzq(@+K$AB6 zUWlF#1I69JC(JxAI6a$hdNy-_xTL|9bfc%ZL5^o1VTh+^=T=={zqPoN6KP55@Z|Dv z==_=gX9j|{3eqb1O3x|`Sx#`*oY!4lSk}%NYKwGvN~gaD5t>g0JyQ5kK(*U zXks6aJ9{iZ#f=19dG8=M6dadAMLXW;=trDJAQyyCJeK`m6PE97vQquGL_scZHRJ$> zNtD)`+Dq7EXC)BF+kF+P3 zJVQ(h4Hr zUIg1Wk$vJw`1GtP1-*YzINr*B&>-nu{1OnOwrf%#jTX?bMJP;DWBBaA5G^d<$I468 zXTcB)U#$EM>0=EcIZeM5AW&mPN-3lf1C2idR6)=)32n9wZg<&1pssM3$v_{){(HgK zVx9t;T5uwSHlH?AdP{Z4we8fzATQ6o1RM2OMPF{d= z&z?*wM#CFvSPJUK$D#WBC&1GfVYXjtAqGX+7{^uj(QxCKr}GKaHzgWrj>0poV|1*+ z^<_L%ZyaPjJqXiR<1FC+sV@&C*SNv;)o>oOYfq5^*RASK+S3|?%k~}}sWk+4=Z67; zRRRuL61Z12#y6aCjVIhrxEVtj@)w)gX&3!VxacD|=F(Z|>ejfZo`d6&<)TT#`0h{QmyA2WDUM_RdK9qp!2VLMW z^E}JvTv2B?x7^ZFQush`dmd7Io|APnh(Wyu=zS)k9TzZ!LUW|6IqF&5dn=({q-urq z^UcKd^|jylUQHqyZbv3Y!j%DFk2SO~z$OKs_+rM-rv3-iJ!NzM)uX4zpKS;Kbxn~4 z)FXKJ94#R?u`sXWgxB2SalFv94#2y;+ONQ``B&0f<(NQhG@}zO5NO4;Kfkgf=;U?k z0W^>QcR1g_9E~RwgvArr z#}=r@#)Kw))dSJyIH*q(v=-)Lx{j8Q=|B|KJ15GJFSG%RBFvlV(+g!uUDwqYUnj;aHaC7Euu)FUywps)o0dZG0lmx5|Vp)cw|u*q8u0K zZpHW!3cqyuU4&@^jCSNKZ?LKz2!0y! zvwcIZo-~Y{bc$?ZO?-b>9NPIUJQ^g|Ola_n%A9rQUg;gY1cf3EOVl96`Hao28E;AXLfz`(WoxVIv?uakhJSa<2(JSo6z%ry73a27N`7^}@ zpMy&&8;JyL4MD;ggC|c$Y$KZ>8zdZ>8Xqp7^jwetB=m~3%NE@5vPBkKS(tHQOfu4P#J#B95 zVl0W|&(B()1CefO$YJMftxpc^c0DwvqIW!VDSTLO zF*K{S5jm;Vji`Nb;k)OZw8P8Z$^A~XmQGy>={yu~J(UygPn&XpbrMPc7I|4X&U!*exgA@BP9q;!)`fqf~{! z5d8E1HJ$$puGy8;dq+Sp{@G#j4tI#P09Db(smggvd89u={{T7OU)G^sdbhO?8JhI}z1jI>K-k2L-A z-)u?*hnAuHDeF15nPPJu4yg9{yLX`OC=%rQu)n2t?Xeum#$Y94H_5W4S6>LBQ|;(t zc+0XkWGA*@*A>?97pvXRATmB)ENS0-Q{|Z*&|fi7hxuW7{M26@BTKh;$xj)rT^v43 z93v+9G!K<5?X7L;TjmHjTQo4^di4M>SlwXzx>)b|cu}bd*Pw}%q={7cZNx{!9@J|O z`fLyCsU+^HBn+}?pZ?g%a>G8zVw2cqnEQA0;AQHMN0~*W4zJv`aY%A2)DoR9t3u(7 zI!R(|m4KN`>Vu9=CTjiYP_Dt#KH2sf^}`+B3T`t2EO#sU3`%nyipNcvBb2w!*$yKN z@wkXt(BH*cC#sX6I08xFv3$QqQPfss8q z9wMh!&+gyyAKx<<=9%0>FeThG{N&v?q0hD5pMnwBUM;7{`_?pzds~DCmyo<2(#p0J=E;&#GWbf3cA!=Rb z-o>>>e4+gn#U@~G6(uqiwxwOsf0-~#h@66%%yqwsiFmUl47N*bONj0Qr-9$5k*Svp{JYi7Y}xCB-n79$z3qjwbmCn~y; zgzp9=VPIwJ`3Qr_un8Sq^w!_xYL5P;mTT8our@kyS1MFUhi?wk>cq=rQ$<0u@cQ_k zW6tgO6O7GPm39h^}-DbpUwtXlv7}|=vTC@oS;q7WM2^{zCaeNzvjo? ziZGspXidF|s*S7D(j?$eDv`Iy*Dd4tDPPn>su;fH#5sf6D^j7-g<14D&r?j`$&xF8wHdnqVq&b`9&_rUnw&Pb>JH}AXACSx;b%BH9nI?qX>eqE+MC+@@ z*E8=qD7za|YFb#uY^MzEmMG)vr4V7fSMtpDU+j)w2f-3w&!U8G^{V%Kcz*j53u#s% zNao?|K|^^Vq_*7XuC(z;wA*InK{QyOB|r5Pz{CQoB)+|;6Ha0Hv78jsMnNYF@Dv%5 zv_J~QD%Bs29glG=Xf)D8C!DsJ+cy0&$wE8q)z#-^U7Ve>Yii=~V)~7N{KcVdgMTHS zND3e*0Mu{y5nkJf|3>+bPnKNCALdX0Wrq3y;Kj-^f4?L5u8n-55OR}Oxn7}bf0^%a)VqFYa5=))@xDnoCUSnU z^FH!5n9Oa^a30qu3Iv3-m;dF3*=|?4F3jC#uGRR+t_Vc74W`K2hoy%gBot%y8gKhm zquDkPT)4kooOCdeswhu@YDxH|ug*Vg}{QM#pi!M8Fz;4~^jRA!I zC@NwZDhP%^4k?a{yRqBx#l5VKSZ0(XM13%j9_m2*6@4&JqL-D~{XJDfKh;rZUp~F= zXk`qgwNnOk*XfJY6o&=SvlaS{;$-72=(yNlS`pbnf%JWCSXMUK)GE@zBN62R*uW`a z!c}-QZRt;aycLD|PuQh-m_-A2lR&vxEG}`O1-Sf5yl~1T+YqCo59+yUbQ0iLH0_*s zxXyN=GE=a@Meqz?2xgSXII3+eOudkr>!!1{sq?*SBE}y}3ZHOL%=d80Ozm3;Ai*#Y zq{b&8=+@;@&%&gYN3{}7Jix3dUr;gpLGk;WF5LAqvuxjjU(*tPdQ-i-^jXZoMYyPj z$R^@ofMFsE_&g3MRF9l@$i5}us?<8 zr|}d)I|zB5n9u@dFSh8pCBTkAUOB$`MJ|D!N@=rvNhtJpUy$e63`r07JBJa1B`$(Z zTo~=ou!5gqAC$vBD235XhrMSPb%MTzV z?0!Vdi#1@l*rcL>@=9lqO5FDwYIH9QzSq@=jL|Nhz`U!w@IdAC|vp701W2t_UB>YE&abcSp`S!Rkv$@ zPukg8(%eAPTc4h`51}^?^8ob-`i?1Ds^gJXxC#4i#SJT(l)sP4d-u@m+>QBu)JzHh zp9c4nrS&`hyt*KA&iL>(v2t8&>fz5o&B|yEkiyIUzfyQdgZuVMy%O18`unXoyKTlq zP4ZZivSiUN51h3uIJ7M4w2%_a`}NSVTChDZ>Jp&^+(5Y`k%h(I>K<~BQN@tEIBrhedACemGIud zaXr~xGA>KCLY+`?LN5nAPf-5g+h(fT+04&U-Z*^3;hM{<(yEcs1xC(X1bKP9^RD&r z*`@jKGDbgG*zt|ldw@;X>k``TA?x!Xi|x0WJvp2oD>^lXBiU(xp;}<*sjw+ zpFISU24sJTwP)i5!Y$eMi74PcKFERgEhUBVvchjU^I}R`-oWX>m#&^lUb`x5Bf~=R zTc7!^FODX=OGOpbFW3M?Qx=$sxuhO(dJFu-N%w1kgQc5eNGou; zYe=42EOZu~iu%Mo@T9S^>{n;^H?^QBf9_mWBqrXDVY-`r9?}^tV8qNOoynoP?hJ zzdj%|^m4+POCX;-h6}tDwlkb$yfcKS(d77{^xtFhAY~r6`XxroANR?MR_iC~* zyyKw%^W%~Jt!CBcmgaKP--cBPTHDKsukl2Z$aOElD+#a=x$EuLhJ=Lop>=;#|IWWY zX2lMh^0=SFka;_k0VRccBOp=I>g%Gz(%O(^A}z z*B4A_$KT8EmT;F~e&1GE6cm*?yi`0EC^~hDj=|$eUV$D!4mFu)RaaV9| zcGY0z!R%sC7y3auHNYc|oc%K+y%V`P+Ysd}609kuWgm6wVSyUfjM+BM08 zOT7<)m&-IKJuW9ViPq4syElsce8Vm?+GEQo(>hXlJAA3XU%aH^cKFght}R+LjZGBj zU4i+Cy>#6L%ch+@dQYxXmuxG&)bp~e)zt%CTsSp_)=Zjo;>MrqTxK*qrF0$FILd2o z>$l13$SYk1Evikk>W+l!Pq3!dRuopY_>RskTU{P`xit08z!kI2SLJi_^QNA+r zq_Jj|6`zjU|5^)`$$2r^78_|fTCfd%fE)rfZKM5P_zZ?oLAQGgX88g|fH22nnm#v7oS5L&Xyk3r8O|o8&^_R(C zCPM`W)Swo17twCNM^|qu8kY56Z}cCMw1uBHuo&l)Fygkn9(|;$7f8~$Pj%wT5u$`^=XGKaL zJ^2MuPL7VsY@kH@XVw0H)75?TlWwq_HM5}FTEaXNrh($T#Mj2VVE3YV$&e=-=U?NJ zl8{zOsk%RQ#>VhmLP8d}TjE^2tjDU!By|Ch0DKi=A!sT?w5Oax)bhwo&?pe+cTl^_ znQ?tE<6Lx*GAQ%F7p2UvjIu1Jpfy-%=)rd?tX!#NIV8hAt;P_Xn(|F<@mh8gHP|67 zm0`iT8eBFdJ6XU8mV5kNpOQHTkrM|@{tMeZ$qlTkAMc%wyk=-T?WS3?hGdGkWBk89tqSpm4dRz<7O zPlc14duw_0p_xS(imfuQnSPGk|IoelySx&rr+(n&oPOywC@G2boLM|y5n4+2i{`{y zh>TIqF+JrVcXZ@kE5-`+;sPfvE$a;@_X#*CwR*~C(i1gw>LJvl^(=TZIYFiw3!)#9 zkUICIhT0!?mIIv@oNiSnZvIp={g;d$T~DCa0xbez>|0AYxzS-bQ^XO?@^YqI{`M>F1QAW~6%x57o!d1g zq5?6&gy}6VYrTzX2;pm=-E;HHoZU;vyxrej=mTHtZQC7F)J2)zByHyX@a=bn=kMkE zz7fC;e0a_Jd0eN~a%y$uzD1>o1OwCIKuHXOzN71RIsC@X!LjdO4WVI_)kBTfF0122 z7$*8lh3T0itg2d6Mm9tw{4kMIjXadbEo{Q?Dq#HJ)gXLqXJRrS!wzXwAKVrAm0Eo; zrPQdEPIOmU&cbQd+w}_%6_<;bSFQNs;u|q&zizThlsJpzLii>t8B{gcRU}tL(+@Th zn`TW*2GtC9H71SIg7@okHeiW~r&(Z8mC?9%L`%ig5IvOjP{DXweW;zrWh_EAIT`{V zKQbommH$?}^(|vqSGHrsdQ{pu!pHwjy23uH?kT9dGsKn3yzuO`__dXXmWD>D8`Yx7 z|0kB+x@u;fBX5dANf}vwvFS-3mrOp7tmq;Q8fs#pjgOD*;_c3m59P<}Bh_&Yav?){ zkr}?I6m2@$kgGXCO_b$c>&T--T~D@I-X7PjI;*V4^i^7tLPgE!era-Zh>XAOU=pWS zgv{2{q2R!&lg5X=;yax?6GOvE;j_`vQ6Ztq)j<0gmlr24{mhG}wrnT)q>!j&`Um$e zAoKo)b$GU0|4#qtdevj1)bi3J;*NjSIwK;aae|i|avFQr7_oOI3Jo17CTp(=MgCpY zP!-Y?wndq=YYvut+IM2%PKxv>qhuGu+HQnGu+*2*z6!Y|jIUi9o5rN+Cansok>qLK zsrpyLZ9Sc}j-M_&Lsy)}1?wHQBwGL|*uM`cF~bq1_aSMK-Vka+gTPxjQw)zPyKz11 zGP`q;rkYmjr=zzWtoF5voTMMQIXD*1URIT|%RYUtPE93BLL7P=4zkE!?aVc7yXL=q zzEY<(W(ZY$D7Sl&c*@s@l-}{o@eH6wsHXbv@vrS-EqNYkuWgFfc5S=0Kk44~e#$hO zo>m?X86H3ykZQ5*GC}cYgMopGMqAVyU{pnqzo~TzE_WD+z@{lxcl`R6j$8Zd*P3z0 z_-R*f?^d!(`fXHSHLd#7({#bhf$nQWRe!t%x2iv=>78Dc+qK_c8vyzNe3@1VY7&}yBi%f&vP>JfGdJ)UN z8e#wiP_XTi!WAF%`y0XZ@@?PUQYT%FvR{M#A%MW}?Ko`7T{H+urOZmlyA zB6o%AqugoFhb^B0CEG8U5cI@Ye4tLKpl?kVjJAUCAq*YNPcK`RSWn(q>gk8m*()kM z{rK^t`*yINe0gF|yMMCj?)LUEOOKno!Tr+<_IBvG?nZTz*s6>rPq+u8-1xn@!ddGo zzu+Gw!PA*M`PQ+my^q8+pAhdio}HOd#V|<)w)d3Z#IwWNx;9nowT2T631y_F)PL{# zB`1$prbxH6uwV3A=Rd~o!#v|!AEhA$IgwU95fFrl+2R`?drVRz+BBN(^R(I0bz44Z zShEFc`BLygW#`^m^AYoMtz}&3&>^@8`9|U8|`bdmxsr|L(mcaKy-f=kxSf?2 zH;;RKc+(p=HN}DscN3F^zCI3}KnTLPy9d_B0rqvDUbkUUNG@_WJiNDB@(eaEBICP$ zutDW`NGP&5DC8i8r|dHuhj=gNP&^j7n4 zb?6u`LNKMyG;cq)a{fBg$`)rx?15#oShh|{&{V1w3Qk?I)6i)Pune7|a-31t`N~@O z&(1ccsxY%kqFDZJ=aP%}t5y;$&v_O<<#_1EoldSB><4GsGI~dU-6KqN@wTFEQv*L; zS4Tt1+93mZ(WKqe$F7UsF73`(Q_H30jb#t#gwviNIG`5V%O|TmIN9{7o2u)-`zm;q zmYV8C)U|%T_(s&8?N<=iS}f73q0j2LsxTS^$^li>Hhw8FFJFQNm3$E)WekBHI8EO4 zIEZdpUu2RO*V;KihOEu%x2I26$%skGYc^FTAD^z-Igj)21aC*8pR9#+tK{ss>-BdU z4_+AW-s3Waitt`50mYdtU{~~Z-6M5d02ABRrP($QRTd;Gnz+?`?IkYGr6}Yz zfaX2eDxip&b0uTwXd_IfZA{Ims1~yopA_`iTC(Q-*u^E_?mp4i-_P*cQnaR; z_4H?Lp=Ir5%+%jepihCArm&%t823L;k!&-SK-wSNDxN0ZX1Ag95hc-sJ>#sb@phPlqcgx@F*vzF3 zm**4NF;}Ig7^UCzeLGUqh+jC#eYpr@xT1xL`?4`w!K#!ZYU5uK9VzL`OQ+$d`50PR z#T)D1?K9m7sh;B%u<|H}6~4SvD#5s!iQdpd7c50g&>{~lo%mgFs5(0}wfX8dD{MBs zTu^v$(`?7rmdFW>RraMS%T}6yd;2A{wW>T{QEM;h^g9uh9%p z-cKRnQR`RdPp8yis8iTk@M)kB`=YT5<4NHiCox^WB|4*Vc9~0af3q;8{hfx@vU6pM zcl%~Dh|XFU{b!x8v2k1jDclq`LqAzBB}ryYlL;TYpm8^~VREEcmBAC%af6`e-O?Ob z8(fXO8dy1gnb~sS6mgcc#N%vXIS}Fg={of^v~7LDyx2-k-W_UkBhNf0t6jlw*PTbr zCCDBpV4~dJ75U(q>sfd^m0UaXzOE6Z2KDF`hd#Vh}v6M8vNSVk5GkwvS6 z2O_9R6^yWUKy*iU^OF|tmF*8{GgT)*YVq0VHBM}5Qu|M_H{^Jp)! zXy)rR9=eouXo&0djAItsyggeRVj%bE z7#R2VZ;AFAi;Gtwo=-M=V=KUk0w818lX-#w?J~{{8@i?`R_>gjo^0w9=YhQE%0WEhN(6d39k~2{ylr z9#J240I_EU3sSbId;;2GojOTs6@bvs6hz`&gL+COv<6XdfZIB2?m1uwa zx}lJYDmNQ1L8PI>?`141uqCT@<-;M}i&IBcZt8sdqh$QLe8?-}QJ%zsON~M;VApn= zY?2rpsN8(QX-*m06|1xQK#EUH4W;>GUH;wgdn z?P@|_Fc#W;$Y z?qQjI?0;wVy&HUgkS-VD@0+dozP==gJmitT7{9(ab6B0B>GKr7rF$K2PfuC-by%g2 z8aI9AZcP23g8&6NldPKH-5=4TlamYD!cdr|NuaPRtE%b@Yz*m>B%fELEQ1>vt2nn8 zdZ9j+5`N4f<*VJ_VzyhnEP6dCh8x@Qzf5jeoS(M?WVu%xgzpf>N5>bApKC|Q6c%mR zGOL7te{gnTl(g0p4i6To;hs@RC=r2R$ zch33Uziq;jFj=>KI{E?JHnppZY`^wcLs3`&WOZs{0i>DW2(MD1X3a8h;8r z387vum3NaWc-a z`ifqt$Evv9%CNh>Gt*$MnW2MAl1E8x9N+IenVb3_ zUvdqZ=hEBz+MY7b76@OT$8Sfc+bj#S974RG_}6ajJw}7AkE@57uM|CA9&cRtGTb0V zWqL!06N~{ z_OkM7u=c#meVpzzR^>FtyF4cJbZqk2I`mtQ|ER3yxSRa%IGKetPqc#9ro!m`F-9r&3G~G5^79)sv#s@gd!%*m00U zvrwdoael`i3aPakF}$%_t=?g^wkW#FY(b6ZoT~~j#8A)497Qc`u=DLJ_-$SQl%IHt z)enw?yXJc~dO2xgn@WSP&$_S&`vb`d1?FJ0W)s1c*1113RvDzaT)OEM2;&SlYIE?f zDyRj&_l((W%e$6&qDw23MT;T*6*I%C2^hmh3k5I=8>P4N|4(~g{+3kw|6SwEIHRdO zpE74Oar!i6o7&=*3pV?A6neRN$^DjI9>%Px*&UKx0pL3u0{d&J&@5Sv0V=saZFLqyS zrt7`5hq@-}DY*K!4%cBIO6T@%&ruV1x4SLO8r_ee0^@e;^h1>u`fE4s?mit$BVVi& zGJ%*wLUPuVX$z~1f%C2I&zhnmcja>j#(yz~AbjTo?rx_RV6~jh&!exiALZOT z(bC=3N=Uv`k#e#u4gX4ZszOf4`bO`oey4=bmClJN`uncr%vFo-yJos(`U2#HDckFC zp#RX;O#34(M_uT}o`*9_9_7`D?&B8UTZ9*{*c(kcFfes+VBPNE|p0YH`jDV%LM{Aqf`yJJ(&az>DiG`o*2s1+&BcEtsMa6NU(LrGu z&5*PX3U!KWfLMp5snXd-ZkvzYO8h*+;`!uG07`tqV#__8`#Pv)>yI-L7XRNN{JJOD4lRJ@ES31=u>G~QpTF-56nE9pnnm=b?Kb?nJI zT`IA*$PfmfmU4{f42~&ZL(fx(Bb8dMu7yv*>RX{qj?KO zhYalm1c~ZvF!7A#c6|V}6Tql=@&nO!`c|&Hy#)q@v39ec^?*Lg%|&KE+L2-O2_~SX zbFSZYnBvS~kK7{PdppX)kaYW#q91N-b1`VH*p>mzI8&yHXdlHsINfvSVF-_alrQ29cGa?3Js1A?R(YkLCJ&Wz^-}> z#j^F_6xRBNYg6#IcZ~nN@Ka0$FJZ?MkEYh$$82!apqn*nP<FqH;Xd>C)Vne1VFJOOfY_-Yz=F~qe_cC)GSmv74R0W3ahN?LEZ z`qBNnfB(A~ZIpKER`R%I;EspW>0I^~9|PuZy!|i0B7BmB0F2@C`)n z$LdM%wm+RjnTNtm^uJ=yw8V!+?(n;i{ zMkeB+&3whr8qFsyK9I8hbi5p61~UWcCr~EJ)qG3w+g+109bwUbKG5bkoD^s)ZOUH? zXW5itXW`cII5*+b*A&>Ts7AU+=EU0Zv9HLR(b4*-29;HhxEI4*I~i9=0i|hJ)qIy_`uw> zMQ(O>z^_+92ZF5&;{$IkXVyIFdpb-{8T4X8U5g6k#UP_O>w3@>C-ZiDwr3&&v?0{ zO$c^!{MW_X)b|~GjLaK5Z|izAL9RJI@S=O(G{>g@tjpnNkyR>1hRr&D{Rg@ag0;cy zl2n8C8Xll0whVR;n;d%aB2tJLcL>}IJS#nwQehi^r~R5iV#UgRV|P)C1*&~PS}t@5 zi1rH~zH`Iz+xt-+qr!!xy>7I5(M88n!)_C{zCGv=@=pO@7@_s3=5hyPB`_FwY;0C* zwUNWEDl+L@`?#T&t5wj}dfQKTdJFTj1dSy%>%5byW7P#*e5_%2Y)mYbdp(=Y3^2{l zipi*iW#7siiy`oXn5)JrZpb8A-=_6XZPiq;pl&Kqkh;VGmUK@0xg>C0q%{&DabM&D zvHlWlo@AIz%~Ba!DBWH68Xxc0P88%?*)~Uc?6;us#5d1@=bWx`Z{N0axqGj2z+)4r z^@3B}R{H`K`dg1#HkniUavBCjE-aX~?H9>rq!9|O0z&o6TF4hVhjc`>E@dsrCPyyW zUjUA|zWINO#ek#P4^f4ng^CgbI}`tkms>x*`Y=e)H9|hFy8Oedvh0PB*URS^U)YfV zIK8CEHzL=^Bl!q4cTaChAU8EaNT|mqXH;BGnfbmr{1AV${gDl)Fe-B*EQFD098g8v z3FP3sD~PM(gQ-7F8^tvvIny^F523oo)K{}vr$PLSRB)&2M&koPmu}Uyb&%g=leK!x ztDD_#L5!WSE@0XQ2Ry48Y+cM-1SOY6m@Z{gLmO{n4Hfc*tkT-_Tv^8Is@#x8!rf^0 zI#jlGp(@o{*Tm@HQUsaV8ZZx=MwP*WBDZ7X47xAgF9z5k@m|;&J&y4o)lv1x>n8eV zEfy+{pPpyjABqf#s{R++vV25a>s@}T8op3k17CrfJEi}L#a+z(kRzAx+G&6uCiR1JzrWcKg=<={y~>nz zZKC1vTKeKSx`Zl_2vH9F$Xso`@%>Aq^+WQ-iA0&qVC(m{a=9;i1{vR-1KBzQ%+9Y} z=^Jql*_WG}gFCLgjerkUtmO4u?EK=Ic5&Kk z0?jyEk@Pxs=~H1ix(7?U*m*^9|5h^D<T@%@Ioub#aztChhKH?HSYqud5e4!8P^~Gm0 zRDXLi(sZ^E`R{JgeKz3S^@V}b zY~eebcQ{xCGk%7D`Y5MAG{htNN29W#+e@=D@Qz@AqnG}j!j_eN=}b(O?J+MzDap?~ zuGUr*nG)w{?7Dp7Le%EI$9iAd2K>CkWcQwp&9EIt?r-V>dJvv>p%Y_hYZq+{S$6la zzJo(`xfXFDp{6!2YpL8u+g&;4oCB_`RPjJc$hvn&LiDq5vULF-P@_zNKhE%Jz9eVQ zH&M5zI|R{g@mg^hku>gL28hy`X_9ytxn7Mb(c*Qjk(_K7#~n2e3X2JXCgHU?2Ku=~ z`rCw-6(nk7N*AWBDh(8p0WAcz?W>HmwA}RzTAI>y(VbOjyOx7IC_>Nc>ul?WI$i0z zSH-_+XQoGLl;p|1Njyz|*O`zGNEk|Dhl4?FvYKT_S*wdbUIeZ_HsPoAm%064!)Ur< z#)5S5sBSOEO|v;Q_37gu*}1tP9_sA^szjST`+cAM-H=<|VkQAg!`kk4Zx*{I?;bxi zK3HCu>vyN@re5L$f8;r16)C z_E&nlfL6X$T@1ad_9haql?5Ge$__R2`sv9lQy#a_a?tKlk#COQhJAgi`4NL_kD;uZ zgC51EV;)lWkp>AFmRhrNEo`y!+1D_9%Hf3k$WMt>&D4iy)nPshk zmGvhyx(Uauu<-prCgq;RZ+M#>jR~m%k$r}OKS%4b-_GRiGcyB_-{-aAS9wq1rseja z{T>zPu;*LP9TD9t;2sd%krYs6ymRjJ4fqockujS6yvxN^5w71Q-*|Hm`*r-)bZwJA zf01QS`8Bp+q^___1E{-o3r%Wk!w)JeZnfbTEOrP~t2Yf|n)KbS5BiQLM8EkO>yGm1 zKwi0ah-fC4?f{*htJr7ZpAmiN(OKZhlVX2^iQ|s8pZ#U6p!-#i9nAO8;EkcE^K?Js zBeaA2QV{ginctu7v`UU!|M|jFZel^2sJs2vqS|WQ~-i=?^SVqr!xnzvp(>LOJHn)U2zh$gyNR7XZe2hyO_P{S?|C4FzUU|g&YpZCUs67i8YAKZ7jd4Gb19gYFEcZ zcr50X&J6Be>uW?mKchA2A55~mP)!P0zy-`(Sd*Alm=Bw8q3YlG)k@vbYUTOSYM}?; z;^vEp@1G3nEF%^(xe3jRb9DK_Q3*9PvEIjQiE3#vIlFx-7ro(uT5R?J6v*XwbdfTd zoPP$xlC2+hFuX>n8CCaXH-RKSLcn_q#SSB+>%d5#E8^ObrkL}S(gE>V-ds<&RSatJ{xrTXKRdPCBJ3gL4t zPo!M?=8KAw8QJls#nvE4OUbS=wn!G-Y+bGd#-5^1!QxE0T63mkX%5#A7F4)Y&W`)} zYoEfPQOj`tWG+rbuFWLCXBz#YM(z9OlDYWaxUm_dQhzuC4n7}6fTzO2QMGJ>q|_W> zt!nANIM#LhbB>|t^Ntxo^y$MG;*0%vhL8|JrMmX`5*r6AToO#NJrBT8&m==RUZ9*8 zGt{faa>KtEo>DkBgc>_?Bos9TR?)T0o`vS~dC3dR=6SiLh2~k-?2x99ncnM(Xj4?f zIExT2U$|I)o2exN_I{?d=&00fXwzRj+~Sm9;DbxNx=3a0zv-X={mVlYaR?w?@?Ks| zg?@Z&V(JlM#8Lfuerwd%Y5qsm=A7G{teJB+#!C9UOOfxZY*j1DH8HDanCnRG#HVOw z+UP%IPx@4|Nj3K&7TRcH=QE=JS8K?O=JdHmJv{Z0dYp0P;n2fC>jEN?*_Xgno}*{> z9iXlhIE#DE185Mx#YTvqxUsgY)x6|%w5~TcKKXUDx>ScN;6_cr7~BbNRU_FL3 z6N;+o=B+E0sq=QAaqD-MAiiZ$pxkJvj0+XPB$To}AU}L#ZSv+-R;G9Ksl{F1>@hb+ z?JBs+wG>m6zPZ-i%3Zi*p1na>R+|a zS^ajP6e=TuO-seb$TtgML5$=v-a3h#=uLw=kiwiySlyNI2+a*3GEvw0YY&P+Fb2hv z(PWB75{07_D2XH@M+ruCD6cX}M28CuWsO@=te`0y^J*+0%d>L^DvIIR!9!}B*)8DY zg1Gs{Fz-UR-CRxe;V8vytBiupQjU2DTzfdrxp2|*lXy=7Gcvt2mT|AbH--j6Ib=f-^BGO?zx0=lu&QJX+}aEG&hW794t6 zVo=Hz?&MZFnL5=_YA5Uyn!5Xe(!HUm#j}XTkZQ5Nh5Dtsvs=@?H5bK-)-1EOgiHl4 zag@hg^FwsGl|_UuO)hkgpnhMEsY@AHNE;UPTc&~8TY)0cXWDz_iKB*o{C68QxP#w3 zy;Y8lfBhV4Gk+5_gqzK63`YO~ewZX#eQwrUp|mW#-Uz*{78VCf-5y>Jjg{(Hq?1b1 zKhg`cD@t5*as8*VAnB3+I9B{r;Z;)su8JH`hAN4Kitbg}1W;1DRIlPfi+H&6IdzLA>CiK6mb zEQcdR(@a#p;$2+mh!7}f08t$5e5#VgbsTHAbHT#ms)4RSG>W#Uj{}nw>FrU; zH)pRFhe{TYI*dY|vyvGTl|0hehaqQeT7q@K6&fU~+BKPXT|j{sO2(fIN77+D?ap~Y zm*fcmR5P2_{)DiU?1}U#3L0u=-Q32JP}vDofi>gs1si%#0!w>$^V`ndN3Mb&H76n4mt;6Ciq$0W*Qp+qmJG3|P}A=y27Wj% zU@l2CLmMR)l4t_bsP?Jq+W=tAO-Xt>Rx;k&K}|$+%RFOM`<+`e(NaT{JkMM{?7qxorP(?b3h#o;@q@w?Wv_Agq+t;1rdnU1{OL8*18D5B(B2* zbFAA7n1L&PozW8O#8@9Q|_BcF9(^=LaL&=iKE4@=UQlzxC=R`W5lk>dnT16Ru{nf=2n z<-f=$RqE}}x3_nUeWBiOKj^Tx3NucyM zhgSANZ%-D#G!!@#gon;LCSlxW{gRfd0_2-u5ts<-hE|fh2jIh$5L8z2%yTDk8{L7w z-tRcXRA&!~QH1h@YF1VO5dx=Oz?)@d>ZGQq~YwnNN} z>5F?QCS!JmVV?w$W<)jwo<|LddN#%ReuaYkml<)p%q!>lFg+|Hn&XbO1~;=Mkf?P>&C48iKpF$zp7S; zO)I@^2JGkf_#UdLKYRv1vu68_HXS`Z}pE`%vRsr9y;L5$#o1hSy8DB5HuJxjBwieEoPCqmXMD?6Nu>BlCY z({$)^L9O<~PczSjPfiG7ItsLz7_j!mvUCOf1y@WZrMYGQ#ER%Z(v=I<>WWMr6h(xj z^w~j2X6<}9SRN%YxEu|EPiH%990$&)^p>R{fvBzj!$E0+$}n44#OLO-s;S(v1`AhB z31j(VuDII`{T_-`98mpcrO_QY>Xy+7&6v4{Qj8S3GybRqY!cv}F%uDZ*%T$H0Je)~ z!$5Du9qkJe*wDj>46*2ZtTWw*;xw341(H8YI$eaxz$3^aGW#e9K^G@edZGTflvSxq zFA%E@e>0C^z0_j5I!VWz6jvBd4hqlU&(3+q(j(zt7vh+2)A;2Z--Pbp;#^t<)<2+xGhZ zkA)g|$YQIq%4;C(z*Dp<4yKX?Qq>q@FJS$DI@6oBI9CEMlrN8~j5pJO;cp|i9&J^x z-lhhe-)#0yEUMuwVoGotZRj>%1;&$k=(p?md%e{98m;v^XXoEG$;qY;oj<&OUOT0< z5y(ZvNLjeNi9`!j+R!IBFVI}gH312qGlDq}ye_Xnw>Y(k0Ttx9Evx%cTVM?vs#;Yo zk7?M{mBc0AFSZE}{f+GY8o@TZeSdM9e{aKg&V2t*_u{Uka#s7U9|D7BE epZ_WO8r_@w@0B@(ru{9MJkR?6r`G+-um2ktH5@Jg diff --git a/bundles/org.openhab.binding.siemenshvac/README.md b/bundles/org.openhab.binding.siemenshvac/README.md index 29a7d217f8e4d..37f336212f260 100644 --- a/bundles/org.openhab.binding.siemenshvac/README.md +++ b/bundles/org.openhab.binding.siemenshvac/README.md @@ -3,7 +3,7 @@ This binding is to support Siemens Hvac controller ecosystem, and the Web Gateway interface OZW672. A typical system is composed of: -![Diagram](Diagram.png) +![Diagram](Doc/Diagram.png) There's a lot of different HVAC controllers depending on model in lot of different PAC constructors. Siemens RVS41.813/327 inside a Atlantic Hybrid Duo was used for the developpement, and is fully supported and tested. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java index d4f80ddfba68e..b9c1609a01675 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java @@ -21,7 +21,7 @@ */ @NonNullByDefault public class SiemensHvacMetadata { - private int Id = -1; + private int id = -1; private int subId = -1; private int groupId = -1; private int catId = -1; @@ -36,11 +36,11 @@ public SiemensHvacMetadata() { } public int getId() { - return Id; + return id; } public void setId(int Id) { - this.Id = Id; + this.id = Id; } public int getSubId() { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java index 952c04cf23201..444625ad0aecf 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java @@ -193,7 +193,6 @@ public void resolveDptDetails(JsonObject result) { this.dptType = desc.get("Type").getAsString(); if (SiemensHvacBindingConstants.DPT_TYPE_ENUM.equals(dptType)) { - JsonArray enums = desc.getAsJsonArray("Enums"); for (Object obj : enums) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java index b38fceb03d194..c4cc5b6b53995 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java @@ -12,16 +12,24 @@ */ package org.openhab.binding.siemenshvac.internal.metadata; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault public class SiemensHvacMetadataLanguage { private String name; private int id; private String language; private int languageId; + public SiemensHvacMetadataLanguage() { + name = ""; + language = ""; + } + public String getName() { return name; } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java index e4e53f1847421..5cd5fa212f6d8 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java @@ -29,7 +29,7 @@ public SiemensHvacMetadataMenu() { childList = new LinkedHashMap(); } - public void AddChild(SiemensHvacMetadata information) { + public void addChild(SiemensHvacMetadata information) { childList.put(information.getId(), information); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java index 60f9aa075f7ee..3fb50cabc054a 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java @@ -31,7 +31,7 @@ public interface SiemensHvacMetadataRegistry { */ void initialize(); - void ReadMeta(); + void readMeta(); @Nullable SiemensHvacMetadataMenu getRoot(); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index 39d0fdcb69611..9209a4df78c3a 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -77,7 +77,7 @@ @Component(immediate = true) public class SiemensHvacMetadataRegistryImpl implements SiemensHvacMetadataRegistry { - private static final Logger logger = LoggerFactory.getLogger(SiemensHvacMetadataRegistryImpl.class); + private final Logger logger = LoggerFactory.getLogger(SiemensHvacMetadataRegistryImpl.class); // A map contains data point config read from Api and/or WebPages private Map dptMap = new Hashtable(); @@ -167,8 +167,7 @@ protected void unsetConfigDescriptionProvider(SiemensHvacConfigDescriptionProvid public void initialize() { } - public void InitDptMap(@Nullable SiemensHvacMetadata node) { - + public void initDptMap(@Nullable SiemensHvacMetadata node) { if (node == null) { return; } @@ -177,7 +176,7 @@ public void InitDptMap(@Nullable SiemensHvacMetadata node) { SiemensHvacMetadataMenu mInformation = (SiemensHvacMetadataMenu) node; for (SiemensHvacMetadata child : mInformation.getChilds().values()) { - InitDptMap(child); + initDptMap(child); } } @@ -204,7 +203,7 @@ public ResolveCount(int count) { resolveCount = count; } - public void DecreaseResolveCount() { + public void decreaseResolveCount() { resolveCount--; } @@ -213,7 +212,7 @@ public int getResolveCount() { } } - public void ResolveDetails(int unresolveCountP) { + public void resolveDetails(int unresolveCountP) { ResolveCount rv = new ResolveCount(unresolveCountP); for (String key : dptMap.keySet()) { @@ -233,7 +232,7 @@ public void ResolveDetails(int unresolveCountP) { } } - public int UnresolveCount() { + public int unresolveCount() { int count = 0; for (String key : dptMap.keySet()) { if (key.indexOf("byId") < 0) { @@ -261,7 +260,7 @@ public int UnresolveCount() { } @Override - public void ReadMeta() { + public void readMeta() { ArrayList lcDevices = devices; SiemensHvacConnector lcHvacConnector = hvacConnector; @@ -274,7 +273,7 @@ public void ReadMeta() { return; } - ReadUserInfo(); + readUserInfo(); SiemensHvacBridgeConfig config = lcHvacConnector.getBridgeConfiguration(); if (config == null) { @@ -297,46 +296,46 @@ public void ReadMeta() { logger.info("siemensHvac:Initialization():Begin_0001"); logger.info("siemensHvac:Initialization():ReadCache"); - LoadMetaDataFromCache(); + loadMetaDataFromCache(); logger.info("siemensHvac:Initialization():ReadDeviceList"); - ReadDeviceList(); + readDeviceList(); if (root == null) { logger.info("siemensHvac:Initialization():BeginReadMenu"); root = new SiemensHvacMetadataMenu(); - ChangeLanguage(1); - ReadMetaData(root, -1, false); - lcHvacConnector.WaitNoNewRequest(); - lcHvacConnector.WaitAllPendingRequest(); + changeLanguage(1); + readMetaData(root, -1, false); + lcHvacConnector.waitNoNewRequest(); + lcHvacConnector.waitAllPendingRequest(); - ChangeLanguage(user.getLanguageId()); - ReadMetaData(root, -1, true); - lcHvacConnector.WaitNoNewRequest(); - lcHvacConnector.WaitAllPendingRequest(); + changeLanguage(user.getLanguageId()); + readMetaData(root, -1, true); + lcHvacConnector.waitNoNewRequest(); + lcHvacConnector.waitAllPendingRequest(); logger.info("siemensHvac:Initialization():EndReadMenu"); } if (root != null) { logger.info("siemensHvac:Initialization():BeginInitDptMap"); - InitDptMap(root); + initDptMap(root); logger.info("siemensHvac:Initialization():EndInitDptMap"); } - int unresolveCount = UnresolveCount(); + int unresolveCount = unresolveCount(); // unresolveCount = 0; while (unresolveCount > 0) { logger.info("siemensHvac:Initialization():BeginResolveDtpMap {}", unresolveCount); - ResolveDetails(unresolveCount); - lcHvacConnector.WaitAllPendingRequest(); - unresolveCount = UnresolveCount(); + resolveDetails(unresolveCount); + lcHvacConnector.waitAllPendingRequest(); + unresolveCount = unresolveCount(); } logger.info("siemensHvac:Initialization():SaveCache"); - SaveMetaDataToCache(); + saveMetaDataToCache(); logger.info("siemensHvac:Initialization():InitThing"); getRoot(); @@ -495,7 +494,7 @@ private ChannelType createChannelType(SiemensHvacMetadataDataPoint dpt, ChannelT description = channelTypeUID.toString(); label = channelTypeUID.getId(); } else { - stateFragment = stateFragment.withPattern(getStatePattern(dpt)).withReadOnly(dpt.getWriteAccess() == false); + stateFragment = stateFragment.withPattern(getStatePattern(dpt)).withReadOnly(!dpt.getWriteAccess()); } if (!options.isEmpty()) { @@ -576,7 +575,7 @@ private void generateConfigDescription(SiemensHvacMetadataDevice device, List((.*|\\n)*?)", Pattern.MULTILINE); @@ -747,13 +746,13 @@ public void ReadUserInfo() { } } - public void ChangeLanguage(int lang) { + public void changeLanguage(int lang) { try { SiemensHvacConnector lcHvacConnector = hvacConnector; String request = "main.app?section=settings&subsection=user&action=modify&userid=1&language=" + lang + "&submit=OK"; if (lcHvacConnector != null) { - lcHvacConnector.DoBasicRequest(request); + lcHvacConnector.doBasicRequest(request); lcHvacConnector.ResetSessionId(false); lcHvacConnector.ResetSessionId(true); } @@ -766,7 +765,7 @@ public void ChangeLanguage(int lang) { } } - public void ReadDeviceList() { + public void readDeviceList() { try { SiemensHvacConnector lcHvacConnector = hvacConnector; ArrayList lcDevices = devices; @@ -791,52 +790,52 @@ public void ReadDeviceList() { for (JsonElement device : devicesList) { JsonObject obj = (JsonObject) device; - String Name = ""; - String Addr = ""; - String Type = ""; - String SerialNr = ""; - String TreeDate = ""; - String TreeTime = ""; - boolean TreeGenerated = false; + String name = ""; + String addr = ""; + String type = ""; + String serialNr = ""; + String treeDate = ""; + String treeTime = ""; + boolean treeGenerated = false; if (obj.has("Name")) { - Name = obj.get("Name").getAsString(); + name = obj.get("Name").getAsString(); } if (obj.has("Addr")) { - Addr = obj.get("Addr").getAsString(); + addr = obj.get("Addr").getAsString(); } if (obj.has("Type")) { - Type = obj.get("Type").getAsString(); + type = obj.get("Type").getAsString(); } if (obj.has("SerialNr")) { - SerialNr = obj.get("SerialNr").getAsString(); + serialNr = obj.get("SerialNr").getAsString(); } if (obj.has("TreeDate")) { - TreeDate = obj.get("TreeDate").getAsString(); + treeDate = obj.get("TreeDate").getAsString(); } if (obj.has("TreeTime")) { - TreeTime = obj.get("TreeTime").getAsString(); + treeTime = obj.get("TreeTime").getAsString(); } if (obj.has("TreeGenerated")) { - TreeGenerated = obj.get("TreeGenerated").getAsBoolean(); + treeGenerated = obj.get("TreeGenerated").getAsBoolean(); } SiemensHvacMetadataDevice deviceObj = new SiemensHvacMetadataDevice(); - deviceObj.setName(Name); - deviceObj.setAddr(Addr); - deviceObj.setSerialNr(SerialNr); - deviceObj.setType(Type); - deviceObj.setTreeDate(TreeDate); - deviceObj.setTreeTime(TreeTime); - deviceObj.setTreeGenerated(TreeGenerated); - - String request2 = "api/menutree/device_root.json?TreeName=Web&SerialNumber=" + SerialNr; + deviceObj.setName(name); + deviceObj.setAddr(addr); + deviceObj.setSerialNr(serialNr); + deviceObj.setType(type); + deviceObj.setTreeDate(treeDate); + deviceObj.setTreeTime(treeTime); + deviceObj.setTreeGenerated(treeGenerated); + + String request2 = "api/menutree/device_root.json?TreeName=Web&SerialNumber=" + serialNr; if (lcHvacConnector != null) { JsonObject response2 = lcHvacConnector.doRequest(request2); @@ -858,7 +857,7 @@ public void ReadDeviceList() { } } - public void ReadMetaData(@Nullable SiemensHvacMetadata parent, int id, boolean localized) { + public void readMetaData(@Nullable SiemensHvacMetadata parent, int id, boolean localized) { try { SiemensHvacConnector lcHvacConnector = hvacConnector; String request = "api/menutree/list.json?"; @@ -873,7 +872,7 @@ public void ReadMetaData(@Nullable SiemensHvacMetadata parent, int id, boolean l public void execute(URI uri, int status, @Nullable Object response) { logger.debug("response for {}, status {}:", uri, status); if (response instanceof JsonObject) { - DecodeMetaDataResult((JsonObject) response, parent, id, localized); + decodeMetaDataResult((JsonObject) response, parent, id, localized); } else { logger.debug("error status {}: {}", uri, status); } @@ -890,7 +889,7 @@ public void execute(URI uri, int status, @Nullable Object response) { @SuppressWarnings("unused") private static int nbDpt = 0; - public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMetadata parent, int id, + public void decodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMetadata parent, int id, boolean localized) { SiemensHvacConnector lcHvacConnector = hvacConnector; if (resultObj.has("MenuItems")) { @@ -918,7 +917,7 @@ public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMeta childNode.setParent(parent); if (parent != null) { - menu.AddChild(childNode); + menu.addChild(childNode); } } @@ -959,7 +958,7 @@ public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMeta childNode.setLongDesc(longDesc); } - ReadMetaData(childNode, itemId, localized); + readMetaData(childNode, itemId, localized); } } @@ -999,7 +998,7 @@ public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMeta childNode.setId(nodeId); childNode.setParent(parent); - menu.AddChild(childNode); + menu.addChild(childNode); } if (dptItem.has("Address")) { @@ -1071,7 +1070,6 @@ public void DecodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMeta @Override public void execute(URI uri, int status, @Nullable Object response) { - if (response != null) { String st = (String) response; st = st.replace("\n", ""); @@ -1085,7 +1083,6 @@ public void execute(URI uri, int status, @Nullable Object response) { String dptId = matcher.group(1); if (id != null && dptId != null && !id.isEmpty() && !dptId.isEmpty()) { - if (idMap.containsKey(id)) { SiemensHvacMetadataDataPoint child = idMap.get(id); if (child != null) { @@ -1101,14 +1098,10 @@ public void execute(URI uri, int status, @Nullable Object response) { } } - if (resultObj.has("WidgetItems")) { - // JSONArray wgItems = (JSONArray) result.get("WidgetItems"); - } } @Override public @Nullable SiemensHvacMetadata getDptMap(@Nullable String key) { - if (key == null) { return null; } @@ -1129,7 +1122,7 @@ public void execute(URI uri, int status, @Nullable Object response) { return null; } - public void LoadMetaDataFromCache() { + public void loadMetaDataFromCache() { File file = null; try { @@ -1149,7 +1142,7 @@ public void LoadMetaDataFromCache() { } } - public void SaveMetaDataToCache() { + public void saveMetaDataToCache() { File file = null; try { @@ -1187,7 +1180,7 @@ public void resolveDptDetails(SiemensHvacMetadataDataPoint dpt, ResolveCount rv) @Override public void execute(URI uri, int status, @Nullable Object response) { if (response instanceof JsonObject) { - rv.DecreaseResolveCount(); + rv.decreaseResolveCount(); logger.debug("siemensHvac:Initialization():ToResolve() {}", rv.getResolveCount()); dpt.resolveDptDetails((JsonObject) response); } else { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java index ff0b4b7a05b33..d83c3a2e68328 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java @@ -12,16 +12,24 @@ */ package org.openhab.binding.siemenshvac.internal.metadata; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault public class SiemensHvacMetadataUser { private String name; private int id; private String language; private int languageId; + public SiemensHvacMetadataUser() { + name = ""; + language = ""; + } + public String getName() { return name; } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java index f160db6438440..6fcb748239d53 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java @@ -46,7 +46,7 @@ public class SiemensHvacDeviceDiscoveryService extends AbstractDiscoveryService implements DiscoveryService, ThingHandlerService { - private static final Logger logger = LoggerFactory.getLogger(SiemensHvacDeviceDiscoveryService.class); + private final Logger logger = LoggerFactory.getLogger(SiemensHvacDeviceDiscoveryService.class); private @Nullable SiemensHvacMetadataRegistry metadataRegistry; private @Nullable SiemensHvacBridgeBaseThingHandler siemensHvacBridgeHandler; @@ -91,7 +91,7 @@ public void startScan() { logger.debug("call startScan()"); if (lcMetadataRegistry != null) { - lcMetadataRegistry.ReadMeta(); + lcMetadataRegistry.readMeta(); ArrayList devices = lcMetadataRegistry.getDevices(); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java index 94a8fd53954c2..537f4d0db9d19 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java @@ -71,10 +71,9 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { @Override public @Nullable Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration, @Nullable ThingUID thingUID, @Nullable ThingUID bridgeUID) { - if (SiemensHvacBindingConstants.THING_TYPE_OZW672.equals(thingTypeUID)) { - ThingUID IPBridgeUID = getIPBridgeThingUID(thingTypeUID, thingUID, configuration); - return super.createThing(thingTypeUID, configuration, IPBridgeUID, null); + ThingUID iPBridgeUID = getIPBridgeThingUID(thingTypeUID, thingUID, configuration); + return super.createThing(thingTypeUID, configuration, iPBridgeUID, null); } else if (SiemensHvacBindingConstants.BINDING_ID.equals(thingTypeUID.getBindingId())) { return super.createThing(thingTypeUID, configuration, thingUID, bridgeUID); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java index 41db1d9cb83a4..d77457c85d470 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -61,7 +61,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { public void initialize() { config = getConfigAs(SiemensHvacBridgeConfig.class); - metaDataRegistry.ReadMeta(); + metaDataRegistry.readMeta(); } public @Nullable SiemensHvacBridgeConfig getBridgeConfiguration() { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java index b12b7bf6850b4..52de1ba49df2d 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java @@ -12,12 +12,14 @@ */ package org.openhab.binding.siemenshvac.internal.handler; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; /** * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault public class SiemensHvacBridgeConfig { public @Nullable String baseUrl; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index b84333c437c6e..3bbdb63593979 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -71,7 +71,7 @@ public class SiemensHvacHandlerImpl extends BaseThingHandler { private final ChannelTypeRegistry channelTypeRegistry; private final TimeZoneProvider timeZoneProvider; - private long LastWrite = 0; + private long lastWrite = 0; public SiemensHvacHandlerImpl(Thing thing, @Nullable SiemensHvacConnector hvacConnector, @Nullable SiemensHvacMetadataRegistry metaDataRegistry, ChannelTypeRegistry channelTypeRegistry, @@ -86,7 +86,6 @@ public SiemensHvacHandlerImpl(Thing thing, @Nullable SiemensHvacConnector hvacCo @Override public void initialize() { - updateStatus(ThingStatus.UNKNOWN); pollingJob = scheduler.scheduleWithFixedDelay(this::pollingCode, 0, 5, TimeUnit.SECONDS); } @@ -103,20 +102,20 @@ private void pollingCode() { long start = System.currentTimeMillis(); var chList = this.getThing().getChannels(); for (Channel channel : chList) { - ReadChannel(channel); + readChannel(channel); } updateStatus(ThingStatus.ONLINE); SiemensHvacConnector lcHvacConnector = hvacConnector; if (lcHvacConnector != null) { logger.debug("WaitAllPendingRequest:Start waiting()"); - lcHvacConnector.WaitAllPendingRequest(); + lcHvacConnector.waitAllPendingRequest(); long end = System.currentTimeMillis(); logger.debug("WaitAllPendingRequest:All request done(): {}", (end - start) / 1000.00); } } - private void ReadChannel(Channel channel) { + private void readChannel(Channel channel) { logger.debug("{}", channel.getDescription()); ThingHandlerCallback cb = this.getCallback(); @@ -136,15 +135,15 @@ private void ReadChannel(Channel channel) { return; } - String Id = channel.getProperties().get("id"); + String id = channel.getProperties().get("id"); String uid = channel.getUID().getId(); String type = tp.getItemType(); - if (Id == null) { - Id = (String) channel.getConfiguration().getProperties().get("id"); + if (id == null) { + id = (String) channel.getConfiguration().getProperties().get("id"); } - if (Id == null) { + if (id == null) { logger.debug("pollingCode : Id is null {} ", channel); return; } @@ -152,11 +151,11 @@ private void ReadChannel(Channel channel) { logger.debug("pollingCode : type is null {} ", channel); return; } - ReadDp(Id, uid, type, true); + ReadDp(id, uid, type, true); logger.debug("{}", isLink); } - public void DecodeReadDp(@Nullable JsonObject response, @Nullable String uid, @Nullable String dp, + public void decodeReadDp(@Nullable JsonObject response, @Nullable String uid, @Nullable String dp, @Nullable String type) { if (response != null && response.has("Data")) { JsonObject subResult = (JsonObject) response.get("Data"); @@ -233,14 +232,14 @@ private void ReadDp(String dp, String uid, String type, boolean async) { public void execute(java.net.URI uri, int status, @Nullable Object response) { // prevent async read if we just write so we have no overlaps long now = System.currentTimeMillis(); - if (now - LastWrite < 5000) { + if (now - lastWrite < 5000) { return; } logger.trace("End read : {}", dp); if (response instanceof JsonObject) { - DecodeReadDp((JsonObject) response, uid, dp, type); + decodeReadDp((JsonObject) response, uid, dp, type); } } }); @@ -248,7 +247,7 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { } else { if (lcHvacConnector != null) { JsonObject js = lcHvacConnector.doRequest(request); - DecodeReadDp(js, uid, dp, type); + decodeReadDp(js, uid, dp, type); } } } catch (Exception e) { @@ -264,7 +263,7 @@ private void WriteDp(String dp, Type dpVal, String type) { SiemensHvacConnector lcHvacConnector = hvacConnector; if (lcHvacConnector != null) { - lcHvacConnector.DisplayRequestStats(); + lcHvacConnector.displayRequestStats(); } if ("-1".equals(dp)) { @@ -274,7 +273,7 @@ private void WriteDp(String dp, Type dpVal, String type) { try { lockObj.lock(); logger.trace("Start write: {}", dp); - LastWrite = System.currentTimeMillis(); + lastWrite = System.currentTimeMillis(); String valUpdate = "0"; String valUpdateEnum = ""; @@ -371,7 +370,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { if (command instanceof RefreshType) { var channel = this.getThing().getChannel(channelUID); if (channel != null) { - ReadChannel(channel); + readChannel(channel); } } else { Channel channel = getThing().getChannel(channelUID); @@ -404,7 +403,6 @@ public void handleCommand(ChannelUID channelUID, Command command) { } if (command instanceof State) { - commandVar = applyState(tp, commandVar); State state = (State) commandVar; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java index e3c5ef2ab0284..e7d457063e19f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java @@ -27,7 +27,7 @@ public interface SiemensHvacConnector { @Nullable - String DoBasicRequest(String uri) throws Exception; + String doBasicRequest(String uri) throws Exception; @Nullable JsonObject doRequest(String req); @@ -35,9 +35,9 @@ public interface SiemensHvacConnector { @Nullable JsonObject doRequest(String req, @Nullable SiemensHvacCallback callback); - public void WaitAllPendingRequest(); + public void waitAllPendingRequest(); - public void WaitNoNewRequest(); + public void waitNoNewRequest(); public void onComplete(Request request); @@ -49,5 +49,5 @@ public interface SiemensHvacConnector { void ResetSessionId(boolean web); - public void DisplayRequestStats(); + public void displayRequestStats(); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 3d6cfc6dabf4f..395a259bbb95f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -60,10 +60,10 @@ @Component(immediate = true) public class SiemensHvacConnectorImpl implements SiemensHvacConnector { - private static final Logger logger = LoggerFactory.getLogger(SiemensHvacConnectorImpl.class); + private final Logger logger = LoggerFactory.getLogger(SiemensHvacConnectorImpl.class); - private final static Gson gson; - private final static Gson gsonWithAdpter; + private static final Gson gson; + private static final Gson gsonWithAdapter; private @Nullable String sessionId = null; private @Nullable String sessionIdHttp = null; @@ -90,7 +90,7 @@ public class SiemensHvacConnectorImpl implements SiemensHvacConnector { adapter.registerSubtype(SiemensHvacMetadataMenu.class); adapter.registerSubtype(SiemensHvacMetadataDataPoint.class); - gsonWithAdpter = new GsonBuilder().setPrettyPrinting().registerTypeAdapterFactory(adapter).create(); + gsonWithAdapter = new GsonBuilder().setPrettyPrinting().registerTypeAdapterFactory(adapter).create(); } @@ -283,14 +283,11 @@ private void doAuth(boolean http) throws Exception { boolean successVal = resultObj2.get("Success").getAsBoolean(); if (successVal) { - if (resultObj.has("SessionId")) { sessionId = resultObj.get("SessionId").getAsString(); logger.debug("Have new SessionId: {} ", sessionId); } - } - } } @@ -310,20 +307,19 @@ private void doAuth(boolean http) throws Exception { } catch (Exception ex) { logger.debug("siemensHvac:doAuth:error() {}", ex.getLocalizedMessage()); - } finally { } } @Override - public @Nullable String DoBasicRequest(String uri) throws Exception { - return DoBasicRequest(uri, null); + public @Nullable String doBasicRequest(String uri) throws Exception { + return doBasicRequest(uri, null); } - public @Nullable String DoBasicRequestAsync(String uri, @Nullable SiemensHvacCallback callback) throws Exception { - return DoBasicRequest(uri, callback); + public @Nullable String doBasicRequestAsync(String uri, @Nullable SiemensHvacCallback callback) throws Exception { + return doBasicRequest(uri, callback); } - public @Nullable String DoBasicRequest(String uri, @Nullable SiemensHvacCallback callback) throws Exception { + public @Nullable String doBasicRequest(String uri, @Nullable SiemensHvacCallback callback) throws Exception { if (sessionIdHttp == null) { doAuth(true); } @@ -383,7 +379,7 @@ private void doAuth(boolean http) throws Exception { @Override public @Nullable JsonObject doRequest(String req, @Nullable SiemensHvacCallback callback) { try { - String response = DoBasicRequest(req, callback); + String response = doBasicRequest(req, callback); if (response != null) { JsonObject resultObj = getGson().fromJson(response, JsonObject.class); @@ -411,13 +407,13 @@ private void doAuth(boolean http) throws Exception { } @Override - public void DisplayRequestStats() { + public void displayRequestStats() { logger.info("DisplayRequestStats : {} ({}/{})", (startedRequest - completedRequest), startedRequest, completedRequest); } @Override - public void WaitAllPendingRequest() { + public void waitAllPendingRequest() { logger.debug("WaitAllPendingRequest:start"); try { Thread.sleep(1000); @@ -445,7 +441,7 @@ public void WaitAllPendingRequest() { } @Override - public void WaitNoNewRequest() { + public void waitNoNewRequest() { logger.debug("WaitNoNewRequest:start"); try { int lastRequest = startedRequest; @@ -467,7 +463,7 @@ public static Gson getGson() { } public static Gson getGsonWithAdapter() { - return gsonWithAdpter; + return gsonWithAdapter; } public void AddDpUpdate(String itemName, Type dp) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java index 4788210583034..b3febc9bb0d02 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java @@ -49,7 +49,8 @@ public class SiemensHvacRequestListener extends BufferingResponseListener private static int onCompleteCount = 0; private static int onFailureCount = 0; - private static final Logger logger = LoggerFactory.getLogger(SiemensHvacRequestListener.class); + private final Logger logger = LoggerFactory.getLogger(SiemensHvacRequestListener.class); + private SiemensHvacConnector hvacConnector; /** @@ -137,7 +138,7 @@ public void onBegin(@Nullable Request request) { } } - public static void DisplayStats() { + public void displayStats() { logger.info("DisplayStats :"); logger.info(" requestListenerCount : {}", requestListenerCount); logger.info(" onSuccessCount : {}", onSuccessCount); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index 3cbfec16f2143..51d67c6bde1a9 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -39,14 +39,11 @@ public class UidUtils { * @return the label without invalid character */ public static String sanetizeId(String label) { - StringBuffer buffer = new StringBuffer(); for (int i = 0; i < label.length(); i++) { char c = label.charAt(i); - if (c == 130) { - - } else if (c >= 232 && c <= 234) { + if (c >= 232 && c <= 234) { c = 'e'; } else if (c >= 200 && c <= 202) { c = 'E'; @@ -100,8 +97,7 @@ else if (c == 248) { c = '_'; } - if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' || c == '-') { - } else { + if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' || c == '-')) { c = '_'; } @@ -207,13 +203,13 @@ public static ChannelTypeUID generateChannelTypeUID(SiemensHvacMetadataDataPoint String shortDesc = dpt.getShortDescEn(); String result = normalizeDescriptor(shortDesc); - if (type.equals("DateTime")) { + if ("DateTime".equals(type)) { result = "datetime"; - } else if (type.equals("String")) { + } else if ("String".equals(type)) { result = "string"; - } else if (type.equals("TimeOfDay")) { + } else if ("TimeOfDay".equals(type)) { result = "datetime"; - } else if (type.equals("Scheduler")) { + } else if ("Scheduler".equals(type)) { result = "datetime"; } From 3d588876b2d61db12dc9478b29cb1a484cc2f723 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 15 Nov 2023 10:45:09 +0100 Subject: [PATCH 081/214] review coding rules Signed-off-by: Laurent ARNAL --- .../doc/Diagram.png | Bin 0 -> 181348 bytes .../SiemensHvacMetadataRegistryImpl.java | 3 +- .../handler/SiemensHvacHandlerImpl.java | 8 +- .../metadata/RuntimeTypeAdapterFactory.java | 478 +++++++ .../metadata/SiemensHvacMetadata.java | 109 ++ .../SiemensHvacMetadataDataPoint.java | 236 ++++ .../metadata/SiemensHvacMetadataDevice.java | 103 ++ .../metadata/SiemensHvacMetadataLanguage.java | 64 + .../metadata/SiemensHvacMetadataMenu.java | 47 + .../SiemensHvacMetadataPointChild.java | 69 + .../metadata/SiemensHvacMetadataRegistry.java | 50 + .../SiemensHvacMetadataRegistryImpl.java | 1192 +++++++++++++++++ .../metadata/SiemensHvacMetadataUser.java | 64 + 13 files changed, 2417 insertions(+), 6 deletions(-) create mode 100644 bundles/org.openhab.binding.siemenshvac/doc/Diagram.png create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/RuntimeTypeAdapterFactory.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadata.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataDataPoint.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataDevice.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataLanguage.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataMenu.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataPointChild.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistry.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataUser.java diff --git a/bundles/org.openhab.binding.siemenshvac/doc/Diagram.png b/bundles/org.openhab.binding.siemenshvac/doc/Diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..300cc2f85838738d1915c87989e2724fa6460279 GIT binary patch literal 181348 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ǤI0X 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

Tf%`~6m*Ej73A^xXEZ*!7Q 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 literal 0 HcmV?d00001 diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index 9209a4df78c3a..957715e15c3a1 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -365,7 +365,6 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { tt = lcThingTypeProvider.getInternalThingType(thingTypeUID); if (tt == null) { - List groupTypes = new ArrayList<>(); int treeId = device.getTreeId(); @@ -690,7 +689,7 @@ public void readUserInfo() { idxCell++; } - if (userName.equals("")) { + if ("".equals(userName)) { continue; } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index 3bbdb63593979..d94874bd3a856 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -151,7 +151,7 @@ private void readChannel(Channel channel) { logger.debug("pollingCode : type is null {} ", channel); return; } - ReadDp(id, uid, type, true); + readDp(id, uid, type, true); logger.debug("{}", isLink); } @@ -209,7 +209,7 @@ public void decodeReadDp(@Nullable JsonObject response, @Nullable String uid, @N } } - private void ReadDp(String dp, String uid, String type, boolean async) { + private void readDp(String dp, String uid, String type, boolean async) { SiemensHvacConnector lcHvacConnector = hvacConnector; if ("-1".equals(dp)) { @@ -259,7 +259,7 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { } } - private void WriteDp(String dp, Type dpVal, String type) { + private void writeDp(String dp, Type dpVal, String type) { SiemensHvacConnector lcHvacConnector = hvacConnector; if (lcHvacConnector != null) { @@ -410,7 +410,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { } if (id != null && type != null) { - WriteDp(id, commandVar, dptType); + writeDp(id, commandVar, dptType); } } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/RuntimeTypeAdapterFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/RuntimeTypeAdapterFactory.java new file mode 100644 index 0000000000000..42f7598fb0f54 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/RuntimeTypeAdapterFactory.java @@ -0,0 +1,478 @@ +/** + * 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 + */ +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Copied from + * https://raw.githubusercontent.com/google/gson/master/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java + * and repackaged here with additional content from + * com.google.gson.internal.{Streams,TypeAdapters,LazilyParsedNumber} + * to avoid using the internal package. + */ +package org.openhab.binding.siemenshvac.internal.metadata; + +import java.io.EOFException; +import java.io.IOException; +import java.io.ObjectStreamException; +import java.math.BigDecimal; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonIOException; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSyntaxException; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import com.google.gson.stream.MalformedJsonException; + +/** + * Adapts values whose runtime type may differ from their declaration type. This + * is necessary when a field's type is not the same type that GSON should create + * when deserializing that field. For example, consider these types: + * + *

+ * {
+ *     @code
+ *     abstract class Shape {
+ *         int x;
+ *         int y;
+ *     }
+ *     class Circle extends Shape {
+ *         int radius;
+ *     }
+ *     class Rectangle extends Shape {
+ *         int width;
+ *         int height;
+ *     }
+ *     class Diamond extends Shape {
+ *         int width;
+ *         int height;
+ *     }
+ *     class Drawing {
+ *         Shape bottomShape;
+ *         Shape topShape;
+ *     }
+ * }
+ * 
+ *

+ * Without additional type information, the serialized JSON is ambiguous. Is + * the bottom shape in this drawing a rectangle or a diamond? + * + *

+ *    {@code
+ *   {
+ *     "bottomShape": {
+ *       "width": 10,
+ *       "height": 5,
+ *       "x": 0,
+ *       "y": 0
+ *     },
+ *     "topShape": {
+ *       "radius": 2,
+ *       "x": 4,
+ *       "y": 1
+ *     }
+ *   }}
+ * 
+ * + * This class addresses this problem by adding type information to the + * serialized JSON and honoring that type information when the JSON is + * deserialized: + * + *
+ *    {@code
+ *   {
+ *     "bottomShape": {
+ *       "type": "Diamond",
+ *       "width": 10,
+ *       "height": 5,
+ *       "x": 0,
+ *       "y": 0
+ *     },
+ *     "topShape": {
+ *       "type": "Circle",
+ *       "radius": 2,
+ *       "x": 4,
+ *       "y": 1
+ *     }
+ *   }}
+ * 
+ * + * Both the type field name ({@code "type"}) and the type labels ({@code + * "Rectangle"}) are configurable. + * + *

Registering Types

+ * Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field + * name to the {@link #of} factory method. If you don't supply an explicit type + * field name, {@code "type"} will be used. + * + *
+ * {
+ *     @code
+ *     RuntimeTypeAdapterFactory shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class, "type");
+ * }
+ * 
+ * + * Next register all of your subtypes. Every subtype must be explicitly + * registered. This protects your application from injection attacks. If you + * don't supply an explicit type label, the type's simple name will be used. + * + *
+ *    {@code
+ *   shapeAdapter.registerSubtype(Rectangle.class, "Rectangle");
+ *   shapeAdapter.registerSubtype(Circle.class, "Circle");
+ *   shapeAdapter.registerSubtype(Diamond.class, "Diamond");
+ * }
+ * 
+ * + * Finally, register the type adapter factory in your application's GSON builder: + * + *
+ * {
+ *     @code
+ *     Gson gson = new GsonBuilder().registerTypeAdapterFactory(shapeAdapterFactory).create();
+ * }
+ * 
+ * + * Like {@code GsonBuilder}, this API supports chaining: + * + *
+ * {
+ *     @code
+ *     RuntimeTypeAdapterFactory shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
+ *             .registerSubtype(Rectangle.class).registerSubtype(Circle.class).registerSubtype(Diamond.class);
+ * }
+ * 
+ */ +/** + * + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault +public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory { + private final Class baseType; + private final String typeFieldName; + private final Map> labelToSubtype = new LinkedHashMap>(); + private final Map, String> subtypeToLabel = new LinkedHashMap, String>(); + + private RuntimeTypeAdapterFactory(Class baseType, String typeFieldName) { + this.baseType = baseType; + this.typeFieldName = typeFieldName; + } + + /** + * Creates a new runtime type adapter using for {@code baseType} using {@code + * typeFieldName} as the type field name. Type field names are case sensitive. + */ + public static RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName) { + return new RuntimeTypeAdapterFactory(baseType, typeFieldName); + } + + /** + * Creates a new runtime type adapter for {@code baseType} using {@code "type"} as + * the type field name. + */ + public static RuntimeTypeAdapterFactory of(Class baseType) { + return new RuntimeTypeAdapterFactory(baseType, "type"); + } + + /** + * Registers {@code type} identified by {@code label}. Labels are case + * sensitive. + * + * @throws IllegalArgumentException if either {@code type} or {@code label} + * have already been registered on this type adapter. + */ + public RuntimeTypeAdapterFactory registerSubtype(Class type, String label) { + if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) { + throw new IllegalArgumentException("types and labels must be unique"); + } + labelToSubtype.put(label, type); + subtypeToLabel.put(type, label); + return this; + } + + /** + * Registers {@code type} identified by its {@link Class#getSimpleName simple + * name}. Labels are case sensitive. + * + * @throws IllegalArgumentException if either {@code type} or its simple name + * have already been registered on this type adapter. + */ + public RuntimeTypeAdapterFactory registerSubtype(Class type) { + return registerSubtype(type, type.getSimpleName()); + } + + @Override + public @Nullable TypeAdapter create(@Nullable Gson gson, @Nullable TypeToken type) { + if (type == null || type.getRawType() != baseType) { + return null; + } + if (gson == null) { + return null; + } + + final Map> labelToDelegate = new LinkedHashMap>(); + final Map, TypeAdapter> subtypeToDelegate = new LinkedHashMap, TypeAdapter>(); + for (Map.Entry> entry : labelToSubtype.entrySet()) { + TypeAdapter delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue())); + labelToDelegate.put(entry.getKey(), delegate); + subtypeToDelegate.put(entry.getValue(), delegate); + } + + return new TypeAdapter() { + @Override + public @Nullable R read(JsonReader in) throws IOException { + JsonElement jsonElement = RuntimeTypeAdapterFactory.parse(in); + if (jsonElement != null) { + JsonElement labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName); + if (labelJsonElement == null) { + throw new JsonParseException("cannot deserialize " + baseType + + " because it does not define a field named " + typeFieldName); + } + String label = labelJsonElement.getAsString(); + @SuppressWarnings("unchecked") // registration requires that subtype extends T + TypeAdapter delegate = (TypeAdapter) labelToDelegate.get(label); + if (delegate == null) { + throw new JsonParseException("cannot deserialize " + baseType + " subtype named " + label + + "; did you forget to register a subtype?"); + } + return delegate.fromJsonTree(jsonElement); + } else { + throw new JsonParseException("cannot deserialize " + baseType + " because jsonElement is null"); + } + } + + @Override + public void write(JsonWriter out, @Nullable R value) throws IOException { + if (value == null) { + return; + } + + Class srcType = value.getClass(); + String label = subtypeToLabel.get(srcType); + @SuppressWarnings("unchecked") // registration requires that subtype extends T + TypeAdapter delegate = (TypeAdapter) subtypeToDelegate.get(srcType); + if (delegate == null) { + throw new JsonParseException( + "cannot serialize " + srcType.getName() + "; did you forget to register a subtype?"); + } + JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject(); + if (jsonObject.has(typeFieldName)) { + throw new JsonParseException("cannot serialize " + srcType.getName() + + " because it already defines a field named " + typeFieldName); + } + JsonObject clone = new JsonObject(); + clone.add(typeFieldName, new JsonPrimitive(label)); + for (Map.Entry e : jsonObject.entrySet()) { + clone.add(e.getKey(), e.getValue()); + } + RuntimeTypeAdapterFactory.write(clone, out); + } + }.nullSafe(); + } + + /** + * Takes a reader in any state and returns the next value as a JsonElement. + */ + private static @Nullable JsonElement parse(JsonReader reader) throws JsonParseException { + boolean isEmpty = true; + try { + reader.peek(); + isEmpty = false; + return RuntimeTypeAdapterFactory.JSON_ELEMENT.read(reader); + } catch (EOFException e) { + /* + * For compatibility with JSON 1.5 and earlier, we return a JsonNull for + * empty documents instead of throwing. + */ + if (isEmpty) { + return JsonNull.INSTANCE; + } + // The stream ended prematurely so it is likely a syntax error. + throw new JsonSyntaxException(e); + } catch (MalformedJsonException e) { + throw new JsonSyntaxException(e); + } catch (IOException e) { + throw new JsonIOException(e); + } catch (NumberFormatException e) { + throw new JsonSyntaxException(e); + } + } + + /** + * Writes the JSON element to the writer, recursively. + */ + private static void write(JsonElement element, JsonWriter writer) throws IOException { + RuntimeTypeAdapterFactory.JSON_ELEMENT.write(writer, element); + } + + private static final TypeAdapter JSON_ELEMENT = new TypeAdapter() { + @Override + public @Nullable JsonElement read(JsonReader in) throws IOException { + switch (in.peek()) { + case STRING: + return new JsonPrimitive(in.nextString()); + case NUMBER: + String number = in.nextString(); + return new JsonPrimitive(new LazilyParsedNumber(number)); + case BOOLEAN: + return new JsonPrimitive(in.nextBoolean()); + case NULL: + in.nextNull(); + return JsonNull.INSTANCE; + case BEGIN_ARRAY: + JsonArray array = new JsonArray(); + in.beginArray(); + while (in.hasNext()) { + array.add(read(in)); + } + in.endArray(); + return array; + case BEGIN_OBJECT: + JsonObject object = new JsonObject(); + in.beginObject(); + while (in.hasNext()) { + object.add(in.nextName(), read(in)); + } + in.endObject(); + return object; + case END_DOCUMENT: + case NAME: + case END_OBJECT: + case END_ARRAY: + default: + throw new IllegalArgumentException(); + } + } + + @Override + public void write(JsonWriter out, @Nullable JsonElement value) throws IOException { + if (value == null || value.isJsonNull()) { + out.nullValue(); + } else if (value.isJsonPrimitive()) { + JsonPrimitive primitive = value.getAsJsonPrimitive(); + if (primitive.isNumber()) { + out.value(primitive.getAsNumber()); + } else if (primitive.isBoolean()) { + out.value(primitive.getAsBoolean()); + } else { + out.value(primitive.getAsString()); + } + + } else if (value.isJsonArray()) { + out.beginArray(); + for (JsonElement e : value.getAsJsonArray()) { + write(out, e); + } + out.endArray(); + + } else if (value.isJsonObject()) { + out.beginObject(); + for (Map.Entry e : value.getAsJsonObject().entrySet()) { + out.name(e.getKey()); + write(out, e.getValue()); + } + out.endObject(); + + } else { + throw new IllegalArgumentException("Couldn't write " + value.getClass()); + } + } + }; + + /** + * This class holds a number value that is lazily converted to a specific number type + * + * @author Inderjeet Singh + */ + public static final class LazilyParsedNumber extends Number { + private final String value; + + public LazilyParsedNumber(String value) { + this.value = value; + } + + @Override + public int intValue() { + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + try { + return (int) Long.parseLong(value); + } catch (NumberFormatException nfe) { + return new BigDecimal(value).intValue(); + } + } + } + + @Override + public long longValue() { + try { + return Long.parseLong(value); + } catch (NumberFormatException e) { + return new BigDecimal(value).longValue(); + } + } + + @Override + public float floatValue() { + return Float.parseFloat(value); + } + + @Override + public double doubleValue() { + return Double.parseDouble(value); + } + + @Override + public String toString() { + return value; + } + + /** + * If somebody is unlucky enough to have to serialize one of these, serialize + * it as a BigDecimal so that they won't need Gson on the other side to + * deserialize it. + */ + private Object writeReplace() throws ObjectStreamException { + return new BigDecimal(value); + } + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadata.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadata.java new file mode 100644 index 0000000000000..b9c1609a01675 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadata.java @@ -0,0 +1,109 @@ +/** + * 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.siemenshvac.internal.metadata; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault +public class SiemensHvacMetadata { + private int id = -1; + private int subId = -1; + private int groupId = -1; + private int catId = -1; + private String shortDescEn = ""; + private String longDescEn = ""; + private String shortDesc = ""; + private String longDesc = ""; + @Nullable + private transient SiemensHvacMetadata parent; + + public SiemensHvacMetadata() { + } + + public int getId() { + return id; + } + + public void setId(int Id) { + this.id = Id; + } + + public int getSubId() { + return subId; + } + + public void setSubId(int subId) { + this.subId = subId; + } + + public int getGroupId() { + return groupId; + } + + public void setGroupId(int groupId) { + this.groupId = groupId; + } + + public int getCatId() { + return catId; + } + + public void setCatId(int catId) { + this.catId = catId; + } + + public String getShortDescEn() { + return shortDescEn; + } + + public void setShortDescEn(String shortDesc) { + this.shortDescEn = shortDesc; + } + + public String getLongDescEn() { + return longDescEn; + } + + public void setLongDescEn(String longDesc) { + this.longDescEn = longDesc; + } + + public String getShortDesc() { + return shortDesc; + } + + public void setShortDesc(String shortDesc) { + this.shortDesc = shortDesc; + } + + public String getLongDesc() { + return longDesc; + } + + public void setLongDesc(String longDesc) { + this.longDesc = longDesc; + } + + public @Nullable SiemensHvacMetadata getParent() { + return parent; + } + + public void setParent(@Nullable SiemensHvacMetadata parent) { + this.parent = parent; + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataDataPoint.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataDataPoint.java new file mode 100644 index 0000000000000..444625ad0aecf --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataDataPoint.java @@ -0,0 +1,236 @@ +/** + * 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.siemenshvac.internal.metadata; + +import java.util.ArrayList; +import java.util.List; + +import javax.validation.constraints.NotNull; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +/** + * + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault +public class SiemensHvacMetadataDataPoint extends SiemensHvacMetadata { + private String dptId = "-1"; + private String dptType = ""; + private @Nullable String dptUnit = null; + private @Nullable String min = null; + private @Nullable String max = null; + private @Nullable String resolution = null; + private @Nullable String fieldWitdh = null; + private @Nullable String decimalDigits = null; + private boolean detailsResolved = false; + private @Nullable String dialogType = null; + private @Nullable String maxLength = null; + private @Nullable String address = null; + private int dptSubKey = -1; + private boolean writeAccess = false; + + private @NotNull List child = List.of(); + + public SiemensHvacMetadataDataPoint() { + child = new ArrayList(); + } + + public String getDptType() { + return dptType; + } + + public void setDptType(String dptType) { + this.dptType = dptType; + } + + public List getChild() { + return child; + } + + public void setChild(List child) { + this.child = child; + } + + public String getDptId() { + return dptId; + } + + public void setDptId(String dptId) { + this.dptId = dptId; + } + + public int getDptSubKey() { + return dptSubKey; + } + + public void setDptSubKey(int dptSubKey) { + this.dptSubKey = dptSubKey; + } + + public @Nullable String getAddress() { + return address; + } + + public void setWriteAccess(boolean writeAccess) { + this.writeAccess = writeAccess; + } + + public boolean getWriteAccess() { + return writeAccess; + } + + public void setAddress(String address) { + this.address = address; + } + + public @Nullable String getDptUnit() { + return dptUnit; + } + + public void setDptUnit(String dptUnit) { + this.dptUnit = dptUnit; + } + + public @Nullable String getMaxLength() { + return maxLength; + } + + public void setMaxLength(String maxLength) { + this.maxLength = maxLength; + } + + public @Nullable String getDialogType() { + return dialogType; + } + + public void setDialogType(String dialogType) { + this.dialogType = dialogType; + } + + public @Nullable String getMin() { + return min; + } + + public void setMin(String min) { + this.min = min; + } + + public @Nullable String getMax() { + return max; + } + + public void setMax(String max) { + this.max = max; + } + + public @Nullable String getResolution() { + return resolution; + } + + public void setResolution(String resolution) { + this.resolution = resolution; + } + + public @Nullable String getFieldWitdh() { + return fieldWitdh; + } + + public void setFieldWitdh(String fieldWitdh) { + this.fieldWitdh = fieldWitdh; + } + + public @Nullable String getDecimalDigits() { + return decimalDigits; + } + + public void setDecimalDigits(String decimalDigits) { + this.decimalDigits = decimalDigits; + } + + public Boolean getDetailsResolved() { + return detailsResolved; + } + + public void setDetailsResolved(Boolean detailsResolved) { + this.detailsResolved = detailsResolved; + } + + public void resolveDptDetails(JsonObject result) { + JsonObject subResultObj = result.getAsJsonObject("Result"); + JsonObject desc = result.getAsJsonObject("Description"); + + if (subResultObj.has("Success")) { + JsonObject error = subResultObj.getAsJsonObject("Error"); + String errorMsg = ""; + if (error != null) { + errorMsg = error.get("Txt").getAsString(); + } + + if (("datatype not supported").equals(errorMsg)) { + detailsResolved = true; + return; + } + + } + + if (desc != null) { + this.dptType = desc.get("Type").getAsString(); + + if (SiemensHvacBindingConstants.DPT_TYPE_ENUM.equals(dptType)) { + JsonArray enums = desc.getAsJsonArray("Enums"); + + for (Object obj : enums) { + JsonObject entry = (JsonObject) obj; + + SiemensHvacMetadataPointChild ch = new SiemensHvacMetadataPointChild(); + ch.setText(entry.get("Text").getAsString()); + ch.setValue(entry.get("Value").getAsString()); + ch.setIsActive(entry.get("IsCurrentValue").getAsString()); + child.add(ch); + } + } else if (SiemensHvacBindingConstants.DPT_TYPE_NUMERIC.equals(dptType)) { + this.dptUnit = desc.get("Unit").getAsString(); + this.min = desc.get("Min").getAsString(); + this.max = desc.get("Max").getAsString(); + this.resolution = desc.get("Resolution").getAsString(); + this.fieldWitdh = desc.get("FieldWitdh").getAsString(); + this.decimalDigits = desc.get("DecimalDigits").getAsString(); + } else if (SiemensHvacBindingConstants.DPT_TYPE_STRING.equals(dptType)) { + this.dialogType = desc.get("DialogType").getAsString(); + this.maxLength = desc.get("MaxLength").getAsString(); + } else if (SiemensHvacBindingConstants.DPT_TYPE_RADIO.equals(dptType)) { + JsonArray buttons = desc.getAsJsonArray("Buttons"); + + child = new ArrayList(); + + for (Object obj : buttons) { + JsonObject button = (JsonObject) obj; + + SiemensHvacMetadataPointChild ch = new SiemensHvacMetadataPointChild(); + ch.setOpt0(button.get("TextOpt0").getAsString()); + ch.setOpt1(button.get("TextOpt1").getAsString()); + ch.setIsActive(button.get("IsActive").getAsString()); + child.add(ch); + } + } + + detailsResolved = true; + } + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataDevice.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataDevice.java new file mode 100644 index 0000000000000..592b02fe9ccf0 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataDevice.java @@ -0,0 +1,103 @@ +/** + * 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.siemenshvac.internal.metadata; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault +public class SiemensHvacMetadataDevice { + + private String name = ""; + + private String addr = ""; + + private String type = "unknown"; + + private String serialNr = ""; + + private @Nullable String treeDate; + + private @Nullable String treeTime; + + private boolean treeGenerated; + private int treeId; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAddr() { + return addr; + } + + public void setAddr(String addr) { + this.addr = addr; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getSerialNr() { + return serialNr; + } + + public void setSerialNr(String serialNr) { + this.serialNr = serialNr; + } + + public @Nullable String getTreeDate() { + return treeDate; + } + + public void setTreeDate(String treeDate) { + this.treeDate = treeDate; + } + + public @Nullable String getTreeTime() { + return treeTime; + } + + public void setTreeTime(String treeTime) { + this.treeTime = treeTime; + } + + public boolean getTreeGenerated() { + return treeGenerated; + } + + public void setTreeGenerated(boolean treeGenerated) { + this.treeGenerated = treeGenerated; + } + + public int getTreeId() { + return treeId; + } + + public void setTreeId(int treeId) { + this.treeId = treeId; + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataLanguage.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataLanguage.java new file mode 100644 index 0000000000000..c4cc5b6b53995 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataLanguage.java @@ -0,0 +1,64 @@ +/** + * 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.siemenshvac.internal.metadata; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault +public class SiemensHvacMetadataLanguage { + private String name; + private int id; + private String language; + private int languageId; + + public SiemensHvacMetadataLanguage() { + name = ""; + language = ""; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + public int getLanguageId() { + return languageId; + } + + public void setLanguageId(int languageId) { + this.languageId = languageId; + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataMenu.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataMenu.java new file mode 100644 index 0000000000000..5cd5fa212f6d8 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataMenu.java @@ -0,0 +1,47 @@ +/** + * 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.siemenshvac.internal.metadata; + +import java.util.HashMap; +import java.util.LinkedHashMap; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault +public class SiemensHvacMetadataMenu extends SiemensHvacMetadata { + private LinkedHashMap childList; + + public SiemensHvacMetadataMenu() { + childList = new LinkedHashMap(); + } + + public void addChild(SiemensHvacMetadata information) { + childList.put(information.getId(), information); + } + + public HashMap getChilds() { + return this.childList; + } + + public boolean hasChild(int Id) { + return this.childList.containsKey(Id); + } + + public SiemensHvacMetadata getChild(int Id) { + return this.childList.get(Id); + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataPointChild.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataPointChild.java new file mode 100644 index 0000000000000..6e27e4dfc1b33 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataPointChild.java @@ -0,0 +1,69 @@ +/** + * 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.siemenshvac.internal.metadata; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault +public class SiemensHvacMetadataPointChild { + + private String text = ""; + private String value = ""; + private String opt0 = ""; + private String opt1 = ""; + private String isActive = ""; + + public String getText() { + return this.text; + } + + public void setText(String text) { + this.text = text; + } + + public String getValue() { + return this.value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getOpt0() { + return this.opt0; + } + + public void setOpt0(String opt0) { + this.opt0 = opt0; + } + + public String getOpt1() { + return this.opt1; + } + + public void setOpt1(String opt1) { + this.opt1 = opt1; + } + + public String getIsActive() { + return this.isActive; + } + + public void setIsActive(String isActive) { + this.isActive = isActive; + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistry.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistry.java new file mode 100644 index 0000000000000..3fb50cabc054a --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistry.java @@ -0,0 +1,50 @@ +/** + * 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.siemenshvac.internal.metadata; + +import java.util.ArrayList; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; +import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelTypeProvider; + +/** + * + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault +public interface SiemensHvacMetadataRegistry { + + /** + * Initializes the type generator. + */ + void initialize(); + + void readMeta(); + + @Nullable + SiemensHvacMetadataMenu getRoot(); + + @Nullable + ArrayList getDevices(); + + @Nullable + SiemensHvacMetadata getDptMap(@Nullable String key); + + @Nullable + SiemensHvacChannelTypeProvider getChannelTypeProvider(); + + @Nullable + SiemensHvacConnector getSiemensHvacConnector(); +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java new file mode 100644 index 0000000000000..957715e15c3a1 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -0,0 +1,1192 @@ +/** + * 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.siemenshvac.internal.metadata; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigDecimal; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; +import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeConfig; +import org.openhab.binding.siemenshvac.internal.network.SiemensHvacCallback; +import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; +import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnectorImpl; +import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelGroupTypeProvider; +import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelTypeProvider; +import org.openhab.binding.siemenshvac.internal.type.SiemensHvacConfigDescriptionProvider; +import org.openhab.binding.siemenshvac.internal.type.SiemensHvacThingTypeProvider; +import org.openhab.binding.siemenshvac.internal.type.UidUtils; +import org.openhab.core.OpenHAB; +import org.openhab.core.config.core.ConfigDescriptionBuilder; +import org.openhab.core.config.core.ConfigDescriptionParameter; +import org.openhab.core.config.core.ConfigDescriptionParameterGroup; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.type.ChannelDefinition; +import org.openhab.core.thing.type.ChannelDefinitionBuilder; +import org.openhab.core.thing.type.ChannelGroupDefinition; +import org.openhab.core.thing.type.ChannelGroupType; +import org.openhab.core.thing.type.ChannelGroupTypeBuilder; +import org.openhab.core.thing.type.ChannelGroupTypeUID; +import org.openhab.core.thing.type.ChannelType; +import org.openhab.core.thing.type.ChannelTypeBuilder; +import org.openhab.core.thing.type.ChannelTypeUID; +import org.openhab.core.thing.type.StateChannelTypeBuilder; +import org.openhab.core.thing.type.ThingType; +import org.openhab.core.thing.type.ThingTypeBuilder; +import org.openhab.core.types.StateDescriptionFragmentBuilder; +import org.openhab.core.types.StateOption; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +/** + * + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault +@Component(immediate = true) +public class SiemensHvacMetadataRegistryImpl implements SiemensHvacMetadataRegistry { + + private final Logger logger = LoggerFactory.getLogger(SiemensHvacMetadataRegistryImpl.class); + + // A map contains data point config read from Api and/or WebPages + private Map dptMap = new Hashtable(); + private @Nullable SiemensHvacMetadata root = null; + private @Nullable ArrayList devices = null; + + private static final String JSON_DIR = OpenHAB.getUserDataFolder() + File.separatorChar + "jsondb"; + + private @Nullable SiemensHvacThingTypeProvider thingTypeProvider; + private @Nullable SiemensHvacChannelTypeProvider channelTypeProvider; + private @Nullable SiemensHvacChannelGroupTypeProvider channelGroupTypeProvider; + private @Nullable SiemensHvacConfigDescriptionProvider configDescriptionProvider; + private @Nullable SiemensHvacConnector hvacConnector; + + private final HashMap userList; + + public SiemensHvacMetadataRegistryImpl() { + userList = new HashMap(); + } + + @Reference + protected void setSiemensHvacConnector(SiemensHvacConnector hvacConnector) { + this.hvacConnector = hvacConnector; + } + + protected void unsetSiemensHvacConnector(SiemensHvacConnector hvacConnector) { + this.hvacConnector = null; + } + + @Reference + protected void setThingTypeProvider(SiemensHvacThingTypeProvider thingTypeProvider) { + this.thingTypeProvider = thingTypeProvider; + } + + protected void unsetThingTypeProvider(SiemensHvacThingTypeProvider thingTypeProvider) { + this.thingTypeProvider = null; + } + + @Reference + protected void setChannelTypeProvider(SiemensHvacChannelTypeProvider channelTypeProvider) { + this.channelTypeProvider = channelTypeProvider; + } + + protected void unsetChannelTypeProvider(SiemensHvacChannelTypeProvider channelTypeProvider) { + this.channelTypeProvider = null; + } + + // + @Reference + protected void setChannelGroupTypeProvider(SiemensHvacChannelGroupTypeProvider channelGroupTypeProvider) { + this.channelGroupTypeProvider = channelGroupTypeProvider; + } + + protected void unsetChannelGroupTypeProvider(SiemensHvacChannelGroupTypeProvider channelGroupTypeProvider) { + this.channelGroupTypeProvider = null; + } + + @Reference + protected void setConfigDescriptionProvider(SiemensHvacConfigDescriptionProvider configDescriptionProvider) { + this.configDescriptionProvider = configDescriptionProvider; + } + + protected void unsetConfigDescriptionProvider(SiemensHvacConfigDescriptionProvider configDescriptionProvider) { + this.configDescriptionProvider = null; + } + + @Override + public @Nullable SiemensHvacConnector getSiemensHvacConnector() { + return this.hvacConnector; + } + + @Override + public @Nullable SiemensHvacChannelTypeProvider getChannelTypeProvider() { + return this.channelTypeProvider; + } + + @Override + public @Nullable ArrayList getDevices() { + return devices; + } + + /** + * Initializes the type generator. + */ + @Override + @Activate + public void initialize() { + } + + public void initDptMap(@Nullable SiemensHvacMetadata node) { + if (node == null) { + return; + } + + if (node.getClass() == SiemensHvacMetadataMenu.class) { + SiemensHvacMetadataMenu mInformation = (SiemensHvacMetadataMenu) node; + + for (SiemensHvacMetadata child : mInformation.getChilds().values()) { + initDptMap(child); + } + } + + if (!node.getLongDesc().isEmpty()) { + dptMap.put("byName" + node.getLongDesc(), node); + } + if (!node.getShortDesc().isEmpty()) { + dptMap.put("byName" + node.getShortDesc(), node); + } + + dptMap.put("byId" + node.getId(), node); + dptMap.put("bySubId" + node.getSubId(), node); + + if (node.getClass() == SiemensHvacMetadataDataPoint.class) { + SiemensHvacMetadataDataPoint dpi = (SiemensHvacMetadataDataPoint) node; + dptMap.put("byDptId" + dpi.getDptId(), node); + } + } + + class ResolveCount { + private int resolveCount = 0; + + public ResolveCount(int count) { + resolveCount = count; + } + + public void decreaseResolveCount() { + resolveCount--; + } + + public int getResolveCount() { + return resolveCount; + } + } + + public void resolveDetails(int unresolveCountP) { + ResolveCount rv = new ResolveCount(unresolveCountP); + + for (String key : dptMap.keySet()) { + if (key.indexOf("byId") < 0) { + continue; + } + + SiemensHvacMetadata node = dptMap.get(key); + if (node != null) { + if (node.getClass() == SiemensHvacMetadataDataPoint.class) { + SiemensHvacMetadataDataPoint dpi = (SiemensHvacMetadataDataPoint) node; + if (!dpi.getDetailsResolved()) { + resolveDptDetails(dpi, rv); + } + } + } + } + } + + public int unresolveCount() { + int count = 0; + for (String key : dptMap.keySet()) { + if (key.indexOf("byId") < 0) { + continue; + } + + SiemensHvacMetadata node = dptMap.get(key); + if (node != null) { + if (node.getClass() == SiemensHvacMetadataDataPoint.class) { + SiemensHvacMetadataDataPoint dpi = (SiemensHvacMetadataDataPoint) node; + if (!dpi.getDetailsResolved()) { + count++; + } + } + } + + } + + return count; + } + + @Override + public @Nullable SiemensHvacMetadataMenu getRoot() { + return (SiemensHvacMetadataMenu) root; + } + + @Override + public void readMeta() { + ArrayList lcDevices = devices; + SiemensHvacConnector lcHvacConnector = hvacConnector; + + if (root != null) { + return; + } + + if (lcHvacConnector == null) { + logger.info("SiemensHvacMetadataRegistryImpl:ReadMeta() : lHvacConnector not initialize."); + return; + } + + readUserInfo(); + + SiemensHvacBridgeConfig config = lcHvacConnector.getBridgeConfiguration(); + if (config == null) { + logger.info("SiemensHvacMetadataRegistryImpl:ReadMeta() : config not initialize."); + return; + } + + SiemensHvacMetadataUser user = null; + + String userName = config.userName; + if (userList.containsKey(userName)) { + user = userList.get(userName); + } + + if (user == null) { + logger.info("SiemensHvacMetadataRegistryImpl:ReadMeta() : cannot find user, aborting."); + return; + } + + logger.info("siemensHvac:Initialization():Begin_0001"); + + logger.info("siemensHvac:Initialization():ReadCache"); + loadMetaDataFromCache(); + + logger.info("siemensHvac:Initialization():ReadDeviceList"); + readDeviceList(); + + if (root == null) { + logger.info("siemensHvac:Initialization():BeginReadMenu"); + root = new SiemensHvacMetadataMenu(); + + changeLanguage(1); + readMetaData(root, -1, false); + lcHvacConnector.waitNoNewRequest(); + lcHvacConnector.waitAllPendingRequest(); + + changeLanguage(user.getLanguageId()); + readMetaData(root, -1, true); + lcHvacConnector.waitNoNewRequest(); + lcHvacConnector.waitAllPendingRequest(); + + logger.info("siemensHvac:Initialization():EndReadMenu"); + } + + if (root != null) { + logger.info("siemensHvac:Initialization():BeginInitDptMap"); + initDptMap(root); + logger.info("siemensHvac:Initialization():EndInitDptMap"); + } + + int unresolveCount = unresolveCount(); + // unresolveCount = 0; + + while (unresolveCount > 0) { + logger.info("siemensHvac:Initialization():BeginResolveDtpMap {}", unresolveCount); + resolveDetails(unresolveCount); + lcHvacConnector.waitAllPendingRequest(); + unresolveCount = unresolveCount(); + } + + logger.info("siemensHvac:Initialization():SaveCache"); + saveMetaDataToCache(); + + logger.info("siemensHvac:Initialization():InitThing"); + getRoot(); + lcDevices = devices; + if (lcDevices != null) { + for (SiemensHvacMetadataDevice device : lcDevices) { + if (device.getType().indexOf("OZW672") >= 0) { + continue; + } + + generateThingsType(device); + } + } + + logger.debug("siemensHvac:InitDptMap():end"); + } + + private void generateThingsType(SiemensHvacMetadataDevice device) { + SiemensHvacThingTypeProvider lcThingTypeProvider = thingTypeProvider; + SiemensHvacChannelTypeProvider lcChannelTypeProvider = channelTypeProvider; + SiemensHvacChannelGroupTypeProvider lcChannelGroupTypeProvider = channelGroupTypeProvider; + logger.debug("Generate thing types for device : {} / {}", device.getName(), device.getSerialNr()); + if (lcThingTypeProvider != null) { + ThingTypeUID thingTypeUID = UidUtils.generateThingTypeUID(device); + ThingType tt = null; + + tt = lcThingTypeProvider.getInternalThingType(thingTypeUID); + + if (tt == null) { + List groupTypes = new ArrayList<>(); + + int treeId = device.getTreeId(); + if (dptMap.containsKey("byId" + treeId)) { + SiemensHvacMetadataMenu menu = (SiemensHvacMetadataMenu) dptMap.get("byId" + treeId); + + if (menu != null) { + var childs = menu.getChilds().values(); + for (SiemensHvacMetadata child : childs) { + + if (child instanceof SiemensHvacMetadataMenu) { + SiemensHvacMetadataMenu subMenu = (SiemensHvacMetadataMenu) child; + + List channelDefinitions = new ArrayList<>(); + + for (SiemensHvacMetadata childDt : subMenu.getChilds().values()) { + + if (childDt instanceof SiemensHvacMetadataDataPoint) { + SiemensHvacMetadataDataPoint dataPoint = (SiemensHvacMetadataDataPoint) childDt; + + if (dataPoint.getDptType().isEmpty()) { + continue; + } + + ChannelTypeUID channelTypeUID = UidUtils.generateChannelTypeUID(dataPoint); + + ChannelType channelType = null; + + if (channelTypeProvider != null && lcChannelTypeProvider != null) { + channelType = lcChannelTypeProvider.getInternalChannelType(channelTypeUID); + if (channelType == null) { + channelType = createChannelType(dataPoint, channelTypeUID); + lcChannelTypeProvider.addChannelType(channelType); + } + } + + SiemensHvacMetadataDataPoint dpt = ((SiemensHvacMetadataDataPoint) childDt); + + Map props = new Hashtable(); + props.put("dptId", "" + dpt.getDptId()); + props.put("id", "" + dpt.getId()); + props.put("subId", "" + dpt.getSubId()); + props.put("groupdId", "" + dpt.getGroupId()); + + String id = dataPoint.getId() + "_" + + UidUtils.sanetizeId(dataPoint.getShortDesc()); + + if (channelType != null) { + ChannelDefinition channelDef = new ChannelDefinitionBuilder(id, + channelType.getUID()).withLabel(dataPoint.getShortDesc()) + .withDescription(dataPoint.getLongDesc()).withProperties(props) + .build(); + + channelDefinitions.add(channelDef); + } + } + } + + // generate group + ChannelGroupTypeUID groupTypeUID = UidUtils.generateChannelGroupTypeUID(subMenu); + ChannelGroupType groupType = null; + + if (lcChannelGroupTypeProvider != null) { + groupType = lcChannelGroupTypeProvider.getInternalChannelGroupType(groupTypeUID); + + if (groupType == null) { + String groupLabel = subMenu.getShortDesc(); + groupType = ChannelGroupTypeBuilder.instance(groupTypeUID, groupLabel) + .withChannelDefinitions(channelDefinitions).withCategory("") + .withDescription(menu.getLongDesc()).build(); + lcChannelGroupTypeProvider.addChannelGroupType(groupType); + groupTypes.add(groupType); + } + } + + } + } + } + + } + + tt = createThingType(device, groupTypes); + lcThingTypeProvider.addThingType(tt); + } + } + } + + private ChannelType createChannelType(SiemensHvacMetadataDataPoint dpt, ChannelTypeUID channelTypeUID) { + ChannelType channelType; + + String itemType = getItemType(dpt); + String category = getCategory(dpt); + String label = itemType; + String description = ""; + + StateDescriptionFragmentBuilder stateFragment = StateDescriptionFragmentBuilder.create(); + + List options = new ArrayList(); + if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { + StringBuilder descBuilder = new StringBuilder(); + descBuilder.append("Enum:"); + List childs = dpt.getChild(); + int idx = 0; + + for (SiemensHvacMetadataPointChild opt : childs) { + StateOption stOpt = new StateOption(opt.getValue(), opt.getText()); + options.add(stOpt); + if (idx > 0) { + descBuilder.append("_"); + } + + descBuilder.append(String.format("(%s:%s)", opt.getValue(), opt.getText())); + idx++; + } + description = descBuilder.toString(); + label = channelTypeUID.getId(); + } + + if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { + BigDecimal min = new BigDecimal(dpt.getMin()); + BigDecimal max = new BigDecimal(dpt.getMax()); + BigDecimal step = new BigDecimal(dpt.getResolution()); + + stateFragment = stateFragment.withMinimum(min).withMaximum(max).withStep(step).withReadOnly(false); + + description = channelTypeUID.toString(); + label = channelTypeUID.getId(); + } else { + stateFragment = stateFragment.withPattern(getStatePattern(dpt)).withReadOnly(!dpt.getWriteAccess()); + } + + if (!options.isEmpty()) { + stateFragment = stateFragment.withOptions(options); + } + + boolean isAdvanced = false; + if (channelTypeUID.getId().contains("-y")) { + isAdvanced = true; + } + if (channelTypeUID.getId().contains("-k")) { + isAdvanced = true; + } + if (channelTypeUID.getId().contains("histo")) { + isAdvanced = true; + } + if (channelTypeUID.getId().contains("-qx")) { + isAdvanced = true; + } + + final StateChannelTypeBuilder channelTypeBuilder = ChannelTypeBuilder.state(channelTypeUID, label, itemType) + .withStateDescriptionFragment(stateFragment.build()); + + channelType = channelTypeBuilder.isAdvanced(isAdvanced).withDescription(description).withCategory(category) + .build(); + + return channelType; + } + + /** + * Creates the ThingType for the given device. + */ + private ThingType createThingType(SiemensHvacMetadataDevice device, List groupTypes) { + SiemensHvacConfigDescriptionProvider lcConfigDescriptionProvider = configDescriptionProvider; + String name = device.getName(); + String description = device.getName(); + + List supportedBridgeTypeUids = new ArrayList<>(); + supportedBridgeTypeUids.add(SiemensHvacBindingConstants.THING_TYPE_OZW672.toString()); + ThingTypeUID thingTypeUID = UidUtils.generateThingTypeUID(device); + + Map properties = new HashMap<>(); + properties.put(Thing.PROPERTY_VENDOR, SiemensHvacBindingConstants.PROPERTY_VENDOR_NAME); + properties.put(Thing.PROPERTY_MODEL_ID, device.getType()); + + URI configDescriptionURI = getConfigDescriptionURI(device); + if (lcConfigDescriptionProvider != null + && lcConfigDescriptionProvider.getInternalConfigDescription(configDescriptionURI) == null) { + generateConfigDescription(device, groupTypes, configDescriptionURI); + } + + List groupDefinitions = new ArrayList<>(); + for (ChannelGroupType groupType : groupTypes) { + String id = groupType.getUID().getId(); + groupDefinitions.add(new ChannelGroupDefinition(id, groupType.getUID())); + } + + return ThingTypeBuilder.instance(thingTypeUID, name).withSupportedBridgeTypeUIDs(supportedBridgeTypeUids) + .withDescription(description).withChannelGroupDefinitions(groupDefinitions).withProperties(properties) + .withRepresentationProperty(Thing.PROPERTY_MODEL_ID).withConfigDescriptionURI(configDescriptionURI) + .withCategory(SiemensHvacBindingConstants.CATEGORY_THING_HVAC).build(); + } + + private URI getConfigDescriptionURI(SiemensHvacMetadataDevice device) { + return URI.create((String.format("%s:%s", SiemensHvacBindingConstants.CONFIG_DESCRIPTION_URI_THING_PREFIX, + UidUtils.generateThingTypeUID(device)))); + } + + private void generateConfigDescription(SiemensHvacMetadataDevice device, List groupTypes, + URI configDescriptionURI) { + SiemensHvacConfigDescriptionProvider lcConfigDescriptionProvider = configDescriptionProvider; + List parms = new ArrayList<>(); + List groups = new ArrayList<>(); + + if (lcConfigDescriptionProvider != null) { + lcConfigDescriptionProvider.addConfigDescription(ConfigDescriptionBuilder.create(configDescriptionURI) + .withParameters(parms).withParameterGroups(groups).build()); + } + } + + public String getItemType(SiemensHvacMetadataDataPoint dpt) { + if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_STRING)) { + return SiemensHvacBindingConstants.ITEM_TYPE_STRING; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { + return SiemensHvacBindingConstants.ITEM_TYPE_NUMBER; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { + return SiemensHvacBindingConstants.ITEM_TYPE_ENUMERATION; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_DATE_TIME)) { + return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_TIME)) { + return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_RADIO)) { + return SiemensHvacBindingConstants.ITEM_TYPE_CONTACT; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_SCHEDULER)) { + return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_CALENDAR)) { + return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; + } else { + logger.debug("unknow type in getItemType()"); + + } + + return ""; + } + + /** + * Determines the category for the given Datapoint. + */ + public static String getCategory(SiemensHvacMetadataDataPoint dp) { + String dpType = dp.getDptType(); + String dptUnit = dp.getDptUnit(); + + if (dptUnit == null) { + return ""; + } else if (dptUnit.contains("°C")) { + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_PROPS_TEMP; + } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_DATE_TIME)) { + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_PROPS_TIME; + } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_TIME)) { + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_PROPS_TIME; + } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_SWITCH; + } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_RADIO)) { + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_SWITCH; + } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_NUMBER; + } else { + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_CONTROL_HEATING; + } + } + + /** + * Returns the state pattern metadata string with unit for the given Datapoint. + */ + public static String getStatePattern(SiemensHvacMetadataDataPoint dpt) { + String unit = dpt.getDptUnit(); + + if ("%".equals(unit)) { + return "%d %%"; + } + + if (unit != null && !unit.isEmpty()) { + if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { + return String.format("%s %s", "%d", "%unit%"); + } + } + + return ""; + } + + public void readUserInfo() { + try { + SiemensHvacConnector lcHvacConnector = hvacConnector; + String request = "main.app?section=settings&subsection=user"; + + if (lcHvacConnector != null) { + String response = lcHvacConnector.doBasicRequest(request); + + if (response != null) { + String st = response; + st = st.replace("\n", ""); + + Pattern pattern1 = Pattern.compile("table class=\\\"user_table\\\".*?>(.*?)<\\/table>"); + Matcher matcher1 = pattern1.matcher(st); + + if (matcher1.find()) { + String userTable = matcher1.group(1); + + Pattern pattern2 = Pattern.compile("(.*?)<\\/tr>"); + Matcher matcher2 = pattern2.matcher(userTable); + + int idx = 0; + while (matcher2.find()) { + String line = matcher2.group(1); + + if (idx > 0) { + Pattern pattern3 = Pattern.compile("(.*?)<\\/td>"); + Matcher matcher3 = pattern3.matcher(line); + + int idxCell = 0; + String userName = ""; + String userEdit = ""; + String userId = ""; + while (matcher3.find()) { + String cell = matcher3.group(2); + String header = matcher3.group(1); + + if (idxCell == 0) { + userName = cell; + } else if (idxCell == 5) { + userEdit = header; + } + idxCell++; + } + + if ("".equals(userName)) { + continue; + } + + Pattern pattern4 = Pattern.compile("userid=(.+?)"); + Matcher matcher4 = pattern4.matcher(userEdit); + + if (matcher4.find()) { + userId = matcher4.group(1); + } + + SiemensHvacMetadataUser user = new SiemensHvacMetadataUser(); + user.setName(userName); + user.setId(Integer.parseInt(userId)); + + request = "main.app?section=settings&subsection=user&action=modify&userid=" + userId; + response = lcHvacConnector.doBasicRequest(request); + + Pattern pattern5 = Pattern.compile( + "", Pattern.MULTILINE); + Matcher matcher5 = pattern5.matcher(response); + + if (matcher5.find()) { + String optionsList = matcher5.group(1); + + Pattern pattern6 = java.util.regex.Pattern.compile( + "", Pattern.MULTILINE); + Matcher matcher6 = pattern6.matcher(optionsList); + + while (matcher6.find()) { + String id = matcher6.group(1); + String opt = matcher6.group(2); + String lang = matcher6.group(3); + + if (opt.indexOf("selected") >= 0) { + user.setLanguage(lang); + user.setLanguageId(Integer.parseInt(id)); + } + } + } + + userList.put(userName, user); + } + + idx++; + + } + } + } + } + } catch (Exception e) { + logger.error("siemensHvac:ResolveDpt:Error during dp reading: {}", e.getLocalizedMessage()); + // Reset sessionId so we redone _auth on error + } + } + + public void changeLanguage(int lang) { + try { + SiemensHvacConnector lcHvacConnector = hvacConnector; + String request = "main.app?section=settings&subsection=user&action=modify&userid=1&language=" + lang + + "&submit=OK"; + if (lcHvacConnector != null) { + lcHvacConnector.doBasicRequest(request); + lcHvacConnector.ResetSessionId(false); + lcHvacConnector.ResetSessionId(true); + } + + } catch ( + + Exception e) { + logger.error("siemensHvac:ResolveDpt:Error during dp reading: {}", e.getLocalizedMessage()); + // Reset sessionId so we redone _auth on error + } + } + + public void readDeviceList() { + try { + SiemensHvacConnector lcHvacConnector = hvacConnector; + ArrayList lcDevices = devices; + + lcDevices = new ArrayList(); + devices = lcDevices; + String request = "api/devicelist/list.json?"; + + JsonObject response = null; + if (lcHvacConnector != null) { + response = lcHvacConnector.doRequest(request); + } + JsonArray devicesList = null; + if (response != null) { + devicesList = response.getAsJsonArray("Devices"); + } + + if (devicesList == null) { + return; + } + + for (JsonElement device : devicesList) { + + JsonObject obj = (JsonObject) device; + String name = ""; + String addr = ""; + String type = ""; + String serialNr = ""; + String treeDate = ""; + String treeTime = ""; + boolean treeGenerated = false; + + if (obj.has("Name")) { + name = obj.get("Name").getAsString(); + } + + if (obj.has("Addr")) { + addr = obj.get("Addr").getAsString(); + } + + if (obj.has("Type")) { + type = obj.get("Type").getAsString(); + } + + if (obj.has("SerialNr")) { + serialNr = obj.get("SerialNr").getAsString(); + } + + if (obj.has("TreeDate")) { + treeDate = obj.get("TreeDate").getAsString(); + } + + if (obj.has("TreeTime")) { + treeTime = obj.get("TreeTime").getAsString(); + } + + if (obj.has("TreeGenerated")) { + treeGenerated = obj.get("TreeGenerated").getAsBoolean(); + } + + SiemensHvacMetadataDevice deviceObj = new SiemensHvacMetadataDevice(); + deviceObj.setName(name); + deviceObj.setAddr(addr); + deviceObj.setSerialNr(serialNr); + deviceObj.setType(type); + deviceObj.setTreeDate(treeDate); + deviceObj.setTreeTime(treeTime); + deviceObj.setTreeGenerated(treeGenerated); + + String request2 = "api/menutree/device_root.json?TreeName=Web&SerialNumber=" + serialNr; + if (lcHvacConnector != null) { + JsonObject response2 = lcHvacConnector.doRequest(request2); + + if (response2 != null && response2.has("TreeItem")) { + JsonObject tree = response2.getAsJsonObject("TreeItem"); + if (tree.has("Id")) { + int treeId = tree.get("Id").getAsInt(); + deviceObj.setTreeId(treeId); + } + } + } + + lcDevices.add(deviceObj); + } + + } catch (Exception e) { + logger.error("siemensHvac:ResolveDpt:Error during dp reading: {}", e.getLocalizedMessage()); + // Reset sessionId so we redone _auth on error + } + } + + public void readMetaData(@Nullable SiemensHvacMetadata parent, int id, boolean localized) { + try { + SiemensHvacConnector lcHvacConnector = hvacConnector; + String request = "api/menutree/list.json?"; + if (id != -1) { + request = request + "&Id=" + id; + } + + if (lcHvacConnector != null) { + lcHvacConnector.doRequest(request, new SiemensHvacCallback() { + + @Override + public void execute(URI uri, int status, @Nullable Object response) { + logger.debug("response for {}, status {}:", uri, status); + if (response instanceof JsonObject) { + decodeMetaDataResult((JsonObject) response, parent, id, localized); + } else { + logger.debug("error status {}: {}", uri, status); + } + } + }); + } + + } catch (Exception e) { + logger.error("siemensHvac:ResolveDpt:Error during dp reading: {} ; {}", id, e.getLocalizedMessage()); + // Reset sessionId so we redone _auth on error + } + } + + @SuppressWarnings("unused") + private static int nbDpt = 0; + + public void decodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMetadata parent, int id, + boolean localized) { + SiemensHvacConnector lcHvacConnector = hvacConnector; + if (resultObj.has("MenuItems")) { + if (parent != null) { + logger.debug("Decode menuItem for: {}", parent.getShortDesc()); + } + SiemensHvacMetadata childNode; + JsonArray menuItems = resultObj.getAsJsonArray("MenuItems"); + + for (JsonElement child : menuItems) { + JsonObject menuItem = child.getAsJsonObject(); + + int itemId = -1; + if (menuItem.has("Id")) { + itemId = menuItem.get("Id").getAsInt(); + } + + SiemensHvacMetadataMenu menu = (SiemensHvacMetadataMenu) parent; + + if (menu.hasChild(itemId)) { + childNode = menu.getChild(itemId); + } else { + childNode = new SiemensHvacMetadataMenu(); + childNode.setId(itemId); + childNode.setParent(parent); + + if (parent != null) { + menu.addChild(childNode); + } + } + + if (menuItem.has("Text")) { + JsonObject descObj = menuItem.getAsJsonObject("Text"); + + int catId = -1; + int groupId = -1; + int subItemId = -1; + String longDesc = ""; + String shortDesc = ""; + + if (descObj.has("CatId")) { + catId = descObj.get("CatId").getAsInt(); + } + if (descObj.has("GroupId")) { + groupId = descObj.get("GroupId").getAsInt(); + } + if (descObj.has("Id")) { + subItemId = descObj.get("Id").getAsInt(); + } + + if (descObj.has("Long")) { + longDesc = descObj.get("Long").getAsString(); + } + if (descObj.has("Short")) { + shortDesc = descObj.get("Short").getAsString(); + } + + childNode.setSubId(subItemId); + childNode.setCatId(catId); + childNode.setGroupId(groupId); + if (!localized) { + childNode.setShortDescEn(shortDesc); + childNode.setLongDescEn(longDesc); + } else { + childNode.setShortDesc(shortDesc); + childNode.setLongDesc(longDesc); + } + + readMetaData(childNode, itemId, localized); + } + + } + } + if (resultObj.has("DatapointItems")) + + { + if (parent != null) { + logger.debug("Decode dp for : {}", parent.getShortDesc()); + } + + SiemensHvacMetadata childNode; + JsonArray dptItems = resultObj.getAsJsonArray("DatapointItems"); + + Map idMap = new Hashtable(); + + for (JsonElement child : dptItems) { + JsonObject dptItem = child.getAsJsonObject(); + + nbDpt++; + + int nodeId = -1; + int dpSubKey = -1; + boolean hasWriteAccess = false; + String address = ""; + + if (dptItem.has("Id")) { + nodeId = dptItem.get("Id").getAsInt(); + } + + SiemensHvacMetadataMenu menu = (SiemensHvacMetadataMenu) parent; + + if (menu.hasChild(nodeId)) { + childNode = menu.getChild(nodeId); + } else { + childNode = new SiemensHvacMetadataDataPoint(); + childNode.setId(nodeId); + childNode.setParent(parent); + + menu.addChild(childNode); + } + + if (dptItem.has("Address")) { + address = dptItem.get("Address").getAsString(); + } + if (dptItem.has("DpSubKey")) { + dpSubKey = dptItem.get("DpSubKey").getAsInt(); + } + if (dptItem.has("WriteAccess")) { + hasWriteAccess = dptItem.get("WriteAccess").getAsBoolean(); + } + + SiemensHvacMetadataDataPoint dptChild = (SiemensHvacMetadataDataPoint) childNode; + + dptChild.setId(nodeId); + dptChild.setAddress(address); + dptChild.setDptSubKey(dpSubKey); + dptChild.setWriteAccess(hasWriteAccess); + + idMap.put("" + nodeId, dptChild); + + if (dptItem.has("Text")) { + JsonObject descObj = dptItem.getAsJsonObject("Text"); + + int catId = -1; + int groupId = -1; + int subItemId = -1; + String longDesc = ""; + String shortDesc = ""; + + if (descObj.has("CatId")) { + catId = descObj.get("CatId").getAsInt(); + } + if (descObj.has("GroupId")) { + groupId = descObj.get("GroupId").getAsInt(); + } + if (descObj.has("Id")) { + subItemId = descObj.get("Id").getAsInt(); + } + if (descObj.has("Long")) { + longDesc = descObj.get("Long").getAsString(); + } + if (descObj.has("Short")) { + shortDesc = descObj.get("Short").getAsString(); + } + + childNode.setSubId(subItemId); + childNode.setCatId(catId); + childNode.setGroupId(groupId); + + if (!localized) { + childNode.setShortDescEn(shortDesc); + childNode.setLongDescEn(longDesc); + } else { + childNode.setShortDesc(shortDesc); + childNode.setLongDesc(longDesc); + } + } + + } + + String request2 = "main.app?section=popcard&idtype=4"; + if (id != -1) { + request2 = request2 + "&id=" + id; + } + + if (lcHvacConnector != null) { + lcHvacConnector.doRequest(request2, new SiemensHvacCallback() { + + @Override + public void execute(URI uri, int status, @Nullable Object response) { + if (response != null) { + String st = (String) response; + st = st.replace("\n", ""); + + Pattern pattern = Pattern + .compile("td class=\\\"dp_linenumber\\\".*?>(.*?)<\\/td>.+?(?=id)id=\"dp(.+?)\""); + Matcher matcher = pattern.matcher(st); + + while (matcher.find()) { + String id = matcher.group(2); + String dptId = matcher.group(1); + + if (id != null && dptId != null && !id.isEmpty() && !dptId.isEmpty()) { + if (idMap.containsKey(id)) { + SiemensHvacMetadataDataPoint child = idMap.get(id); + if (child != null) { + child.setDptId(dptId); + } + } + + } + } + } + } + }); + } + + } + } + + @Override + public @Nullable SiemensHvacMetadata getDptMap(@Nullable String key) { + if (key == null) { + return null; + } + + if (dptMap.containsKey("byMenu" + key)) { + return dptMap.get("byMenu" + key); + } + if (dptMap.containsKey("byName" + key)) { + return dptMap.get("byName" + key); + } + if (dptMap.containsKey("byDptId" + key)) { + return dptMap.get("byDptId" + key); + } + if (dptMap.containsKey("byId" + key)) { + return dptMap.get("byId" + key); + } + + return null; + } + + public void loadMetaDataFromCache() { + File file = null; + + try { + file = new File(JSON_DIR + File.separator + "siemens.json"); + + if (!file.exists()) { + return; + } + + byte[] bytes = Files.readAllBytes(file.toPath()); + String js = new String(bytes, StandardCharsets.UTF_8); + + root = SiemensHvacConnectorImpl.getGsonWithAdapter().fromJson(js, SiemensHvacMetadataMenu.class); + } catch (IOException ioe) { + logger.warn("Couldn't read Siemens MetaData information from file '{}'.", file.getAbsolutePath()); + + } + } + + public void saveMetaDataToCache() { + File file = null; + + try { + file = new File(JSON_DIR + File.separator + "siemens.json"); + + if (!file.exists()) { + file.getParentFile().mkdirs(); + file.createNewFile(); + } + + try (FileOutputStream os = new FileOutputStream(file)) { + String js = SiemensHvacConnectorImpl.getGsonWithAdapter().toJson(root); + + byte[] bt = js.getBytes(); + os.write(bt); + os.flush(); + } + + } catch (IOException ioe) { + logger.warn("Couldn't write Siemens MetaData information to file '{}'.", file.getAbsolutePath()); + + } + } + + public void resolveDptDetails(SiemensHvacMetadataDataPoint dpt, ResolveCount rv) { + SiemensHvacConnector lcHvacConnector = hvacConnector; + if (dpt.getDetailsResolved()) { + return; + } + + String request = "api/menutree/datapoint_desc.json?Id=" + dpt.getId(); + if (lcHvacConnector != null) { + lcHvacConnector.doRequest(request, new SiemensHvacCallback() { + + @Override + public void execute(URI uri, int status, @Nullable Object response) { + if (response instanceof JsonObject) { + rv.decreaseResolveCount(); + logger.debug("siemensHvac:Initialization():ToResolve() {}", rv.getResolveCount()); + dpt.resolveDptDetails((JsonObject) response); + } else { + logger.debug("Invalid response from Siemens gateway, result is not a JsonObject"); + } + } + }); + } + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataUser.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataUser.java new file mode 100644 index 0000000000000..d83c3a2e68328 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataUser.java @@ -0,0 +1,64 @@ +/** + * 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.siemenshvac.internal.metadata; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault +public class SiemensHvacMetadataUser { + private String name; + private int id; + private String language; + private int languageId; + + public SiemensHvacMetadataUser() { + name = ""; + language = ""; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + public int getLanguageId() { + return languageId; + } + + public void setLanguageId(int languageId) { + this.languageId = languageId; + } +} From 8acf42e7da62f2a4d75e60e6d4c8555b96de2199 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 15 Nov 2023 10:51:16 +0100 Subject: [PATCH 082/214] fix coding rules Signed-off-by: Laurent ARNAL --- .../SiemensHvacMetadataRegistryImpl.java | 19 +++++++++++-------- .../SiemensHvacMetadataRegistryImpl.java | 19 +++++++++++-------- .../network/SiemensHvacConnector.java | 7 ++++++- .../network/SiemensHvacConnectorImpl.java | 19 ++++++++----------- .../network/SiemensHvacRequestListener.java | 2 +- 5 files changed, 37 insertions(+), 29 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java index 957715e15c3a1..06204097a1ea0 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java @@ -33,7 +33,6 @@ import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeConfig; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacCallback; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; -import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnectorImpl; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelGroupTypeProvider; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelTypeProvider; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacConfigDescriptionProvider; @@ -752,8 +751,8 @@ public void changeLanguage(int lang) { + "&submit=OK"; if (lcHvacConnector != null) { lcHvacConnector.doBasicRequest(request); - lcHvacConnector.ResetSessionId(false); - lcHvacConnector.ResetSessionId(true); + lcHvacConnector.resetSessionId(false); + lcHvacConnector.resetSessionId(true); } } catch ( @@ -1134,7 +1133,9 @@ public void loadMetaDataFromCache() { byte[] bytes = Files.readAllBytes(file.toPath()); String js = new String(bytes, StandardCharsets.UTF_8); - root = SiemensHvacConnectorImpl.getGsonWithAdapter().fromJson(js, SiemensHvacMetadataMenu.class); + if (hvacConnector != null) { + root = hvacConnector.getGsonWithAdapter().fromJson(js, SiemensHvacMetadataMenu.class); + } } catch (IOException ioe) { logger.warn("Couldn't read Siemens MetaData information from file '{}'.", file.getAbsolutePath()); @@ -1153,11 +1154,13 @@ public void saveMetaDataToCache() { } try (FileOutputStream os = new FileOutputStream(file)) { - String js = SiemensHvacConnectorImpl.getGsonWithAdapter().toJson(root); + if (hvacConnector != null) { + String js = hvacConnector.getGsonWithAdapter().toJson(root); - byte[] bt = js.getBytes(); - os.write(bt); - os.flush(); + byte[] bt = js.getBytes(); + os.write(bt); + os.flush(); + } } } catch (IOException ioe) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java index 957715e15c3a1..06204097a1ea0 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -33,7 +33,6 @@ import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeConfig; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacCallback; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; -import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnectorImpl; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelGroupTypeProvider; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelTypeProvider; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacConfigDescriptionProvider; @@ -752,8 +751,8 @@ public void changeLanguage(int lang) { + "&submit=OK"; if (lcHvacConnector != null) { lcHvacConnector.doBasicRequest(request); - lcHvacConnector.ResetSessionId(false); - lcHvacConnector.ResetSessionId(true); + lcHvacConnector.resetSessionId(false); + lcHvacConnector.resetSessionId(true); } } catch ( @@ -1134,7 +1133,9 @@ public void loadMetaDataFromCache() { byte[] bytes = Files.readAllBytes(file.toPath()); String js = new String(bytes, StandardCharsets.UTF_8); - root = SiemensHvacConnectorImpl.getGsonWithAdapter().fromJson(js, SiemensHvacMetadataMenu.class); + if (hvacConnector != null) { + root = hvacConnector.getGsonWithAdapter().fromJson(js, SiemensHvacMetadataMenu.class); + } } catch (IOException ioe) { logger.warn("Couldn't read Siemens MetaData information from file '{}'.", file.getAbsolutePath()); @@ -1153,11 +1154,13 @@ public void saveMetaDataToCache() { } try (FileOutputStream os = new FileOutputStream(file)) { - String js = SiemensHvacConnectorImpl.getGsonWithAdapter().toJson(root); + if (hvacConnector != null) { + String js = hvacConnector.getGsonWithAdapter().toJson(root); - byte[] bt = js.getBytes(); - os.write(bt); - os.flush(); + byte[] bt = js.getBytes(); + os.write(bt); + os.flush(); + } } } catch (IOException ioe) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java index e7d457063e19f..c99c0d554fcc0 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java @@ -18,6 +18,7 @@ import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeBaseThingHandler; import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeConfig; +import com.google.gson.Gson; import com.google.gson.JsonObject; /** @@ -47,7 +48,11 @@ public interface SiemensHvacConnector { public @Nullable SiemensHvacBridgeConfig getBridgeConfiguration(); - void ResetSessionId(boolean web); + void resetSessionId(boolean web); public void displayRequestStats(); + + Gson getGson(); + + Gson getGsonWithAdapter(); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 395a259bbb95f..24ac4e208ea2d 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -62,8 +62,8 @@ public class SiemensHvacConnectorImpl implements SiemensHvacConnector { private final Logger logger = LoggerFactory.getLogger(SiemensHvacConnectorImpl.class); - private static final Gson gson; - private static final Gson gsonWithAdapter; + private final Gson gson; + private final Gson gsonWithAdapter; private @Nullable String sessionId = null; private @Nullable String sessionIdHttp = null; @@ -81,7 +81,8 @@ public class SiemensHvacConnectorImpl implements SiemensHvacConnector { private @Nullable SiemensHvacBridgeBaseThingHandler hvacBridgeBaseThingHandler; - static { + @Activate + public SiemensHvacConnectorImpl(@Reference HttpClientFactory httpClientFactory) { GsonBuilder builder = new GsonBuilder(); gson = builder.setPrettyPrinting().create(); @@ -92,10 +93,6 @@ public class SiemensHvacConnectorImpl implements SiemensHvacConnector { gsonWithAdapter = new GsonBuilder().setPrettyPrinting().registerTypeAdapterFactory(adapter).create(); - } - - @Activate - public SiemensHvacConnectorImpl(@Reference HttpClientFactory httpClientFactory) { this.updateCommand = new Hashtable(); this.httpClientFactory = httpClientFactory; @@ -458,22 +455,22 @@ public void waitNoNewRequest() { logger.debug("WaitNoNewRequest:end WaitAllStartingRequest"); } - public static Gson getGson() { + public Gson getGson() { return gson; } - public static Gson getGsonWithAdapter() { + public Gson getGsonWithAdapter() { return gsonWithAdapter; } - public void AddDpUpdate(String itemName, Type dp) { + public void addDpUpdate(String itemName, Type dp) { synchronized (updateCommand) { updateCommand.put(itemName, dp); } } @Override - public void ResetSessionId(boolean web) { + public void resetSessionId(boolean web) { if (web) { sessionIdHttp = null; } else { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java index b3febc9bb0d02..e9371beecbf25 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java @@ -178,7 +178,7 @@ public void onComplete(@Nullable Result result) { } else { JsonObject resultObj = null; try { - Gson gson = SiemensHvacConnectorImpl.getGson(); + Gson gson = hvacConnector.getGson(); resultObj = gson.fromJson(content, JsonObject.class); } catch (JsonSyntaxException ex) { logger.debug("error: {}", ex.toString()); From 30309efda7c65ef6bd7a84206edb43bd604edb9d Mon Sep 17 00:00:00 2001 From: lo92fr Date: Wed, 15 Nov 2023 10:58:40 +0100 Subject: [PATCH 083/214] Delete bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata directory remove duplicate Metadata directory Signed-off-by: Laurent ARNAL --- .../Metadata/RuntimeTypeAdapterFactory.java | 478 ------- .../Metadata/SiemensHvacMetadata.java | 109 -- .../SiemensHvacMetadataDataPoint.java | 236 ---- .../Metadata/SiemensHvacMetadataDevice.java | 103 -- .../Metadata/SiemensHvacMetadataLanguage.java | 64 - .../Metadata/SiemensHvacMetadataMenu.java | 47 - .../SiemensHvacMetadataPointChild.java | 69 - .../Metadata/SiemensHvacMetadataRegistry.java | 50 - .../SiemensHvacMetadataRegistryImpl.java | 1195 ----------------- .../Metadata/SiemensHvacMetadataUser.java | 64 - 10 files changed, 2415 deletions(-) delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java deleted file mode 100644 index 42f7598fb0f54..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/RuntimeTypeAdapterFactory.java +++ /dev/null @@ -1,478 +0,0 @@ -/** - * 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 - */ -/* - * Copyright (C) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Copied from - * https://raw.githubusercontent.com/google/gson/master/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java - * and repackaged here with additional content from - * com.google.gson.internal.{Streams,TypeAdapters,LazilyParsedNumber} - * to avoid using the internal package. - */ -package org.openhab.binding.siemenshvac.internal.metadata; - -import java.io.EOFException; -import java.io.IOException; -import java.io.ObjectStreamException; -import java.math.BigDecimal; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; - -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonIOException; -import com.google.gson.JsonNull; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSyntaxException; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import com.google.gson.stream.MalformedJsonException; - -/** - * Adapts values whose runtime type may differ from their declaration type. This - * is necessary when a field's type is not the same type that GSON should create - * when deserializing that field. For example, consider these types: - * - *
- * {
- *     @code
- *     abstract class Shape {
- *         int x;
- *         int y;
- *     }
- *     class Circle extends Shape {
- *         int radius;
- *     }
- *     class Rectangle extends Shape {
- *         int width;
- *         int height;
- *     }
- *     class Diamond extends Shape {
- *         int width;
- *         int height;
- *     }
- *     class Drawing {
- *         Shape bottomShape;
- *         Shape topShape;
- *     }
- * }
- * 
- *

- * Without additional type information, the serialized JSON is ambiguous. Is - * the bottom shape in this drawing a rectangle or a diamond? - * - *

- *    {@code
- *   {
- *     "bottomShape": {
- *       "width": 10,
- *       "height": 5,
- *       "x": 0,
- *       "y": 0
- *     },
- *     "topShape": {
- *       "radius": 2,
- *       "x": 4,
- *       "y": 1
- *     }
- *   }}
- * 
- * - * This class addresses this problem by adding type information to the - * serialized JSON and honoring that type information when the JSON is - * deserialized: - * - *
- *    {@code
- *   {
- *     "bottomShape": {
- *       "type": "Diamond",
- *       "width": 10,
- *       "height": 5,
- *       "x": 0,
- *       "y": 0
- *     },
- *     "topShape": {
- *       "type": "Circle",
- *       "radius": 2,
- *       "x": 4,
- *       "y": 1
- *     }
- *   }}
- * 
- * - * Both the type field name ({@code "type"}) and the type labels ({@code - * "Rectangle"}) are configurable. - * - *

Registering Types

- * Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field - * name to the {@link #of} factory method. If you don't supply an explicit type - * field name, {@code "type"} will be used. - * - *
- * {
- *     @code
- *     RuntimeTypeAdapterFactory shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class, "type");
- * }
- * 
- * - * Next register all of your subtypes. Every subtype must be explicitly - * registered. This protects your application from injection attacks. If you - * don't supply an explicit type label, the type's simple name will be used. - * - *
- *    {@code
- *   shapeAdapter.registerSubtype(Rectangle.class, "Rectangle");
- *   shapeAdapter.registerSubtype(Circle.class, "Circle");
- *   shapeAdapter.registerSubtype(Diamond.class, "Diamond");
- * }
- * 
- * - * Finally, register the type adapter factory in your application's GSON builder: - * - *
- * {
- *     @code
- *     Gson gson = new GsonBuilder().registerTypeAdapterFactory(shapeAdapterFactory).create();
- * }
- * 
- * - * Like {@code GsonBuilder}, this API supports chaining: - * - *
- * {
- *     @code
- *     RuntimeTypeAdapterFactory shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
- *             .registerSubtype(Rectangle.class).registerSubtype(Circle.class).registerSubtype(Diamond.class);
- * }
- * 
- */ -/** - * - * @author Laurent Arnal - Initial contribution - */ -@NonNullByDefault -public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory { - private final Class baseType; - private final String typeFieldName; - private final Map> labelToSubtype = new LinkedHashMap>(); - private final Map, String> subtypeToLabel = new LinkedHashMap, String>(); - - private RuntimeTypeAdapterFactory(Class baseType, String typeFieldName) { - this.baseType = baseType; - this.typeFieldName = typeFieldName; - } - - /** - * Creates a new runtime type adapter using for {@code baseType} using {@code - * typeFieldName} as the type field name. Type field names are case sensitive. - */ - public static RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName) { - return new RuntimeTypeAdapterFactory(baseType, typeFieldName); - } - - /** - * Creates a new runtime type adapter for {@code baseType} using {@code "type"} as - * the type field name. - */ - public static RuntimeTypeAdapterFactory of(Class baseType) { - return new RuntimeTypeAdapterFactory(baseType, "type"); - } - - /** - * Registers {@code type} identified by {@code label}. Labels are case - * sensitive. - * - * @throws IllegalArgumentException if either {@code type} or {@code label} - * have already been registered on this type adapter. - */ - public RuntimeTypeAdapterFactory registerSubtype(Class type, String label) { - if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) { - throw new IllegalArgumentException("types and labels must be unique"); - } - labelToSubtype.put(label, type); - subtypeToLabel.put(type, label); - return this; - } - - /** - * Registers {@code type} identified by its {@link Class#getSimpleName simple - * name}. Labels are case sensitive. - * - * @throws IllegalArgumentException if either {@code type} or its simple name - * have already been registered on this type adapter. - */ - public RuntimeTypeAdapterFactory registerSubtype(Class type) { - return registerSubtype(type, type.getSimpleName()); - } - - @Override - public @Nullable TypeAdapter create(@Nullable Gson gson, @Nullable TypeToken type) { - if (type == null || type.getRawType() != baseType) { - return null; - } - if (gson == null) { - return null; - } - - final Map> labelToDelegate = new LinkedHashMap>(); - final Map, TypeAdapter> subtypeToDelegate = new LinkedHashMap, TypeAdapter>(); - for (Map.Entry> entry : labelToSubtype.entrySet()) { - TypeAdapter delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue())); - labelToDelegate.put(entry.getKey(), delegate); - subtypeToDelegate.put(entry.getValue(), delegate); - } - - return new TypeAdapter() { - @Override - public @Nullable R read(JsonReader in) throws IOException { - JsonElement jsonElement = RuntimeTypeAdapterFactory.parse(in); - if (jsonElement != null) { - JsonElement labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName); - if (labelJsonElement == null) { - throw new JsonParseException("cannot deserialize " + baseType - + " because it does not define a field named " + typeFieldName); - } - String label = labelJsonElement.getAsString(); - @SuppressWarnings("unchecked") // registration requires that subtype extends T - TypeAdapter delegate = (TypeAdapter) labelToDelegate.get(label); - if (delegate == null) { - throw new JsonParseException("cannot deserialize " + baseType + " subtype named " + label - + "; did you forget to register a subtype?"); - } - return delegate.fromJsonTree(jsonElement); - } else { - throw new JsonParseException("cannot deserialize " + baseType + " because jsonElement is null"); - } - } - - @Override - public void write(JsonWriter out, @Nullable R value) throws IOException { - if (value == null) { - return; - } - - Class srcType = value.getClass(); - String label = subtypeToLabel.get(srcType); - @SuppressWarnings("unchecked") // registration requires that subtype extends T - TypeAdapter delegate = (TypeAdapter) subtypeToDelegate.get(srcType); - if (delegate == null) { - throw new JsonParseException( - "cannot serialize " + srcType.getName() + "; did you forget to register a subtype?"); - } - JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject(); - if (jsonObject.has(typeFieldName)) { - throw new JsonParseException("cannot serialize " + srcType.getName() - + " because it already defines a field named " + typeFieldName); - } - JsonObject clone = new JsonObject(); - clone.add(typeFieldName, new JsonPrimitive(label)); - for (Map.Entry e : jsonObject.entrySet()) { - clone.add(e.getKey(), e.getValue()); - } - RuntimeTypeAdapterFactory.write(clone, out); - } - }.nullSafe(); - } - - /** - * Takes a reader in any state and returns the next value as a JsonElement. - */ - private static @Nullable JsonElement parse(JsonReader reader) throws JsonParseException { - boolean isEmpty = true; - try { - reader.peek(); - isEmpty = false; - return RuntimeTypeAdapterFactory.JSON_ELEMENT.read(reader); - } catch (EOFException e) { - /* - * For compatibility with JSON 1.5 and earlier, we return a JsonNull for - * empty documents instead of throwing. - */ - if (isEmpty) { - return JsonNull.INSTANCE; - } - // The stream ended prematurely so it is likely a syntax error. - throw new JsonSyntaxException(e); - } catch (MalformedJsonException e) { - throw new JsonSyntaxException(e); - } catch (IOException e) { - throw new JsonIOException(e); - } catch (NumberFormatException e) { - throw new JsonSyntaxException(e); - } - } - - /** - * Writes the JSON element to the writer, recursively. - */ - private static void write(JsonElement element, JsonWriter writer) throws IOException { - RuntimeTypeAdapterFactory.JSON_ELEMENT.write(writer, element); - } - - private static final TypeAdapter JSON_ELEMENT = new TypeAdapter() { - @Override - public @Nullable JsonElement read(JsonReader in) throws IOException { - switch (in.peek()) { - case STRING: - return new JsonPrimitive(in.nextString()); - case NUMBER: - String number = in.nextString(); - return new JsonPrimitive(new LazilyParsedNumber(number)); - case BOOLEAN: - return new JsonPrimitive(in.nextBoolean()); - case NULL: - in.nextNull(); - return JsonNull.INSTANCE; - case BEGIN_ARRAY: - JsonArray array = new JsonArray(); - in.beginArray(); - while (in.hasNext()) { - array.add(read(in)); - } - in.endArray(); - return array; - case BEGIN_OBJECT: - JsonObject object = new JsonObject(); - in.beginObject(); - while (in.hasNext()) { - object.add(in.nextName(), read(in)); - } - in.endObject(); - return object; - case END_DOCUMENT: - case NAME: - case END_OBJECT: - case END_ARRAY: - default: - throw new IllegalArgumentException(); - } - } - - @Override - public void write(JsonWriter out, @Nullable JsonElement value) throws IOException { - if (value == null || value.isJsonNull()) { - out.nullValue(); - } else if (value.isJsonPrimitive()) { - JsonPrimitive primitive = value.getAsJsonPrimitive(); - if (primitive.isNumber()) { - out.value(primitive.getAsNumber()); - } else if (primitive.isBoolean()) { - out.value(primitive.getAsBoolean()); - } else { - out.value(primitive.getAsString()); - } - - } else if (value.isJsonArray()) { - out.beginArray(); - for (JsonElement e : value.getAsJsonArray()) { - write(out, e); - } - out.endArray(); - - } else if (value.isJsonObject()) { - out.beginObject(); - for (Map.Entry e : value.getAsJsonObject().entrySet()) { - out.name(e.getKey()); - write(out, e.getValue()); - } - out.endObject(); - - } else { - throw new IllegalArgumentException("Couldn't write " + value.getClass()); - } - } - }; - - /** - * This class holds a number value that is lazily converted to a specific number type - * - * @author Inderjeet Singh - */ - public static final class LazilyParsedNumber extends Number { - private final String value; - - public LazilyParsedNumber(String value) { - this.value = value; - } - - @Override - public int intValue() { - try { - return Integer.parseInt(value); - } catch (NumberFormatException e) { - try { - return (int) Long.parseLong(value); - } catch (NumberFormatException nfe) { - return new BigDecimal(value).intValue(); - } - } - } - - @Override - public long longValue() { - try { - return Long.parseLong(value); - } catch (NumberFormatException e) { - return new BigDecimal(value).longValue(); - } - } - - @Override - public float floatValue() { - return Float.parseFloat(value); - } - - @Override - public double doubleValue() { - return Double.parseDouble(value); - } - - @Override - public String toString() { - return value; - } - - /** - * If somebody is unlucky enough to have to serialize one of these, serialize - * it as a BigDecimal so that they won't need Gson on the other side to - * deserialize it. - */ - private Object writeReplace() throws ObjectStreamException { - return new BigDecimal(value); - } - } -} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java deleted file mode 100644 index b9c1609a01675..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadata.java +++ /dev/null @@ -1,109 +0,0 @@ -/** - * 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.siemenshvac.internal.metadata; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; - -/** - * - * @author Laurent Arnal - Initial contribution - */ -@NonNullByDefault -public class SiemensHvacMetadata { - private int id = -1; - private int subId = -1; - private int groupId = -1; - private int catId = -1; - private String shortDescEn = ""; - private String longDescEn = ""; - private String shortDesc = ""; - private String longDesc = ""; - @Nullable - private transient SiemensHvacMetadata parent; - - public SiemensHvacMetadata() { - } - - public int getId() { - return id; - } - - public void setId(int Id) { - this.id = Id; - } - - public int getSubId() { - return subId; - } - - public void setSubId(int subId) { - this.subId = subId; - } - - public int getGroupId() { - return groupId; - } - - public void setGroupId(int groupId) { - this.groupId = groupId; - } - - public int getCatId() { - return catId; - } - - public void setCatId(int catId) { - this.catId = catId; - } - - public String getShortDescEn() { - return shortDescEn; - } - - public void setShortDescEn(String shortDesc) { - this.shortDescEn = shortDesc; - } - - public String getLongDescEn() { - return longDescEn; - } - - public void setLongDescEn(String longDesc) { - this.longDescEn = longDesc; - } - - public String getShortDesc() { - return shortDesc; - } - - public void setShortDesc(String shortDesc) { - this.shortDesc = shortDesc; - } - - public String getLongDesc() { - return longDesc; - } - - public void setLongDesc(String longDesc) { - this.longDesc = longDesc; - } - - public @Nullable SiemensHvacMetadata getParent() { - return parent; - } - - public void setParent(@Nullable SiemensHvacMetadata parent) { - this.parent = parent; - } -} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java deleted file mode 100644 index 444625ad0aecf..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDataPoint.java +++ /dev/null @@ -1,236 +0,0 @@ -/** - * 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.siemenshvac.internal.metadata; - -import java.util.ArrayList; -import java.util.List; - -import javax.validation.constraints.NotNull; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; - -/** - * - * @author Laurent Arnal - Initial contribution - */ -@NonNullByDefault -public class SiemensHvacMetadataDataPoint extends SiemensHvacMetadata { - private String dptId = "-1"; - private String dptType = ""; - private @Nullable String dptUnit = null; - private @Nullable String min = null; - private @Nullable String max = null; - private @Nullable String resolution = null; - private @Nullable String fieldWitdh = null; - private @Nullable String decimalDigits = null; - private boolean detailsResolved = false; - private @Nullable String dialogType = null; - private @Nullable String maxLength = null; - private @Nullable String address = null; - private int dptSubKey = -1; - private boolean writeAccess = false; - - private @NotNull List child = List.of(); - - public SiemensHvacMetadataDataPoint() { - child = new ArrayList(); - } - - public String getDptType() { - return dptType; - } - - public void setDptType(String dptType) { - this.dptType = dptType; - } - - public List getChild() { - return child; - } - - public void setChild(List child) { - this.child = child; - } - - public String getDptId() { - return dptId; - } - - public void setDptId(String dptId) { - this.dptId = dptId; - } - - public int getDptSubKey() { - return dptSubKey; - } - - public void setDptSubKey(int dptSubKey) { - this.dptSubKey = dptSubKey; - } - - public @Nullable String getAddress() { - return address; - } - - public void setWriteAccess(boolean writeAccess) { - this.writeAccess = writeAccess; - } - - public boolean getWriteAccess() { - return writeAccess; - } - - public void setAddress(String address) { - this.address = address; - } - - public @Nullable String getDptUnit() { - return dptUnit; - } - - public void setDptUnit(String dptUnit) { - this.dptUnit = dptUnit; - } - - public @Nullable String getMaxLength() { - return maxLength; - } - - public void setMaxLength(String maxLength) { - this.maxLength = maxLength; - } - - public @Nullable String getDialogType() { - return dialogType; - } - - public void setDialogType(String dialogType) { - this.dialogType = dialogType; - } - - public @Nullable String getMin() { - return min; - } - - public void setMin(String min) { - this.min = min; - } - - public @Nullable String getMax() { - return max; - } - - public void setMax(String max) { - this.max = max; - } - - public @Nullable String getResolution() { - return resolution; - } - - public void setResolution(String resolution) { - this.resolution = resolution; - } - - public @Nullable String getFieldWitdh() { - return fieldWitdh; - } - - public void setFieldWitdh(String fieldWitdh) { - this.fieldWitdh = fieldWitdh; - } - - public @Nullable String getDecimalDigits() { - return decimalDigits; - } - - public void setDecimalDigits(String decimalDigits) { - this.decimalDigits = decimalDigits; - } - - public Boolean getDetailsResolved() { - return detailsResolved; - } - - public void setDetailsResolved(Boolean detailsResolved) { - this.detailsResolved = detailsResolved; - } - - public void resolveDptDetails(JsonObject result) { - JsonObject subResultObj = result.getAsJsonObject("Result"); - JsonObject desc = result.getAsJsonObject("Description"); - - if (subResultObj.has("Success")) { - JsonObject error = subResultObj.getAsJsonObject("Error"); - String errorMsg = ""; - if (error != null) { - errorMsg = error.get("Txt").getAsString(); - } - - if (("datatype not supported").equals(errorMsg)) { - detailsResolved = true; - return; - } - - } - - if (desc != null) { - this.dptType = desc.get("Type").getAsString(); - - if (SiemensHvacBindingConstants.DPT_TYPE_ENUM.equals(dptType)) { - JsonArray enums = desc.getAsJsonArray("Enums"); - - for (Object obj : enums) { - JsonObject entry = (JsonObject) obj; - - SiemensHvacMetadataPointChild ch = new SiemensHvacMetadataPointChild(); - ch.setText(entry.get("Text").getAsString()); - ch.setValue(entry.get("Value").getAsString()); - ch.setIsActive(entry.get("IsCurrentValue").getAsString()); - child.add(ch); - } - } else if (SiemensHvacBindingConstants.DPT_TYPE_NUMERIC.equals(dptType)) { - this.dptUnit = desc.get("Unit").getAsString(); - this.min = desc.get("Min").getAsString(); - this.max = desc.get("Max").getAsString(); - this.resolution = desc.get("Resolution").getAsString(); - this.fieldWitdh = desc.get("FieldWitdh").getAsString(); - this.decimalDigits = desc.get("DecimalDigits").getAsString(); - } else if (SiemensHvacBindingConstants.DPT_TYPE_STRING.equals(dptType)) { - this.dialogType = desc.get("DialogType").getAsString(); - this.maxLength = desc.get("MaxLength").getAsString(); - } else if (SiemensHvacBindingConstants.DPT_TYPE_RADIO.equals(dptType)) { - JsonArray buttons = desc.getAsJsonArray("Buttons"); - - child = new ArrayList(); - - for (Object obj : buttons) { - JsonObject button = (JsonObject) obj; - - SiemensHvacMetadataPointChild ch = new SiemensHvacMetadataPointChild(); - ch.setOpt0(button.get("TextOpt0").getAsString()); - ch.setOpt1(button.get("TextOpt1").getAsString()); - ch.setIsActive(button.get("IsActive").getAsString()); - child.add(ch); - } - } - - detailsResolved = true; - } - } -} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java deleted file mode 100644 index 592b02fe9ccf0..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataDevice.java +++ /dev/null @@ -1,103 +0,0 @@ -/** - * 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.siemenshvac.internal.metadata; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; - -/** - * - * @author Laurent Arnal - Initial contribution - */ -@NonNullByDefault -public class SiemensHvacMetadataDevice { - - private String name = ""; - - private String addr = ""; - - private String type = "unknown"; - - private String serialNr = ""; - - private @Nullable String treeDate; - - private @Nullable String treeTime; - - private boolean treeGenerated; - private int treeId; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getAddr() { - return addr; - } - - public void setAddr(String addr) { - this.addr = addr; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getSerialNr() { - return serialNr; - } - - public void setSerialNr(String serialNr) { - this.serialNr = serialNr; - } - - public @Nullable String getTreeDate() { - return treeDate; - } - - public void setTreeDate(String treeDate) { - this.treeDate = treeDate; - } - - public @Nullable String getTreeTime() { - return treeTime; - } - - public void setTreeTime(String treeTime) { - this.treeTime = treeTime; - } - - public boolean getTreeGenerated() { - return treeGenerated; - } - - public void setTreeGenerated(boolean treeGenerated) { - this.treeGenerated = treeGenerated; - } - - public int getTreeId() { - return treeId; - } - - public void setTreeId(int treeId) { - this.treeId = treeId; - } -} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java deleted file mode 100644 index c4cc5b6b53995..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataLanguage.java +++ /dev/null @@ -1,64 +0,0 @@ -/** - * 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.siemenshvac.internal.metadata; - -import org.eclipse.jdt.annotation.NonNullByDefault; - -/** - * - * @author Laurent Arnal - Initial contribution - */ -@NonNullByDefault -public class SiemensHvacMetadataLanguage { - private String name; - private int id; - private String language; - private int languageId; - - public SiemensHvacMetadataLanguage() { - name = ""; - language = ""; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getLanguage() { - return language; - } - - public void setLanguage(String language) { - this.language = language; - } - - public int getLanguageId() { - return languageId; - } - - public void setLanguageId(int languageId) { - this.languageId = languageId; - } -} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java deleted file mode 100644 index 5cd5fa212f6d8..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataMenu.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * 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.siemenshvac.internal.metadata; - -import java.util.HashMap; -import java.util.LinkedHashMap; - -import org.eclipse.jdt.annotation.NonNullByDefault; - -/** - * - * @author Laurent Arnal - Initial contribution - */ -@NonNullByDefault -public class SiemensHvacMetadataMenu extends SiemensHvacMetadata { - private LinkedHashMap childList; - - public SiemensHvacMetadataMenu() { - childList = new LinkedHashMap(); - } - - public void addChild(SiemensHvacMetadata information) { - childList.put(information.getId(), information); - } - - public HashMap getChilds() { - return this.childList; - } - - public boolean hasChild(int Id) { - return this.childList.containsKey(Id); - } - - public SiemensHvacMetadata getChild(int Id) { - return this.childList.get(Id); - } -} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java deleted file mode 100644 index 6e27e4dfc1b33..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataPointChild.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * 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.siemenshvac.internal.metadata; - -import org.eclipse.jdt.annotation.NonNullByDefault; - -/** - * - * @author Laurent Arnal - Initial contribution - */ -@NonNullByDefault -public class SiemensHvacMetadataPointChild { - - private String text = ""; - private String value = ""; - private String opt0 = ""; - private String opt1 = ""; - private String isActive = ""; - - public String getText() { - return this.text; - } - - public void setText(String text) { - this.text = text; - } - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - - public String getOpt0() { - return this.opt0; - } - - public void setOpt0(String opt0) { - this.opt0 = opt0; - } - - public String getOpt1() { - return this.opt1; - } - - public void setOpt1(String opt1) { - this.opt1 = opt1; - } - - public String getIsActive() { - return this.isActive; - } - - public void setIsActive(String isActive) { - this.isActive = isActive; - } -} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java deleted file mode 100644 index 3fb50cabc054a..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistry.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * 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.siemenshvac.internal.metadata; - -import java.util.ArrayList; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; -import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelTypeProvider; - -/** - * - * @author Laurent Arnal - Initial contribution - */ -@NonNullByDefault -public interface SiemensHvacMetadataRegistry { - - /** - * Initializes the type generator. - */ - void initialize(); - - void readMeta(); - - @Nullable - SiemensHvacMetadataMenu getRoot(); - - @Nullable - ArrayList getDevices(); - - @Nullable - SiemensHvacMetadata getDptMap(@Nullable String key); - - @Nullable - SiemensHvacChannelTypeProvider getChannelTypeProvider(); - - @Nullable - SiemensHvacConnector getSiemensHvacConnector(); -} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java deleted file mode 100644 index 06204097a1ea0..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataRegistryImpl.java +++ /dev/null @@ -1,1195 +0,0 @@ -/** - * 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.siemenshvac.internal.metadata; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.math.BigDecimal; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; -import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeConfig; -import org.openhab.binding.siemenshvac.internal.network.SiemensHvacCallback; -import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; -import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelGroupTypeProvider; -import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelTypeProvider; -import org.openhab.binding.siemenshvac.internal.type.SiemensHvacConfigDescriptionProvider; -import org.openhab.binding.siemenshvac.internal.type.SiemensHvacThingTypeProvider; -import org.openhab.binding.siemenshvac.internal.type.UidUtils; -import org.openhab.core.OpenHAB; -import org.openhab.core.config.core.ConfigDescriptionBuilder; -import org.openhab.core.config.core.ConfigDescriptionParameter; -import org.openhab.core.config.core.ConfigDescriptionParameterGroup; -import org.openhab.core.thing.Thing; -import org.openhab.core.thing.ThingTypeUID; -import org.openhab.core.thing.type.ChannelDefinition; -import org.openhab.core.thing.type.ChannelDefinitionBuilder; -import org.openhab.core.thing.type.ChannelGroupDefinition; -import org.openhab.core.thing.type.ChannelGroupType; -import org.openhab.core.thing.type.ChannelGroupTypeBuilder; -import org.openhab.core.thing.type.ChannelGroupTypeUID; -import org.openhab.core.thing.type.ChannelType; -import org.openhab.core.thing.type.ChannelTypeBuilder; -import org.openhab.core.thing.type.ChannelTypeUID; -import org.openhab.core.thing.type.StateChannelTypeBuilder; -import org.openhab.core.thing.type.ThingType; -import org.openhab.core.thing.type.ThingTypeBuilder; -import org.openhab.core.types.StateDescriptionFragmentBuilder; -import org.openhab.core.types.StateOption; -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Reference; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -/** - * - * @author Laurent Arnal - Initial contribution - */ -@NonNullByDefault -@Component(immediate = true) -public class SiemensHvacMetadataRegistryImpl implements SiemensHvacMetadataRegistry { - - private final Logger logger = LoggerFactory.getLogger(SiemensHvacMetadataRegistryImpl.class); - - // A map contains data point config read from Api and/or WebPages - private Map dptMap = new Hashtable(); - private @Nullable SiemensHvacMetadata root = null; - private @Nullable ArrayList devices = null; - - private static final String JSON_DIR = OpenHAB.getUserDataFolder() + File.separatorChar + "jsondb"; - - private @Nullable SiemensHvacThingTypeProvider thingTypeProvider; - private @Nullable SiemensHvacChannelTypeProvider channelTypeProvider; - private @Nullable SiemensHvacChannelGroupTypeProvider channelGroupTypeProvider; - private @Nullable SiemensHvacConfigDescriptionProvider configDescriptionProvider; - private @Nullable SiemensHvacConnector hvacConnector; - - private final HashMap userList; - - public SiemensHvacMetadataRegistryImpl() { - userList = new HashMap(); - } - - @Reference - protected void setSiemensHvacConnector(SiemensHvacConnector hvacConnector) { - this.hvacConnector = hvacConnector; - } - - protected void unsetSiemensHvacConnector(SiemensHvacConnector hvacConnector) { - this.hvacConnector = null; - } - - @Reference - protected void setThingTypeProvider(SiemensHvacThingTypeProvider thingTypeProvider) { - this.thingTypeProvider = thingTypeProvider; - } - - protected void unsetThingTypeProvider(SiemensHvacThingTypeProvider thingTypeProvider) { - this.thingTypeProvider = null; - } - - @Reference - protected void setChannelTypeProvider(SiemensHvacChannelTypeProvider channelTypeProvider) { - this.channelTypeProvider = channelTypeProvider; - } - - protected void unsetChannelTypeProvider(SiemensHvacChannelTypeProvider channelTypeProvider) { - this.channelTypeProvider = null; - } - - // - @Reference - protected void setChannelGroupTypeProvider(SiemensHvacChannelGroupTypeProvider channelGroupTypeProvider) { - this.channelGroupTypeProvider = channelGroupTypeProvider; - } - - protected void unsetChannelGroupTypeProvider(SiemensHvacChannelGroupTypeProvider channelGroupTypeProvider) { - this.channelGroupTypeProvider = null; - } - - @Reference - protected void setConfigDescriptionProvider(SiemensHvacConfigDescriptionProvider configDescriptionProvider) { - this.configDescriptionProvider = configDescriptionProvider; - } - - protected void unsetConfigDescriptionProvider(SiemensHvacConfigDescriptionProvider configDescriptionProvider) { - this.configDescriptionProvider = null; - } - - @Override - public @Nullable SiemensHvacConnector getSiemensHvacConnector() { - return this.hvacConnector; - } - - @Override - public @Nullable SiemensHvacChannelTypeProvider getChannelTypeProvider() { - return this.channelTypeProvider; - } - - @Override - public @Nullable ArrayList getDevices() { - return devices; - } - - /** - * Initializes the type generator. - */ - @Override - @Activate - public void initialize() { - } - - public void initDptMap(@Nullable SiemensHvacMetadata node) { - if (node == null) { - return; - } - - if (node.getClass() == SiemensHvacMetadataMenu.class) { - SiemensHvacMetadataMenu mInformation = (SiemensHvacMetadataMenu) node; - - for (SiemensHvacMetadata child : mInformation.getChilds().values()) { - initDptMap(child); - } - } - - if (!node.getLongDesc().isEmpty()) { - dptMap.put("byName" + node.getLongDesc(), node); - } - if (!node.getShortDesc().isEmpty()) { - dptMap.put("byName" + node.getShortDesc(), node); - } - - dptMap.put("byId" + node.getId(), node); - dptMap.put("bySubId" + node.getSubId(), node); - - if (node.getClass() == SiemensHvacMetadataDataPoint.class) { - SiemensHvacMetadataDataPoint dpi = (SiemensHvacMetadataDataPoint) node; - dptMap.put("byDptId" + dpi.getDptId(), node); - } - } - - class ResolveCount { - private int resolveCount = 0; - - public ResolveCount(int count) { - resolveCount = count; - } - - public void decreaseResolveCount() { - resolveCount--; - } - - public int getResolveCount() { - return resolveCount; - } - } - - public void resolveDetails(int unresolveCountP) { - ResolveCount rv = new ResolveCount(unresolveCountP); - - for (String key : dptMap.keySet()) { - if (key.indexOf("byId") < 0) { - continue; - } - - SiemensHvacMetadata node = dptMap.get(key); - if (node != null) { - if (node.getClass() == SiemensHvacMetadataDataPoint.class) { - SiemensHvacMetadataDataPoint dpi = (SiemensHvacMetadataDataPoint) node; - if (!dpi.getDetailsResolved()) { - resolveDptDetails(dpi, rv); - } - } - } - } - } - - public int unresolveCount() { - int count = 0; - for (String key : dptMap.keySet()) { - if (key.indexOf("byId") < 0) { - continue; - } - - SiemensHvacMetadata node = dptMap.get(key); - if (node != null) { - if (node.getClass() == SiemensHvacMetadataDataPoint.class) { - SiemensHvacMetadataDataPoint dpi = (SiemensHvacMetadataDataPoint) node; - if (!dpi.getDetailsResolved()) { - count++; - } - } - } - - } - - return count; - } - - @Override - public @Nullable SiemensHvacMetadataMenu getRoot() { - return (SiemensHvacMetadataMenu) root; - } - - @Override - public void readMeta() { - ArrayList lcDevices = devices; - SiemensHvacConnector lcHvacConnector = hvacConnector; - - if (root != null) { - return; - } - - if (lcHvacConnector == null) { - logger.info("SiemensHvacMetadataRegistryImpl:ReadMeta() : lHvacConnector not initialize."); - return; - } - - readUserInfo(); - - SiemensHvacBridgeConfig config = lcHvacConnector.getBridgeConfiguration(); - if (config == null) { - logger.info("SiemensHvacMetadataRegistryImpl:ReadMeta() : config not initialize."); - return; - } - - SiemensHvacMetadataUser user = null; - - String userName = config.userName; - if (userList.containsKey(userName)) { - user = userList.get(userName); - } - - if (user == null) { - logger.info("SiemensHvacMetadataRegistryImpl:ReadMeta() : cannot find user, aborting."); - return; - } - - logger.info("siemensHvac:Initialization():Begin_0001"); - - logger.info("siemensHvac:Initialization():ReadCache"); - loadMetaDataFromCache(); - - logger.info("siemensHvac:Initialization():ReadDeviceList"); - readDeviceList(); - - if (root == null) { - logger.info("siemensHvac:Initialization():BeginReadMenu"); - root = new SiemensHvacMetadataMenu(); - - changeLanguage(1); - readMetaData(root, -1, false); - lcHvacConnector.waitNoNewRequest(); - lcHvacConnector.waitAllPendingRequest(); - - changeLanguage(user.getLanguageId()); - readMetaData(root, -1, true); - lcHvacConnector.waitNoNewRequest(); - lcHvacConnector.waitAllPendingRequest(); - - logger.info("siemensHvac:Initialization():EndReadMenu"); - } - - if (root != null) { - logger.info("siemensHvac:Initialization():BeginInitDptMap"); - initDptMap(root); - logger.info("siemensHvac:Initialization():EndInitDptMap"); - } - - int unresolveCount = unresolveCount(); - // unresolveCount = 0; - - while (unresolveCount > 0) { - logger.info("siemensHvac:Initialization():BeginResolveDtpMap {}", unresolveCount); - resolveDetails(unresolveCount); - lcHvacConnector.waitAllPendingRequest(); - unresolveCount = unresolveCount(); - } - - logger.info("siemensHvac:Initialization():SaveCache"); - saveMetaDataToCache(); - - logger.info("siemensHvac:Initialization():InitThing"); - getRoot(); - lcDevices = devices; - if (lcDevices != null) { - for (SiemensHvacMetadataDevice device : lcDevices) { - if (device.getType().indexOf("OZW672") >= 0) { - continue; - } - - generateThingsType(device); - } - } - - logger.debug("siemensHvac:InitDptMap():end"); - } - - private void generateThingsType(SiemensHvacMetadataDevice device) { - SiemensHvacThingTypeProvider lcThingTypeProvider = thingTypeProvider; - SiemensHvacChannelTypeProvider lcChannelTypeProvider = channelTypeProvider; - SiemensHvacChannelGroupTypeProvider lcChannelGroupTypeProvider = channelGroupTypeProvider; - logger.debug("Generate thing types for device : {} / {}", device.getName(), device.getSerialNr()); - if (lcThingTypeProvider != null) { - ThingTypeUID thingTypeUID = UidUtils.generateThingTypeUID(device); - ThingType tt = null; - - tt = lcThingTypeProvider.getInternalThingType(thingTypeUID); - - if (tt == null) { - List groupTypes = new ArrayList<>(); - - int treeId = device.getTreeId(); - if (dptMap.containsKey("byId" + treeId)) { - SiemensHvacMetadataMenu menu = (SiemensHvacMetadataMenu) dptMap.get("byId" + treeId); - - if (menu != null) { - var childs = menu.getChilds().values(); - for (SiemensHvacMetadata child : childs) { - - if (child instanceof SiemensHvacMetadataMenu) { - SiemensHvacMetadataMenu subMenu = (SiemensHvacMetadataMenu) child; - - List channelDefinitions = new ArrayList<>(); - - for (SiemensHvacMetadata childDt : subMenu.getChilds().values()) { - - if (childDt instanceof SiemensHvacMetadataDataPoint) { - SiemensHvacMetadataDataPoint dataPoint = (SiemensHvacMetadataDataPoint) childDt; - - if (dataPoint.getDptType().isEmpty()) { - continue; - } - - ChannelTypeUID channelTypeUID = UidUtils.generateChannelTypeUID(dataPoint); - - ChannelType channelType = null; - - if (channelTypeProvider != null && lcChannelTypeProvider != null) { - channelType = lcChannelTypeProvider.getInternalChannelType(channelTypeUID); - if (channelType == null) { - channelType = createChannelType(dataPoint, channelTypeUID); - lcChannelTypeProvider.addChannelType(channelType); - } - } - - SiemensHvacMetadataDataPoint dpt = ((SiemensHvacMetadataDataPoint) childDt); - - Map props = new Hashtable(); - props.put("dptId", "" + dpt.getDptId()); - props.put("id", "" + dpt.getId()); - props.put("subId", "" + dpt.getSubId()); - props.put("groupdId", "" + dpt.getGroupId()); - - String id = dataPoint.getId() + "_" - + UidUtils.sanetizeId(dataPoint.getShortDesc()); - - if (channelType != null) { - ChannelDefinition channelDef = new ChannelDefinitionBuilder(id, - channelType.getUID()).withLabel(dataPoint.getShortDesc()) - .withDescription(dataPoint.getLongDesc()).withProperties(props) - .build(); - - channelDefinitions.add(channelDef); - } - } - } - - // generate group - ChannelGroupTypeUID groupTypeUID = UidUtils.generateChannelGroupTypeUID(subMenu); - ChannelGroupType groupType = null; - - if (lcChannelGroupTypeProvider != null) { - groupType = lcChannelGroupTypeProvider.getInternalChannelGroupType(groupTypeUID); - - if (groupType == null) { - String groupLabel = subMenu.getShortDesc(); - groupType = ChannelGroupTypeBuilder.instance(groupTypeUID, groupLabel) - .withChannelDefinitions(channelDefinitions).withCategory("") - .withDescription(menu.getLongDesc()).build(); - lcChannelGroupTypeProvider.addChannelGroupType(groupType); - groupTypes.add(groupType); - } - } - - } - } - } - - } - - tt = createThingType(device, groupTypes); - lcThingTypeProvider.addThingType(tt); - } - } - } - - private ChannelType createChannelType(SiemensHvacMetadataDataPoint dpt, ChannelTypeUID channelTypeUID) { - ChannelType channelType; - - String itemType = getItemType(dpt); - String category = getCategory(dpt); - String label = itemType; - String description = ""; - - StateDescriptionFragmentBuilder stateFragment = StateDescriptionFragmentBuilder.create(); - - List options = new ArrayList(); - if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { - StringBuilder descBuilder = new StringBuilder(); - descBuilder.append("Enum:"); - List childs = dpt.getChild(); - int idx = 0; - - for (SiemensHvacMetadataPointChild opt : childs) { - StateOption stOpt = new StateOption(opt.getValue(), opt.getText()); - options.add(stOpt); - if (idx > 0) { - descBuilder.append("_"); - } - - descBuilder.append(String.format("(%s:%s)", opt.getValue(), opt.getText())); - idx++; - } - description = descBuilder.toString(); - label = channelTypeUID.getId(); - } - - if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { - BigDecimal min = new BigDecimal(dpt.getMin()); - BigDecimal max = new BigDecimal(dpt.getMax()); - BigDecimal step = new BigDecimal(dpt.getResolution()); - - stateFragment = stateFragment.withMinimum(min).withMaximum(max).withStep(step).withReadOnly(false); - - description = channelTypeUID.toString(); - label = channelTypeUID.getId(); - } else { - stateFragment = stateFragment.withPattern(getStatePattern(dpt)).withReadOnly(!dpt.getWriteAccess()); - } - - if (!options.isEmpty()) { - stateFragment = stateFragment.withOptions(options); - } - - boolean isAdvanced = false; - if (channelTypeUID.getId().contains("-y")) { - isAdvanced = true; - } - if (channelTypeUID.getId().contains("-k")) { - isAdvanced = true; - } - if (channelTypeUID.getId().contains("histo")) { - isAdvanced = true; - } - if (channelTypeUID.getId().contains("-qx")) { - isAdvanced = true; - } - - final StateChannelTypeBuilder channelTypeBuilder = ChannelTypeBuilder.state(channelTypeUID, label, itemType) - .withStateDescriptionFragment(stateFragment.build()); - - channelType = channelTypeBuilder.isAdvanced(isAdvanced).withDescription(description).withCategory(category) - .build(); - - return channelType; - } - - /** - * Creates the ThingType for the given device. - */ - private ThingType createThingType(SiemensHvacMetadataDevice device, List groupTypes) { - SiemensHvacConfigDescriptionProvider lcConfigDescriptionProvider = configDescriptionProvider; - String name = device.getName(); - String description = device.getName(); - - List supportedBridgeTypeUids = new ArrayList<>(); - supportedBridgeTypeUids.add(SiemensHvacBindingConstants.THING_TYPE_OZW672.toString()); - ThingTypeUID thingTypeUID = UidUtils.generateThingTypeUID(device); - - Map properties = new HashMap<>(); - properties.put(Thing.PROPERTY_VENDOR, SiemensHvacBindingConstants.PROPERTY_VENDOR_NAME); - properties.put(Thing.PROPERTY_MODEL_ID, device.getType()); - - URI configDescriptionURI = getConfigDescriptionURI(device); - if (lcConfigDescriptionProvider != null - && lcConfigDescriptionProvider.getInternalConfigDescription(configDescriptionURI) == null) { - generateConfigDescription(device, groupTypes, configDescriptionURI); - } - - List groupDefinitions = new ArrayList<>(); - for (ChannelGroupType groupType : groupTypes) { - String id = groupType.getUID().getId(); - groupDefinitions.add(new ChannelGroupDefinition(id, groupType.getUID())); - } - - return ThingTypeBuilder.instance(thingTypeUID, name).withSupportedBridgeTypeUIDs(supportedBridgeTypeUids) - .withDescription(description).withChannelGroupDefinitions(groupDefinitions).withProperties(properties) - .withRepresentationProperty(Thing.PROPERTY_MODEL_ID).withConfigDescriptionURI(configDescriptionURI) - .withCategory(SiemensHvacBindingConstants.CATEGORY_THING_HVAC).build(); - } - - private URI getConfigDescriptionURI(SiemensHvacMetadataDevice device) { - return URI.create((String.format("%s:%s", SiemensHvacBindingConstants.CONFIG_DESCRIPTION_URI_THING_PREFIX, - UidUtils.generateThingTypeUID(device)))); - } - - private void generateConfigDescription(SiemensHvacMetadataDevice device, List groupTypes, - URI configDescriptionURI) { - SiemensHvacConfigDescriptionProvider lcConfigDescriptionProvider = configDescriptionProvider; - List parms = new ArrayList<>(); - List groups = new ArrayList<>(); - - if (lcConfigDescriptionProvider != null) { - lcConfigDescriptionProvider.addConfigDescription(ConfigDescriptionBuilder.create(configDescriptionURI) - .withParameters(parms).withParameterGroups(groups).build()); - } - } - - public String getItemType(SiemensHvacMetadataDataPoint dpt) { - if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_STRING)) { - return SiemensHvacBindingConstants.ITEM_TYPE_STRING; - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { - return SiemensHvacBindingConstants.ITEM_TYPE_NUMBER; - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { - return SiemensHvacBindingConstants.ITEM_TYPE_ENUMERATION; - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_DATE_TIME)) { - return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_TIME)) { - return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_RADIO)) { - return SiemensHvacBindingConstants.ITEM_TYPE_CONTACT; - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_SCHEDULER)) { - return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_CALENDAR)) { - return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; - } else { - logger.debug("unknow type in getItemType()"); - - } - - return ""; - } - - /** - * Determines the category for the given Datapoint. - */ - public static String getCategory(SiemensHvacMetadataDataPoint dp) { - String dpType = dp.getDptType(); - String dptUnit = dp.getDptUnit(); - - if (dptUnit == null) { - return ""; - } else if (dptUnit.contains("°C")) { - return SiemensHvacBindingConstants.CATEGORY_CHANNEL_PROPS_TEMP; - } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_DATE_TIME)) { - return SiemensHvacBindingConstants.CATEGORY_CHANNEL_PROPS_TIME; - } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_TIME)) { - return SiemensHvacBindingConstants.CATEGORY_CHANNEL_PROPS_TIME; - } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { - return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_SWITCH; - } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_RADIO)) { - return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_SWITCH; - } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { - return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_NUMBER; - } else { - return SiemensHvacBindingConstants.CATEGORY_CHANNEL_CONTROL_HEATING; - } - } - - /** - * Returns the state pattern metadata string with unit for the given Datapoint. - */ - public static String getStatePattern(SiemensHvacMetadataDataPoint dpt) { - String unit = dpt.getDptUnit(); - - if ("%".equals(unit)) { - return "%d %%"; - } - - if (unit != null && !unit.isEmpty()) { - if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { - return String.format("%s %s", "%d", "%unit%"); - } - } - - return ""; - } - - public void readUserInfo() { - try { - SiemensHvacConnector lcHvacConnector = hvacConnector; - String request = "main.app?section=settings&subsection=user"; - - if (lcHvacConnector != null) { - String response = lcHvacConnector.doBasicRequest(request); - - if (response != null) { - String st = response; - st = st.replace("\n", ""); - - Pattern pattern1 = Pattern.compile("table class=\\\"user_table\\\".*?>(.*?)<\\/table>"); - Matcher matcher1 = pattern1.matcher(st); - - if (matcher1.find()) { - String userTable = matcher1.group(1); - - Pattern pattern2 = Pattern.compile("(.*?)<\\/tr>"); - Matcher matcher2 = pattern2.matcher(userTable); - - int idx = 0; - while (matcher2.find()) { - String line = matcher2.group(1); - - if (idx > 0) { - Pattern pattern3 = Pattern.compile("(.*?)<\\/td>"); - Matcher matcher3 = pattern3.matcher(line); - - int idxCell = 0; - String userName = ""; - String userEdit = ""; - String userId = ""; - while (matcher3.find()) { - String cell = matcher3.group(2); - String header = matcher3.group(1); - - if (idxCell == 0) { - userName = cell; - } else if (idxCell == 5) { - userEdit = header; - } - idxCell++; - } - - if ("".equals(userName)) { - continue; - } - - Pattern pattern4 = Pattern.compile("userid=(.+?)"); - Matcher matcher4 = pattern4.matcher(userEdit); - - if (matcher4.find()) { - userId = matcher4.group(1); - } - - SiemensHvacMetadataUser user = new SiemensHvacMetadataUser(); - user.setName(userName); - user.setId(Integer.parseInt(userId)); - - request = "main.app?section=settings&subsection=user&action=modify&userid=" + userId; - response = lcHvacConnector.doBasicRequest(request); - - Pattern pattern5 = Pattern.compile( - "", Pattern.MULTILINE); - Matcher matcher5 = pattern5.matcher(response); - - if (matcher5.find()) { - String optionsList = matcher5.group(1); - - Pattern pattern6 = java.util.regex.Pattern.compile( - "", Pattern.MULTILINE); - Matcher matcher6 = pattern6.matcher(optionsList); - - while (matcher6.find()) { - String id = matcher6.group(1); - String opt = matcher6.group(2); - String lang = matcher6.group(3); - - if (opt.indexOf("selected") >= 0) { - user.setLanguage(lang); - user.setLanguageId(Integer.parseInt(id)); - } - } - } - - userList.put(userName, user); - } - - idx++; - - } - } - } - } - } catch (Exception e) { - logger.error("siemensHvac:ResolveDpt:Error during dp reading: {}", e.getLocalizedMessage()); - // Reset sessionId so we redone _auth on error - } - } - - public void changeLanguage(int lang) { - try { - SiemensHvacConnector lcHvacConnector = hvacConnector; - String request = "main.app?section=settings&subsection=user&action=modify&userid=1&language=" + lang - + "&submit=OK"; - if (lcHvacConnector != null) { - lcHvacConnector.doBasicRequest(request); - lcHvacConnector.resetSessionId(false); - lcHvacConnector.resetSessionId(true); - } - - } catch ( - - Exception e) { - logger.error("siemensHvac:ResolveDpt:Error during dp reading: {}", e.getLocalizedMessage()); - // Reset sessionId so we redone _auth on error - } - } - - public void readDeviceList() { - try { - SiemensHvacConnector lcHvacConnector = hvacConnector; - ArrayList lcDevices = devices; - - lcDevices = new ArrayList(); - devices = lcDevices; - String request = "api/devicelist/list.json?"; - - JsonObject response = null; - if (lcHvacConnector != null) { - response = lcHvacConnector.doRequest(request); - } - JsonArray devicesList = null; - if (response != null) { - devicesList = response.getAsJsonArray("Devices"); - } - - if (devicesList == null) { - return; - } - - for (JsonElement device : devicesList) { - - JsonObject obj = (JsonObject) device; - String name = ""; - String addr = ""; - String type = ""; - String serialNr = ""; - String treeDate = ""; - String treeTime = ""; - boolean treeGenerated = false; - - if (obj.has("Name")) { - name = obj.get("Name").getAsString(); - } - - if (obj.has("Addr")) { - addr = obj.get("Addr").getAsString(); - } - - if (obj.has("Type")) { - type = obj.get("Type").getAsString(); - } - - if (obj.has("SerialNr")) { - serialNr = obj.get("SerialNr").getAsString(); - } - - if (obj.has("TreeDate")) { - treeDate = obj.get("TreeDate").getAsString(); - } - - if (obj.has("TreeTime")) { - treeTime = obj.get("TreeTime").getAsString(); - } - - if (obj.has("TreeGenerated")) { - treeGenerated = obj.get("TreeGenerated").getAsBoolean(); - } - - SiemensHvacMetadataDevice deviceObj = new SiemensHvacMetadataDevice(); - deviceObj.setName(name); - deviceObj.setAddr(addr); - deviceObj.setSerialNr(serialNr); - deviceObj.setType(type); - deviceObj.setTreeDate(treeDate); - deviceObj.setTreeTime(treeTime); - deviceObj.setTreeGenerated(treeGenerated); - - String request2 = "api/menutree/device_root.json?TreeName=Web&SerialNumber=" + serialNr; - if (lcHvacConnector != null) { - JsonObject response2 = lcHvacConnector.doRequest(request2); - - if (response2 != null && response2.has("TreeItem")) { - JsonObject tree = response2.getAsJsonObject("TreeItem"); - if (tree.has("Id")) { - int treeId = tree.get("Id").getAsInt(); - deviceObj.setTreeId(treeId); - } - } - } - - lcDevices.add(deviceObj); - } - - } catch (Exception e) { - logger.error("siemensHvac:ResolveDpt:Error during dp reading: {}", e.getLocalizedMessage()); - // Reset sessionId so we redone _auth on error - } - } - - public void readMetaData(@Nullable SiemensHvacMetadata parent, int id, boolean localized) { - try { - SiemensHvacConnector lcHvacConnector = hvacConnector; - String request = "api/menutree/list.json?"; - if (id != -1) { - request = request + "&Id=" + id; - } - - if (lcHvacConnector != null) { - lcHvacConnector.doRequest(request, new SiemensHvacCallback() { - - @Override - public void execute(URI uri, int status, @Nullable Object response) { - logger.debug("response for {}, status {}:", uri, status); - if (response instanceof JsonObject) { - decodeMetaDataResult((JsonObject) response, parent, id, localized); - } else { - logger.debug("error status {}: {}", uri, status); - } - } - }); - } - - } catch (Exception e) { - logger.error("siemensHvac:ResolveDpt:Error during dp reading: {} ; {}", id, e.getLocalizedMessage()); - // Reset sessionId so we redone _auth on error - } - } - - @SuppressWarnings("unused") - private static int nbDpt = 0; - - public void decodeMetaDataResult(JsonObject resultObj, @Nullable SiemensHvacMetadata parent, int id, - boolean localized) { - SiemensHvacConnector lcHvacConnector = hvacConnector; - if (resultObj.has("MenuItems")) { - if (parent != null) { - logger.debug("Decode menuItem for: {}", parent.getShortDesc()); - } - SiemensHvacMetadata childNode; - JsonArray menuItems = resultObj.getAsJsonArray("MenuItems"); - - for (JsonElement child : menuItems) { - JsonObject menuItem = child.getAsJsonObject(); - - int itemId = -1; - if (menuItem.has("Id")) { - itemId = menuItem.get("Id").getAsInt(); - } - - SiemensHvacMetadataMenu menu = (SiemensHvacMetadataMenu) parent; - - if (menu.hasChild(itemId)) { - childNode = menu.getChild(itemId); - } else { - childNode = new SiemensHvacMetadataMenu(); - childNode.setId(itemId); - childNode.setParent(parent); - - if (parent != null) { - menu.addChild(childNode); - } - } - - if (menuItem.has("Text")) { - JsonObject descObj = menuItem.getAsJsonObject("Text"); - - int catId = -1; - int groupId = -1; - int subItemId = -1; - String longDesc = ""; - String shortDesc = ""; - - if (descObj.has("CatId")) { - catId = descObj.get("CatId").getAsInt(); - } - if (descObj.has("GroupId")) { - groupId = descObj.get("GroupId").getAsInt(); - } - if (descObj.has("Id")) { - subItemId = descObj.get("Id").getAsInt(); - } - - if (descObj.has("Long")) { - longDesc = descObj.get("Long").getAsString(); - } - if (descObj.has("Short")) { - shortDesc = descObj.get("Short").getAsString(); - } - - childNode.setSubId(subItemId); - childNode.setCatId(catId); - childNode.setGroupId(groupId); - if (!localized) { - childNode.setShortDescEn(shortDesc); - childNode.setLongDescEn(longDesc); - } else { - childNode.setShortDesc(shortDesc); - childNode.setLongDesc(longDesc); - } - - readMetaData(childNode, itemId, localized); - } - - } - } - if (resultObj.has("DatapointItems")) - - { - if (parent != null) { - logger.debug("Decode dp for : {}", parent.getShortDesc()); - } - - SiemensHvacMetadata childNode; - JsonArray dptItems = resultObj.getAsJsonArray("DatapointItems"); - - Map idMap = new Hashtable(); - - for (JsonElement child : dptItems) { - JsonObject dptItem = child.getAsJsonObject(); - - nbDpt++; - - int nodeId = -1; - int dpSubKey = -1; - boolean hasWriteAccess = false; - String address = ""; - - if (dptItem.has("Id")) { - nodeId = dptItem.get("Id").getAsInt(); - } - - SiemensHvacMetadataMenu menu = (SiemensHvacMetadataMenu) parent; - - if (menu.hasChild(nodeId)) { - childNode = menu.getChild(nodeId); - } else { - childNode = new SiemensHvacMetadataDataPoint(); - childNode.setId(nodeId); - childNode.setParent(parent); - - menu.addChild(childNode); - } - - if (dptItem.has("Address")) { - address = dptItem.get("Address").getAsString(); - } - if (dptItem.has("DpSubKey")) { - dpSubKey = dptItem.get("DpSubKey").getAsInt(); - } - if (dptItem.has("WriteAccess")) { - hasWriteAccess = dptItem.get("WriteAccess").getAsBoolean(); - } - - SiemensHvacMetadataDataPoint dptChild = (SiemensHvacMetadataDataPoint) childNode; - - dptChild.setId(nodeId); - dptChild.setAddress(address); - dptChild.setDptSubKey(dpSubKey); - dptChild.setWriteAccess(hasWriteAccess); - - idMap.put("" + nodeId, dptChild); - - if (dptItem.has("Text")) { - JsonObject descObj = dptItem.getAsJsonObject("Text"); - - int catId = -1; - int groupId = -1; - int subItemId = -1; - String longDesc = ""; - String shortDesc = ""; - - if (descObj.has("CatId")) { - catId = descObj.get("CatId").getAsInt(); - } - if (descObj.has("GroupId")) { - groupId = descObj.get("GroupId").getAsInt(); - } - if (descObj.has("Id")) { - subItemId = descObj.get("Id").getAsInt(); - } - if (descObj.has("Long")) { - longDesc = descObj.get("Long").getAsString(); - } - if (descObj.has("Short")) { - shortDesc = descObj.get("Short").getAsString(); - } - - childNode.setSubId(subItemId); - childNode.setCatId(catId); - childNode.setGroupId(groupId); - - if (!localized) { - childNode.setShortDescEn(shortDesc); - childNode.setLongDescEn(longDesc); - } else { - childNode.setShortDesc(shortDesc); - childNode.setLongDesc(longDesc); - } - } - - } - - String request2 = "main.app?section=popcard&idtype=4"; - if (id != -1) { - request2 = request2 + "&id=" + id; - } - - if (lcHvacConnector != null) { - lcHvacConnector.doRequest(request2, new SiemensHvacCallback() { - - @Override - public void execute(URI uri, int status, @Nullable Object response) { - if (response != null) { - String st = (String) response; - st = st.replace("\n", ""); - - Pattern pattern = Pattern - .compile("td class=\\\"dp_linenumber\\\".*?>(.*?)<\\/td>.+?(?=id)id=\"dp(.+?)\""); - Matcher matcher = pattern.matcher(st); - - while (matcher.find()) { - String id = matcher.group(2); - String dptId = matcher.group(1); - - if (id != null && dptId != null && !id.isEmpty() && !dptId.isEmpty()) { - if (idMap.containsKey(id)) { - SiemensHvacMetadataDataPoint child = idMap.get(id); - if (child != null) { - child.setDptId(dptId); - } - } - - } - } - } - } - }); - } - - } - } - - @Override - public @Nullable SiemensHvacMetadata getDptMap(@Nullable String key) { - if (key == null) { - return null; - } - - if (dptMap.containsKey("byMenu" + key)) { - return dptMap.get("byMenu" + key); - } - if (dptMap.containsKey("byName" + key)) { - return dptMap.get("byName" + key); - } - if (dptMap.containsKey("byDptId" + key)) { - return dptMap.get("byDptId" + key); - } - if (dptMap.containsKey("byId" + key)) { - return dptMap.get("byId" + key); - } - - return null; - } - - public void loadMetaDataFromCache() { - File file = null; - - try { - file = new File(JSON_DIR + File.separator + "siemens.json"); - - if (!file.exists()) { - return; - } - - byte[] bytes = Files.readAllBytes(file.toPath()); - String js = new String(bytes, StandardCharsets.UTF_8); - - if (hvacConnector != null) { - root = hvacConnector.getGsonWithAdapter().fromJson(js, SiemensHvacMetadataMenu.class); - } - } catch (IOException ioe) { - logger.warn("Couldn't read Siemens MetaData information from file '{}'.", file.getAbsolutePath()); - - } - } - - public void saveMetaDataToCache() { - File file = null; - - try { - file = new File(JSON_DIR + File.separator + "siemens.json"); - - if (!file.exists()) { - file.getParentFile().mkdirs(); - file.createNewFile(); - } - - try (FileOutputStream os = new FileOutputStream(file)) { - if (hvacConnector != null) { - String js = hvacConnector.getGsonWithAdapter().toJson(root); - - byte[] bt = js.getBytes(); - os.write(bt); - os.flush(); - } - } - - } catch (IOException ioe) { - logger.warn("Couldn't write Siemens MetaData information to file '{}'.", file.getAbsolutePath()); - - } - } - - public void resolveDptDetails(SiemensHvacMetadataDataPoint dpt, ResolveCount rv) { - SiemensHvacConnector lcHvacConnector = hvacConnector; - if (dpt.getDetailsResolved()) { - return; - } - - String request = "api/menutree/datapoint_desc.json?Id=" + dpt.getId(); - if (lcHvacConnector != null) { - lcHvacConnector.doRequest(request, new SiemensHvacCallback() { - - @Override - public void execute(URI uri, int status, @Nullable Object response) { - if (response instanceof JsonObject) { - rv.decreaseResolveCount(); - logger.debug("siemensHvac:Initialization():ToResolve() {}", rv.getResolveCount()); - dpt.resolveDptDetails((JsonObject) response); - } else { - logger.debug("Invalid response from Siemens gateway, result is not a JsonObject"); - } - } - }); - } - } -} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java deleted file mode 100644 index d83c3a2e68328..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/Metadata/SiemensHvacMetadataUser.java +++ /dev/null @@ -1,64 +0,0 @@ -/** - * 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.siemenshvac.internal.metadata; - -import org.eclipse.jdt.annotation.NonNullByDefault; - -/** - * - * @author Laurent Arnal - Initial contribution - */ -@NonNullByDefault -public class SiemensHvacMetadataUser { - private String name; - private int id; - private String language; - private int languageId; - - public SiemensHvacMetadataUser() { - name = ""; - language = ""; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getLanguage() { - return language; - } - - public void setLanguage(String language) { - this.language = language; - } - - public int getLanguageId() { - return languageId; - } - - public void setLanguageId(int languageId) { - this.languageId = languageId; - } -} From a1a8537d97bf88a5864d31ac6193aa4a97bee120 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 15 Nov 2023 11:00:45 +0100 Subject: [PATCH 084/214] fix typo Signed-off-by: Laurent ARNAL --- bundles/org.openhab.binding.siemenshvac/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/bundles/org.openhab.binding.siemenshvac/README.md b/bundles/org.openhab.binding.siemenshvac/README.md index 37f336212f260..c0e007d09ec82 100644 --- a/bundles/org.openhab.binding.siemenshvac/README.md +++ b/bundles/org.openhab.binding.siemenshvac/README.md @@ -126,3 +126,4 @@ Number Thermostat_Temperature "Thermostat tempeature [%.1f °C] Number Thermostat_Setpoint "Thermostat setpoint [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:1724#1726_ConsConfort_TA_CC1" } Number Heat_Mode "Heat mode [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:1724#1725_Regime_CC1" } ``` + From 01ccf99529fc89678b7be9b8b8091ad7f26aeea7 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 15 Nov 2023 11:45:22 +0100 Subject: [PATCH 085/214] fix Diagram path Signed-off-by: Laurent ARNAL --- bundles/org.openhab.binding.siemenshvac/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/README.md b/bundles/org.openhab.binding.siemenshvac/README.md index c0e007d09ec82..3569bec157cc7 100644 --- a/bundles/org.openhab.binding.siemenshvac/README.md +++ b/bundles/org.openhab.binding.siemenshvac/README.md @@ -3,7 +3,7 @@ This binding is to support Siemens Hvac controller ecosystem, and the Web Gateway interface OZW672. A typical system is composed of: -![Diagram](Doc/Diagram.png) +![Diagram](doc/Diagram.png) There's a lot of different HVAC controllers depending on model in lot of different PAC constructors. Siemens RVS41.813/327 inside a Atlantic Hybrid Duo was used for the developpement, and is fully supported and tested. From 49aac2ec33d1f7b9f4258e111e4fdc2930bb603b Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 15 Nov 2023 14:46:23 +0100 Subject: [PATCH 086/214] replace loop by Normalizer to sanetize UID Signed-off-by: Laurent ARNAL --- .../siemenshvac/internal/type/UidUtils.java | 78 ++++--------------- 1 file changed, 15 insertions(+), 63 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index 51d67c6bde1a9..70fdb82c803a0 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.siemenshvac.internal.type; +import java.text.Normalizer; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint; @@ -39,72 +41,22 @@ public class UidUtils { * @return the label without invalid character */ public static String sanetizeId(String label) { - StringBuffer buffer = new StringBuffer(); - - for (int i = 0; i < label.length(); i++) { - char c = label.charAt(i); - if (c >= 232 && c <= 234) { - c = 'e'; - } else if (c >= 200 && c <= 202) { - c = 'E'; - } - - else if (c >= 236 && c <= 239) { - c = 'i'; - } else if (c >= 204 && c <= 207) { - c = 'I'; - } - - else if (c >= 242 && c <= 246) { - c = 'o'; - } else if (c >= 249 && c <= 252) { - c = 'u'; - } else if (c >= 217 && c <= 220) { - c = 'U'; - } - - else if (c >= 224 && c <= 229) { - c = 'a'; - } else if (c == 192 && c <= 197) { - c = 'A'; - } - - else if (c == 199) { - c = 'c'; - } else if (c == 231) { - c = 'C'; - } - - else if (c == '_') { - c = '_'; - } else if (c == ' ') { - c = '_'; - } else if (c == '&') { - c = '_'; - } else if (c == '/') { - c = '_'; - } else if (c == '.') { - c = '_'; - } else if (c == '(') { - c = '_'; - } else if (c == ')') { - c = '_'; - } - - else if (c == 248) { - c = '_'; - } else if (c == '\'') { - c = '_'; - } + String result = label; - if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' || c == '-')) { - c = '_'; - } - - buffer.append(c); + if (!Normalizer.isNormalized(label, Normalizer.Form.NFKD)) { + result = Normalizer.normalize(label, Normalizer.Form.NFKD); + result = result.replaceAll("\\p{M}", ""); } - return buffer.toString(); + result = result.replace(' ', '_'); + result = result.replace('.', '_'); + result = result.replace('\'', '_'); + result = result.replace('(', '_'); + result = result.replace(')', '_'); + result = result.replace('&', '_'); + result = result.replace('/', '_'); + + return result; } /** From 7bb99ef2bd225e6cc91baf952e8b0dda5edb7ea6 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 15 Nov 2023 15:41:29 +0100 Subject: [PATCH 087/214] fix missing filter in UidUtils Signed-off-by: Laurent ARNAL --- .../org/openhab/binding/siemenshvac/internal/type/UidUtils.java | 1 + 1 file changed, 1 insertion(+) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index 70fdb82c803a0..b97fa28d8e9f6 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -55,6 +55,7 @@ public static String sanetizeId(String label) { result = result.replace(')', '_'); result = result.replace('&', '_'); result = result.replace('/', '_'); + result = result.replace('°', '_'); return result; } From 239e2d249e6e19f35a433230c2b1b89b1e29b305 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 16 Nov 2023 15:07:39 +0100 Subject: [PATCH 088/214] jlaur review : remove duplicate entry Signed-off-by: Laurent ARNAL --- bom/openhab-addons/pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index 3b99653a2c250..4d8cdb95a40ae 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -1571,11 +1571,6 @@ org.openhab.binding.siemenshvac ${project.version} - - org.openhab.addons.bundles - org.openhab.binding.siemenshvac - ${project.version} - org.openhab.addons.bundles org.openhab.binding.siemensrds From cd5c35f45d41a8461dfa5b8162d613bae6cb9891 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 16 Nov 2023 15:09:38 +0100 Subject: [PATCH 089/214] jlaur review : remove @Disabled annotation on test Signed-off-by: Laurent ARNAL --- .../openhab/binding/siemenshvac/internal/type/UidUtilsTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java b/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java index 05bd76b4b4643..6cac9e167c161 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java +++ b/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java @@ -15,14 +15,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; /** * @author Sönke Küper - Initial contribution */ @NonNullByDefault -@Disabled("These tests use the real website which may not always be available") public class UidUtilsTest { @Test From ebf1bfff9b7fb265cd357118886ff73cc05bc6be Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 16 Nov 2023 15:17:54 +0100 Subject: [PATCH 090/214] remove extra public keywords Signed-off-by: Laurent ARNAL --- .../internal/network/SiemensHvacConnector.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java index c99c0d554fcc0..094707eeb879a 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java @@ -36,21 +36,22 @@ public interface SiemensHvacConnector { @Nullable JsonObject doRequest(String req, @Nullable SiemensHvacCallback callback); - public void waitAllPendingRequest(); + void waitAllPendingRequest(); - public void waitNoNewRequest(); + void waitNoNewRequest(); - public void onComplete(Request request); + void onComplete(Request request); - public void onError(Request request, SiemensHvacCallback cb) throws Exception; + void onError(Request request, SiemensHvacCallback cb) throws Exception; - public void setSiemensHvacBridgeBaseThingHandler(SiemensHvacBridgeBaseThingHandler hvacBridgeBaseThingHandler); + void setSiemensHvacBridgeBaseThingHandler(SiemensHvacBridgeBaseThingHandler hvacBridgeBaseThingHandler); - public @Nullable SiemensHvacBridgeConfig getBridgeConfiguration(); + @Nullable + SiemensHvacBridgeConfig getBridgeConfiguration(); void resetSessionId(boolean web); - public void displayRequestStats(); + void displayRequestStats(); Gson getGson(); From ae336f6a5b1842766a6439cc2f12eef95ba67d26 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 16 Nov 2023 15:46:21 +0100 Subject: [PATCH 091/214] review clinique : commit some missing fixes Signed-off-by: Laurent ARNAL --- .../siemenshvac/internal/handler/SiemensHvacHandlerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index d94874bd3a856..2098b4e73d23f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -288,7 +288,7 @@ private void writeDp(String dp, Type dpVal, String type) { StringType bdc = (StringType) dpVal; valUpdate = bdc.toString(); - if (("Enumeration").equals(type)) { + if ("Enumeration".equals(type)) { String[] valuesUpdateDp = valUpdate.split(":"); valUpdateEnum = valuesUpdateDp[0]; From c715c7bec4ea2b1c551d9a68d74db4c8f9258a63 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 16 Nov 2023 20:38:59 +0100 Subject: [PATCH 092/214] fix typo in pom.xml Signed-off-by: Laurent ARNAL --- bundles/org.openhab.binding.siemenshvac/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/pom.xml b/bundles/org.openhab.binding.siemenshvac/pom.xml index 5103b0a2d7431..b892be90efa75 100644 --- a/bundles/org.openhab.binding.siemenshvac/pom.xml +++ b/bundles/org.openhab.binding.siemenshvac/pom.xml @@ -12,6 +12,6 @@ org.openhab.binding.siemenshvac - openHAB Add-ons::Bundles::SiemensHvac Binding + openHAB Add-ons :: Bundles :: SiemensHvac Binding From 3fdd6e66c695e738c3f7a8ed080492280fbd75cf Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 16 Nov 2023 20:39:23 +0100 Subject: [PATCH 093/214] remove empty line on SiemensHvacHandlerImpl.java Signed-off-by: Laurent ARNAL --- .../siemenshvac/internal/handler/SiemensHvacHandlerImpl.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index 2098b4e73d23f..93a3fb92f42d9 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -205,7 +205,6 @@ public void decodeReadDp(@Nullable JsonObject response, @Nullable String uid, @N } else { updateState(updateKey, new StringType(value.getAsString())); } - } } @@ -306,7 +305,6 @@ private void writeDp(String dp, Type dpVal, String type) { logger.trace("Write request response: {} ", response); } - } catch (Exception e) { logger.debug("siemensHvac:ReadDp:Error during dp reading: {}: {}", dp, e.getLocalizedMessage()); // Reset sessionId so we redone _auth on error @@ -357,7 +355,6 @@ private Command applyState(ChannelType tp, Command command) { if (doMods) { result = new DecimalType("" + v1); } - } } return result; From 79d6b9d945345791b696d319007f1c26c37621c4 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 16 Nov 2023 20:40:26 +0100 Subject: [PATCH 094/214] jlaur review : fix two null warning on SiemensHvacMetadataRegistryImpl.java Signed-off-by: Laurent ARNAL --- .../metadata/SiemensHvacMetadataRegistryImpl.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java index 06204097a1ea0..3b9c466efb7d2 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -1121,6 +1121,7 @@ public void execute(URI uri, int status, @Nullable Object response) { } public void loadMetaDataFromCache() { + SiemensHvacConnector lcHvacConnector = hvacConnector; File file = null; try { @@ -1133,8 +1134,8 @@ public void loadMetaDataFromCache() { byte[] bytes = Files.readAllBytes(file.toPath()); String js = new String(bytes, StandardCharsets.UTF_8); - if (hvacConnector != null) { - root = hvacConnector.getGsonWithAdapter().fromJson(js, SiemensHvacMetadataMenu.class); + if (lcHvacConnector != null) { + root = lcHvacConnector.getGsonWithAdapter().fromJson(js, SiemensHvacMetadataMenu.class); } } catch (IOException ioe) { logger.warn("Couldn't read Siemens MetaData information from file '{}'.", file.getAbsolutePath()); @@ -1143,6 +1144,7 @@ public void loadMetaDataFromCache() { } public void saveMetaDataToCache() { + SiemensHvacConnector lcHvacConnector = hvacConnector; File file = null; try { @@ -1154,8 +1156,8 @@ public void saveMetaDataToCache() { } try (FileOutputStream os = new FileOutputStream(file)) { - if (hvacConnector != null) { - String js = hvacConnector.getGsonWithAdapter().toJson(root); + if (lcHvacConnector != null) { + String js = lcHvacConnector.getGsonWithAdapter().toJson(root); byte[] bt = js.getBytes(); os.write(bt); From c036cb46e6aba3764e82631d6a3172cd2fcad448 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Fri, 17 Nov 2023 09:52:03 +0100 Subject: [PATCH 095/214] fix addon.xml headers Signed-off-by: Laurent ARNAL --- .../src/main/resources/OH-INF/addon/addon.xml | 3 ++- .../src/main/resources/OH-INF/thing/ozw672.xml | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml index cafd8e7ee15b8..90ac868b4fbc6 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml @@ -1,6 +1,7 @@ + xmlns:addon="https://openhab.org/schemas/addon/v1.0.0" + xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd"> binding SiemensHvac Binding diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml index 770ac7781d30f..c736e99d2e426 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml @@ -95,5 +95,4 @@ -
From 09066c23ebdf2aad993438655d648f2521e4ad78 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Fri, 17 Nov 2023 10:27:32 +0100 Subject: [PATCH 096/214] fix another nullable warning on MetdataMenu Signed-off-by: Laurent ARNAL --- .../internal/metadata/SiemensHvacMetadataMenu.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataMenu.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataMenu.java index 5cd5fa212f6d8..82df1e9590b95 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataMenu.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataMenu.java @@ -15,6 +15,8 @@ import java.util.HashMap; import java.util.LinkedHashMap; +import javax.validation.constraints.NotNull; + import org.eclipse.jdt.annotation.NonNullByDefault; /** @@ -41,7 +43,7 @@ public boolean hasChild(int Id) { return this.childList.containsKey(Id); } - public SiemensHvacMetadata getChild(int Id) { + public @NotNull SiemensHvacMetadata getChild(int Id) { return this.childList.get(Id); } } From f2b81df01dff88580e7843836c6ea7b198f2bd24 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 5 Dec 2023 12:17:39 +0100 Subject: [PATCH 097/214] fix account handling when we are using end user account to connect to the gateway Bug from Vasek: https://github.com/openhab/openhab-addons/pull/14263#issuecomment-1837271964 Signed-off-by: Laurent ARNAL --- .../SiemensHvacMetadataRegistryImpl.java | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java index 3b9c466efb7d2..7a829384c7129 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -304,12 +304,12 @@ public void readMeta() { logger.info("siemensHvac:Initialization():BeginReadMenu"); root = new SiemensHvacMetadataMenu(); - changeLanguage(1); + changeLanguage(user, 1); readMetaData(root, -1, false); lcHvacConnector.waitNoNewRequest(); lcHvacConnector.waitAllPendingRequest(); - changeLanguage(user.getLanguageId()); + changeLanguage(user, user.getLanguageId()); readMetaData(root, -1, true); lcHvacConnector.waitNoNewRequest(); lcHvacConnector.waitAllPendingRequest(); @@ -695,15 +695,21 @@ public void readUserInfo() { Pattern pattern4 = Pattern.compile("userid=(.+?)"); Matcher matcher4 = pattern4.matcher(userEdit); + SiemensHvacMetadataUser user = new SiemensHvacMetadataUser(); + user.setName(userName); + if (matcher4.find()) { userId = matcher4.group(1); + user.setId(Integer.parseInt(userId)); + } else { + userId = null; + user.setId(-1); } - SiemensHvacMetadataUser user = new SiemensHvacMetadataUser(); - user.setName(userName); - user.setId(Integer.parseInt(userId)); - - request = "main.app?section=settings&subsection=user&action=modify&userid=" + userId; + request = "main.app?section=settings&subsection=user&action=modify"; + if (userId != null) { + request = request + "&userid=" + userId; + } response = lcHvacConnector.doBasicRequest(request); Pattern pattern5 = Pattern.compile( @@ -744,11 +750,14 @@ public void readUserInfo() { } } - public void changeLanguage(int lang) { + public void changeLanguage(SiemensHvacMetadataUser user, int lang) { try { SiemensHvacConnector lcHvacConnector = hvacConnector; - String request = "main.app?section=settings&subsection=user&action=modify&userid=1&language=" + lang - + "&submit=OK"; + String request = "main.app?section=settings&subsection=user&action=modify"; + if (user.getId() != -1) { + request = request + "&userid=" + user.getId(); + } + request = request + "&language=" + lang + "&submit=OK"; if (lcHvacConnector != null) { lcHvacConnector.doBasicRequest(request); lcHvacConnector.resetSessionId(false); From 10ffbcbd1b99601ea89c72c18457cfebe8ac2cc8 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 5 Dec 2023 12:21:22 +0100 Subject: [PATCH 098/214] fix possible invalid '+' characters in id Signed-off-by: Laurent ARNAL --- .../org/openhab/binding/siemenshvac/internal/type/UidUtils.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index b97fa28d8e9f6..78d4f97b59cc8 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -56,6 +56,7 @@ public static String sanetizeId(String label) { result = result.replace('&', '_'); result = result.replace('/', '_'); result = result.replace('°', '_'); + result = result.replace('+', '_'); return result; } @@ -127,6 +128,7 @@ private static String normalizeDescriptor(String descriptor) { result = result.replace('\'', '-'); result = result.replace('/', '-'); result = result.replace(' ', '-'); + result = result.replace("+", "-"); result = result.replace("--", "-"); result = result.replace("standard-tsp-hc", "time-switch-program-standard"); From efd2a06ec8b975d08d77979a4cd6e16c86783def Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 5 Dec 2023 14:45:47 +0100 Subject: [PATCH 099/214] add some verification mechanism on baseUrl format Signed-off-by: Laurent ARNAL --- .../SiemensHvacBridgeBaseThingHandler.java | 20 ++++++++++++++++++- .../main/resources/OH-INF/thing/ozw672.xml | 7 ++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java index d77457c85d470..9314290a6a7de 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -59,8 +59,26 @@ public void handleCommand(ChannelUID channelUID, Command command) { @Override public void initialize() { - config = getConfigAs(SiemensHvacBridgeConfig.class); + SiemensHvacBridgeConfig lcConfig = getConfigAs(SiemensHvacBridgeConfig.class); + String baseUrl = null; + if (lcConfig.baseUrl != null) { + baseUrl = lcConfig.baseUrl; + } + + if (baseUrl == null) { + throw new RuntimeException("baseUrl is mandatory on configuration !"); + } + + if (!baseUrl.startsWith("http://") && !baseUrl.startsWith("https://")) { + baseUrl = "http://" + baseUrl; + } + + if (!baseUrl.endsWith("/")) { + baseUrl = baseUrl + "/"; + } + + config = lcConfig; metaDataRegistry.readMeta(); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml index c736e99d2e426..f7ec75657454f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml @@ -10,10 +10,11 @@ This is a OZW672 IP interface - + - The URL of the Siemens Hvac IP gateway - network-address + url + The URL of the Siemens Hvac IP gateway. Must be in format http://hostname/ or https://hostname/. Don't forget the trailing '/' + true User name of the Siemens Hvac gateway From 4d47e6c50ebf97eaad185ab90de0e58f29328e48 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 5 Dec 2023 17:55:24 +0100 Subject: [PATCH 100/214] mvn spotless:apply Signed-off-by: Laurent ARNAL --- .../src/main/resources/OH-INF/thing/ozw672.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml index f7ec75657454f..43c109b1cbb6b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml @@ -13,7 +13,8 @@ url - The URL of the Siemens Hvac IP gateway. Must be in format http://hostname/ or https://hostname/. Don't forget the trailing '/' + The URL of the Siemens Hvac IP gateway. Must be in format http://hostname/ or https://hostname/. Don't + forget the trailing '/' true From 5f174ee67d579e58f616348e24f9950ac536b4ac Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 13 Dec 2023 18:47:53 +0100 Subject: [PATCH 101/214] fix error on handling session expiration Signed-off-by: Laurent ARNAL --- .../internal/network/SiemensHvacConnectorImpl.java | 10 ++++++++++ .../internal/network/SiemensHvacRequestListener.java | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 24ac4e208ea2d..dcb32238ce7b9 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -153,6 +153,14 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacCallback cb) return; } + if (sessionIdHttp == null) { + doAuth(true); + } + + if (sessionId == null) { + doAuth(false); + } + try { final Request retryRequest = httpClient.newRequest(request.getURI()); request.method(HttpMethod.GET); @@ -455,10 +463,12 @@ public void waitNoNewRequest() { logger.debug("WaitNoNewRequest:end WaitAllStartingRequest"); } + @Override public Gson getGson() { return gson; } + @Override public Gson getGsonWithAdapter() { return gsonWithAdapter; } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java index e9371beecbf25..1b0600966b8ac 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java @@ -195,6 +195,11 @@ public void onComplete(@Nullable Result result) { errorMsg = error.get("Txt").getAsString(); } + if (errorMsg.indexOf("session") >= 0) { + hvacConnector.resetSessionId(false); + hvacConnector.resetSessionId(true); + } + if (resultVal) { hvacConnector.onComplete(result.getRequest()); callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), From 62308d7c85c1e862e7f43c0d76d540e29f677e88 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 21 Dec 2023 14:53:37 +0100 Subject: [PATCH 102/214] Complete review of binding code : Better handling of bridge and thing status to follow error like network communication error, config error, etc... Enhance reliability of SiemensHvacConnectorImpl by modifying that handling of asynchronous request Verify network errors so bridge or thing change to OffLine if the bridge is not accessible from network, or if the thing is not accesible from the bridge Review initialization phase to prevent thing begins to pool before bridge is online. And many more ... Signed-off-by: Laurent ARNAL --- .../SiemensHvacDeviceDiscoveryService.java | 7 +- .../SiemensHvacBridgeBaseThingHandler.java | 51 +++- .../handler/SiemensHvacHandlerImpl.java | 80 +++++- .../SiemensHvacOZW672BridgeThingHandler.java | 2 - .../metadata/SiemensHvacMetadataRegistry.java | 5 +- .../SiemensHvacMetadataRegistryImpl.java | 16 +- .../network/SiemensHvacConnector.java | 13 +- .../network/SiemensHvacConnectorImpl.java | 172 ++++++++----- .../network/SiemensHvacRequestHandler.java | 85 +++++++ .../network/SiemensHvacRequestListener.java | 240 ------------------ 10 files changed, 354 insertions(+), 317 deletions(-) create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestHandler.java delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java index 6fcb748239d53..01e183bc62395 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java @@ -23,6 +23,7 @@ import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeBaseThingHandler; import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDevice; import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataRegistry; +import org.openhab.binding.siemenshvac.internal.type.SiemensHvacException; import org.openhab.binding.siemenshvac.internal.type.UidUtils; import org.openhab.core.config.discovery.AbstractDiscoveryService; import org.openhab.core.config.discovery.DiscoveryResult; @@ -91,7 +92,11 @@ public void startScan() { logger.debug("call startScan()"); if (lcMetadataRegistry != null) { - lcMetadataRegistry.readMeta(); + try { + lcMetadataRegistry.readMeta(); + } catch (SiemensHvacException ex) { + + } ArrayList devices = lcMetadataRegistry.getDevices(); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java index 9314290a6a7de..e5a22566bd9cb 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -12,19 +12,25 @@ */ package org.openhab.binding.siemenshvac.internal.handler; +import java.util.concurrent.TimeUnit; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.discovery.SiemensHvacDeviceDiscoveryService; import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataRegistry; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; +import org.openhab.binding.siemenshvac.internal.type.SiemensHvacException; import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.net.NetworkAddressService; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.ThingStatusInfo; import org.openhab.core.thing.binding.BaseBridgeHandler; import org.openhab.core.types.Command; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * The {@link SiemensHvacBridgeBaseThingHandler} is responsible for handling commands, which are @@ -35,6 +41,7 @@ @NonNullByDefault public abstract class SiemensHvacBridgeBaseThingHandler extends BaseBridgeHandler { + private final Logger logger = LoggerFactory.getLogger(SiemensHvacBridgeBaseThingHandler.class); private @Nullable SiemensHvacDeviceDiscoveryService discoveryService; private final @Nullable HttpClientFactory httpClientFactory; private final SiemensHvacMetadataRegistry metaDataRegistry; @@ -57,8 +64,16 @@ public SiemensHvacBridgeBaseThingHandler(Bridge bridge, @Nullable NetworkAddress public void handleCommand(ChannelUID channelUID, Command command) { } + @Override + public void dispose() { + metaDataRegistry.invalidate(); + } + @Override public void initialize() { + updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NOT_YET_READY, + "Waiting bridge initialization, reading metadata in background"); + SiemensHvacBridgeConfig lcConfig = getConfigAs(SiemensHvacBridgeConfig.class); String baseUrl = null; @@ -79,13 +94,47 @@ public void initialize() { } config = lcConfig; - metaDataRegistry.readMeta(); + + // Will read metadata in background to not block initialize for a long period ! + scheduler.schedule(this::pollingCode, 1, TimeUnit.SECONDS); + } + + public static String getStackTrace(final Throwable throwable) { + StringBuffer sb = new StringBuffer(); + + Throwable current = throwable; + while (current != null) { + sb.append(current.getLocalizedMessage()); + sb.append(",\r\n"); + if (throwable.getCause() != throwable) { + current = current.getCause(); + } else { + current = null; + } + } + return sb.toString(); + } + + private void pollingCode() { + try { + metaDataRegistry.readMeta(); + updateStatus(ThingStatus.ONLINE); + } catch (SiemensHvacException ex) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + String.format("Error occurs during gateway initialization: %s", getStackTrace(ex))); + } + } public @Nullable SiemensHvacBridgeConfig getBridgeConfiguration() { return config; } + @Override + public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) { + logger.info("bridge status changed : " + bridgeStatusInfo); + } + @Override public void updateStatus(ThingStatus status) { super.updateStatus(status); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index 93a3fb92f42d9..9709067569a98 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -27,15 +27,19 @@ import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataRegistry; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacCallback; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; +import org.openhab.binding.siemenshvac.internal.network.SiemensHvacRequestListener.ErrorSource; import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.StringType; +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.ThingStatusInfo; import org.openhab.core.thing.binding.BaseThingHandler; import org.openhab.core.thing.binding.ThingHandlerCallback; import org.openhab.core.thing.type.ChannelType; @@ -84,9 +88,24 @@ public SiemensHvacHandlerImpl(Thing thing, @Nullable SiemensHvacConnector hvacCo this.timeZoneProvider = timeZoneProvider; } + @Override + protected void updateStatus(ThingStatus status) { + super.updateStatus(status); + } + + @Override + public void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) { + super.updateStatus(status, statusDetail, description); + } + + @Override + public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) { + logger.info("bridge status changed : " + bridgeStatusInfo); + } + @Override public void initialize() { - updateStatus(ThingStatus.UNKNOWN); + updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NOT_YET_READY); pollingJob = scheduler.scheduleWithFixedDelay(this::pollingCode, 0, 5, TimeUnit.SECONDS); } @@ -99,20 +118,71 @@ public void dispose() { } private void pollingCode() { + Bridge lcBridge = getBridge(); + + if (lcBridge == null) { + return; + } + + if (lcBridge.getStatus() == ThingStatus.OFFLINE) { + logger.debug("Bridge is offline, change thing status to offline!"); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "Bridge is offline"); + return; + } + + if (lcBridge.getStatus() != ThingStatus.ONLINE) { + logger.debug("Bridge is not ready, don't enter pooling for now!"); + return; + } + + if (getThing().getStatus() != ThingStatus.OFFLINE) { + updateStatus(ThingStatus.ONLINE); + } + long start = System.currentTimeMillis(); var chList = this.getThing().getChannels(); - for (Channel channel : chList) { - readChannel(channel); - } - updateStatus(ThingStatus.ONLINE); SiemensHvacConnector lcHvacConnector = hvacConnector; if (lcHvacConnector != null) { + int previousRequestCount = lcHvacConnector.getRequestCount(); + int previousErrorCount = lcHvacConnector.getErrorCount(); + + for (Channel channel : chList) { + readChannel(channel); + } + logger.debug("WaitAllPendingRequest:Start waiting()"); lcHvacConnector.waitAllPendingRequest(); long end = System.currentTimeMillis(); logger.debug("WaitAllPendingRequest:All request done(): {}", (end - start) / 1000.00); + + int newRequestCount = lcHvacConnector.getRequestCount(); + int newErrorCount = lcHvacConnector.getErrorCount(); + + int requestCount = newRequestCount - previousRequestCount; + int errorCount = newErrorCount - previousErrorCount; + + double errorRate = (double) errorCount / requestCount * 100.00; + + if (errorRate > 20) { + SiemensHvacBridgeBaseThingHandler bridgeHandler = (SiemensHvacBridgeBaseThingHandler) lcBridge + .getHandler(); + + if (lcHvacConnector.getErrorSource() == ErrorSource.ErrorBridge) { + if (bridgeHandler != null) { + bridgeHandler.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + String.format("Communication ErrorRate to gateway is too high : %f", errorRate)); + } + } else if (lcHvacConnector.getErrorSource() == ErrorSource.ErrorThings) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + String.format("Communication ErrorRate to thing is too high : %f", errorRate)); + } + } else { + updateStatus(ThingStatus.ONLINE); + } + } + } private void readChannel(Channel channel) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java index fa00fd9d91c07..b1e50916d0b35 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java @@ -22,7 +22,6 @@ import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.net.NetworkAddressService; import org.openhab.core.thing.Bridge; -import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.binding.ThingHandlerService; import org.osgi.service.component.annotations.Activate; import org.slf4j.Logger; @@ -50,7 +49,6 @@ public SiemensHvacOZW672BridgeThingHandler(Bridge bridge, @Nullable NetworkAddre public void initialize() { logger.debug("Initialize() bridge"); super.initialize(); - updateStatus(ThingStatus.ONLINE); } @Override diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistry.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistry.java index 3fb50cabc054a..ee6ed7c18926b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistry.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistry.java @@ -18,6 +18,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelTypeProvider; +import org.openhab.binding.siemenshvac.internal.type.SiemensHvacException; /** * @@ -31,7 +32,7 @@ public interface SiemensHvacMetadataRegistry { */ void initialize(); - void readMeta(); + void readMeta() throws SiemensHvacException; @Nullable SiemensHvacMetadataMenu getRoot(); @@ -47,4 +48,6 @@ public interface SiemensHvacMetadataRegistry { @Nullable SiemensHvacConnector getSiemensHvacConnector(); + + void invalidate(); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java index 7a829384c7129..437a8c076c33e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -36,6 +36,7 @@ import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelGroupTypeProvider; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelTypeProvider; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacConfigDescriptionProvider; +import org.openhab.binding.siemenshvac.internal.type.SiemensHvacException; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacThingTypeProvider; import org.openhab.binding.siemenshvac.internal.type.UidUtils; import org.openhab.core.OpenHAB; @@ -259,7 +260,7 @@ public int unresolveCount() { } @Override - public void readMeta() { + public void readMeta() throws SiemensHvacException { ArrayList lcDevices = devices; SiemensHvacConnector lcHvacConnector = hvacConnector; @@ -643,7 +644,7 @@ public static String getStatePattern(SiemensHvacMetadataDataPoint dpt) { return ""; } - public void readUserInfo() { + public void readUserInfo() throws SiemensHvacException { try { SiemensHvacConnector lcHvacConnector = hvacConnector; String request = "main.app?section=settings&subsection=user"; @@ -745,7 +746,8 @@ public void readUserInfo() { } } } catch (Exception e) { - logger.error("siemensHvac:ResolveDpt:Error during dp reading: {}", e.getLocalizedMessage()); + logger.error("siemensHvac:ResolveDpt:Error during reading user info: {}", e.getLocalizedMessage()); + throw new SiemensHvacException("Error durring reading user info", e); // Reset sessionId so we redone _auth on error } } @@ -1203,4 +1205,12 @@ public void execute(URI uri, int status, @Nullable Object response) { }); } } + + @Override + public void invalidate() { + root = null; + if (hvacConnector != null) { + hvacConnector.invalidate(); + } + } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java index 094707eeb879a..13db9d1a30e3b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java @@ -40,9 +40,10 @@ public interface SiemensHvacConnector { void waitNoNewRequest(); - void onComplete(Request request); + void onComplete(Request request, SiemensHvacRequestHandler reqListener) throws Exception; - void onError(Request request, SiemensHvacCallback cb) throws Exception; + void onError(Request request, SiemensHvacRequestHandler reqListener, + SiemensHvacRequestListener.ErrorSource errorSource) throws Exception; void setSiemensHvacBridgeBaseThingHandler(SiemensHvacBridgeBaseThingHandler hvacBridgeBaseThingHandler); @@ -56,4 +57,12 @@ public interface SiemensHvacConnector { Gson getGson(); Gson getGsonWithAdapter(); + + int getRequestCount(); + + int getErrorCount(); + + SiemensHvacRequestListener.ErrorSource getErrorSource(); + + void invalidate(); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index dcb32238ce7b9..253a3691fa63a 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -15,14 +15,13 @@ import java.net.CookieStore; import java.net.HttpCookie; import java.net.URI; +import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -62,6 +61,8 @@ public class SiemensHvacConnectorImpl implements SiemensHvacConnector { private final Logger logger = LoggerFactory.getLogger(SiemensHvacConnectorImpl.class); + private Map currentHandlerRegistry = new HashMap(); + private Map handlerInErrorRegistry = new HashMap(); private final Gson gson; private final Gson gsonWithAdapter; @@ -73,12 +74,12 @@ public class SiemensHvacConnectorImpl implements SiemensHvacConnector { protected HttpClient httpClient; - private static int startedRequest = 0; - private static int completedRequest = 0; - private Lock lockObj = new ReentrantLock(); - private Map updateCommand; + private int requestCount = 0; + private int errorCount = 0; + private SiemensHvacRequestListener.ErrorSource errorSource = SiemensHvacRequestListener.ErrorSource.ErrorBridge; + private @Nullable SiemensHvacBridgeBaseThingHandler hvacBridgeBaseThingHandler; @Activate @@ -129,30 +130,31 @@ public void unsetSiemensHvacBridgeBaseThingHandler(SiemensHvacBridgeBaseThingHan } @Override - public void onComplete(@Nullable Request request) { - lockObj.lock(); - try { - logger.debug("OnComplete"); - completedRequest++; - } finally { - lockObj.unlock(); - } + public void onComplete(@Nullable Request request, SiemensHvacRequestHandler reqHandler) throws Exception { + UnregisterRequestHandler(reqHandler); } @Override - public void onError(@Nullable Request request, @Nullable SiemensHvacCallback cb) throws Exception { - lockObj.lock(); - try { - logger.debug("OnError"); - completedRequest++; - } finally { - lockObj.unlock(); + public void onError(@Nullable Request request, @Nullable SiemensHvacRequestHandler reqHandler, + SiemensHvacRequestListener.ErrorSource errorSource) throws Exception { + if (reqHandler == null || request == null) { + throw new SiemensHvacException("internalError : onError call with reqHandler == null"); } - if (cb == null || request == null) { + // reqHandler.displayStats(); + + if (reqHandler.getRetryCount() >= 1) { + logger.info("unable to handle request, retryCount>5, cancel it"); + UnregisterRequestHandler(reqHandler); + RegisterHandlerError(reqHandler); + errorCount++; + this.errorSource = errorSource; return; } + // Wait one second before retrying the request to avoid flooding the gateway + Thread.sleep(1000); + if (sessionIdHttp == null) { doAuth(true); } @@ -164,9 +166,10 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacCallback cb) try { final Request retryRequest = httpClient.newRequest(request.getURI()); request.method(HttpMethod.GET); + reqHandler.incrementRetryCount(); if (retryRequest != null) { - executeRequest(retryRequest, cb); + executeRequest(retryRequest, reqHandler); } } catch (Exception ex) { logger.debug("Error during gateway request:", ex); @@ -174,43 +177,60 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacCallback cb) } } + private @Nullable ContentResponse executeRequest(final Request request) throws Exception { + return executeRequest(request, (SiemensHvacCallback) null); + } + private @Nullable ContentResponse executeRequest(final Request request, @Nullable SiemensHvacCallback callback) throws Exception { + + requestCount++; + + // For asynchronous request, we create a RequestHandler that will enable us to follow request state + SiemensHvacRequestHandler requestHandler = null; + if (callback != null) { + requestHandler = new SiemensHvacRequestHandler(callback, this); + currentHandlerRegistry.put(requestHandler, requestHandler); + } + + return executeRequest(request, requestHandler); + } + + private void UnregisterRequestHandler(SiemensHvacRequestHandler handler) throws SiemensHvacException { + if (!currentHandlerRegistry.containsKey(handler)) { + throw new SiemensHvacException("Internal error, try to unregister not registred handler: " + handler); + } + + currentHandlerRegistry.remove(handler); + } + + private void RegisterHandlerError(SiemensHvacRequestHandler handler) { + handlerInErrorRegistry.put(handler, handler); + } + + private @Nullable ContentResponse executeRequest(final Request request, + @Nullable SiemensHvacRequestHandler requestHandler) throws Exception { // Give a high timeout because we queue a lot of async request, // so enqueued them will take some times ... request.timeout(240, TimeUnit.SECONDS); ContentResponse response = null; - @Nullable - SiemensHvacRequestListener requestListener = null; - if (callback != null) { - requestListener = new SiemensHvacRequestListener(callback, this); - } - try { - if (requestListener != null) { - lockObj.lock(); - try { - startedRequest++; - logger.trace("StartedRequest: {}", startedRequest - completedRequest); - - } finally { - lockObj.unlock(); - } - + if (requestHandler != null) { + SiemensHvacRequestListener requestListener = new SiemensHvacRequestListener(requestHandler); request.send(requestListener); } else { response = request.send(); } } catch (InterruptedException | TimeoutException | ExecutionException e) { - throw new SiemensHvacException("siemensHvac:Exception by executing request: " + request.getQuery() + " ; " + throw new SiemensHvacException("siemensHvac:Exception by executing request: " + request.getURI() + " ; " + e.getLocalizedMessage()); } return response; } - private void initConfig() throws Exception { + private void initConfig() throws SiemensHvacException { SiemensHvacBridgeBaseThingHandler lcHvacBridgeBaseThingHandler = hvacBridgeBaseThingHandler; if (lcHvacBridgeBaseThingHandler != null) { @@ -226,7 +246,7 @@ private void initConfig() throws Exception { return config; } - private void doAuth(boolean http) throws Exception { + private void doAuth(boolean http) throws SiemensHvacException { logger.debug("siemensHvac:doAuth()"); initConfig(); @@ -255,7 +275,7 @@ private void doAuth(boolean http) throws Exception { logger.debug("siemensHvac:doAuth:connect()"); try { - ContentResponse response = executeRequest(request, null); + ContentResponse response = executeRequest(request); if (response != null) { int statusCode = response.getStatus(); @@ -299,7 +319,8 @@ private void doAuth(boolean http) throws Exception { logger.debug("siemensHvac:doAuth:decodeResponse:()"); if (sessionId == null) { - logger.debug("Session request auth was unsuccessful in _doAuth()"); + throw new SiemensHvacException( + "Session request auth was unsuccessful in _doAuth(), please verify login parameters"); } } @@ -312,6 +333,7 @@ private void doAuth(boolean http) throws Exception { } catch (Exception ex) { logger.debug("siemensHvac:doAuth:error() {}", ex.getLocalizedMessage()); + throw new SiemensHvacException("Error during authentification", ex); } } @@ -413,8 +435,8 @@ private void doAuth(boolean http) throws Exception { @Override public void displayRequestStats() { - logger.info("DisplayRequestStats : {} ({}/{})", (startedRequest - completedRequest), startedRequest, - completedRequest); + logger.info("DisplayRequestStats : currentRuning {}, error {}", currentHandlerRegistry.keySet().size(), + handlerInErrorRegistry.keySet().size()); } @Override @@ -423,20 +445,18 @@ public void waitAllPendingRequest() { try { Thread.sleep(1000); boolean allRequestDone = false; + int idx = 0; while (!allRequestDone) { - int idx = 0; - - allRequestDone = true; - while (idx < 5 && allRequestDone) { - logger.debug("WaitAllPendingRequest:waitAllRequestDone {} : {} ({}/{})", idx, - (startedRequest - completedRequest), startedRequest, completedRequest); - if (startedRequest != completedRequest) { - allRequestDone = false; - } - Thread.sleep(1000); - idx++; + allRequestDone = false; + int currentRequestCount = currentHandlerRegistry.keySet().size(); + logger.debug("WaitAllPendingRequest:waitAllRequestDone {} : {}", idx, currentRequestCount); + + if (currentRequestCount == 0) { + allRequestDone = true; } + Thread.sleep(1000); + idx++; } } catch (InterruptedException ex) { logger.debug("WaitAllPendingRequest:interrupted in WaitAllRequest"); @@ -449,12 +469,17 @@ public void waitAllPendingRequest() { public void waitNoNewRequest() { logger.debug("WaitNoNewRequest:start"); try { - int lastRequest = startedRequest; - Thread.sleep(5000); - while (lastRequest != startedRequest) { - logger.debug("waitNoNewRequest {}/{})", startedRequest, lastRequest); + int lastRequestCount = currentHandlerRegistry.keySet().size(); + boolean newRequest = true; + while (newRequest) { Thread.sleep(5000); - lastRequest = startedRequest; + int newRequestCount = currentHandlerRegistry.keySet().size(); + if (newRequestCount != lastRequestCount) { + logger.debug("waitNoNewRequest {}/{})", newRequestCount, lastRequestCount); + lastRequestCount = newRequestCount; + } else { + newRequest = false; + } } } catch (InterruptedException ex) { logger.debug("WaitAllPendingRequest:interrupted in WaitAllRequest"); @@ -487,4 +512,27 @@ public void resetSessionId(boolean web) { sessionId = null; } } + + @Override + public int getRequestCount() { + return requestCount; + } + + @Override + public int getErrorCount() { + return errorCount; + } + + @Override + public SiemensHvacRequestListener.ErrorSource getErrorSource() { + return errorSource; + } + + @Override + public void invalidate() { + sessionId = null; + sessionIdHttp = null; + currentHandlerRegistry.clear(); + handlerInErrorRegistry.clear(); + } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestHandler.java new file mode 100644 index 0000000000000..fbee353875004 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestHandler.java @@ -0,0 +1,85 @@ +/** + * 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.siemenshvac.internal.network; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.api.Response; +import org.eclipse.jetty.client.api.Result; + +/** + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault +public class SiemensHvacRequestHandler { + private SiemensHvacConnector hvacConnector; + + private int retryCount = 0; + + @Nullable + private Request request = null; + + @Nullable + private Response response = null; + + @Nullable + private Result result = null; + + /** + * Callback to execute on complete response + */ + private final SiemensHvacCallback callback; + + /** + * Constructor + * + * @param callback Callback which execute method has to be called. + */ + public SiemensHvacRequestHandler(SiemensHvacCallback callback, SiemensHvacConnector hvacConnector) { + this.callback = callback; + this.hvacConnector = hvacConnector; + } + + public SiemensHvacConnector getHvacConnector() { + return hvacConnector; + } + + public SiemensHvacCallback getCallback() { + return callback; + } + + public void incrementRetryCount() { + retryCount++; + } + + public int getRetryCount() { + return retryCount; + } + + @Nullable + public Response getResponse() { + return response; + } + + @Nullable + public Request getRequest() { + return request; + } + + @Nullable + public Result getResult() { + return result; + } + +} \ No newline at end of file diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java deleted file mode 100644 index 1b0600966b8ac..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java +++ /dev/null @@ -1,240 +0,0 @@ -/** - * 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.siemenshvac.internal.network; - -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.jetty.client.api.Request; -import org.eclipse.jetty.client.api.Request.BeginListener; -import org.eclipse.jetty.client.api.Request.QueuedListener; -import org.eclipse.jetty.client.api.Response; -import org.eclipse.jetty.client.api.Response.CompleteListener; -import org.eclipse.jetty.client.api.Response.ContentListener; -import org.eclipse.jetty.client.api.Response.FailureListener; -import org.eclipse.jetty.client.api.Response.SuccessListener; -import org.eclipse.jetty.client.api.Result; -import org.eclipse.jetty.client.util.BufferingResponseListener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.Gson; -import com.google.gson.JsonObject; -import com.google.gson.JsonSyntaxException; - -/** - * @author Laurent Arnal - Initial contribution - */ -@NonNullByDefault -public class SiemensHvacRequestListener extends BufferingResponseListener - implements SuccessListener, FailureListener, ContentListener, CompleteListener, QueuedListener, BeginListener { - - private static Lock lockObj = new ReentrantLock(); - private static int requestListenerCount = 0; - private static int onSuccessCount = 0; - private static int onBeginCount = 0; - private static int onQueuedCount = 0; - private static int onCompleteCount = 0; - private static int onFailureCount = 0; - - private final Logger logger = LoggerFactory.getLogger(SiemensHvacRequestListener.class); - - private SiemensHvacConnector hvacConnector; - - /** - * Callback to execute on complete response - */ - private final SiemensHvacCallback callback; - - public static int getQueuedCount() { - return onQueuedCount; - } - - public static int getRequestListenerCount() { - return requestListenerCount; - } - - public static int getCompleteCount() { - return onCompleteCount; - } - - public static int getCurrentRunning() { - return requestListenerCount - onCompleteCount; - } - - /** - * Constructor - * - * @param callback Callback which execute method has to be called. - */ - public SiemensHvacRequestListener(SiemensHvacCallback callback, SiemensHvacConnector hvacConnector) { - this.callback = callback; - this.hvacConnector = hvacConnector; - - lockObj.lock(); - try { - requestListenerCount++; - } finally { - lockObj.unlock(); - } - } - - @Override - public void onSuccess(@Nullable Response response) { - lockObj.lock(); - try { - onSuccessCount++; - } finally { - lockObj.unlock(); - } - if (response != null) { - logger.debug("{} response: {}", response.getRequest().getURI(), response.getStatus()); - } - } - - @Override - public void onFailure(@Nullable Response response, @Nullable Throwable failure) { - lockObj.lock(); - try { - onFailureCount++; - } finally { - lockObj.unlock(); - } - if (response != null && failure != null) { - logger.debug("response failed: {} {}", response.getRequest().getURI(), failure.getLocalizedMessage(), - failure); - } - } - - @Override - public void onQueued(@Nullable Request request) { - lockObj.lock(); - try { - onQueuedCount++; - } finally { - lockObj.unlock(); - } - } - - @Override - public void onBegin(@Nullable Request request) { - lockObj.lock(); - try { - onBeginCount++; - } finally { - lockObj.unlock(); - } - } - - public void displayStats() { - logger.info("DisplayStats :"); - logger.info(" requestListenerCount : {}", requestListenerCount); - logger.info(" onSuccessCount : {}", onSuccessCount); - logger.info(" onBeginCount : {}", onBeginCount); - logger.info(" onQueuedCount : {}", onQueuedCount); - logger.info(" onCompleteCount : {}", onCompleteCount); - logger.info(" onFailureCount : {}", onFailureCount); - } - - @Override - public void onComplete(@Nullable Result result) { - lockObj.lock(); - try { - onCompleteCount++; - } finally { - lockObj.unlock(); - } - - if (result == null) { - return; - } - - try { - String content = getContentAsString(); - logger.trace("response complete: {}", content); - - if (result.getResponse().getStatus() != 200) { - logger.debug("Error requesting gateway, non success code: {}", result.getResponse().getStatus()); - hvacConnector.onError(result.getRequest(), callback); - return; - } - - if (content != null) { - if (content.indexOf("") >= 0) { - hvacConnector.onComplete(result.getRequest()); - callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), content); - } else { - JsonObject resultObj = null; - try { - Gson gson = hvacConnector.getGson(); - resultObj = gson.fromJson(content, JsonObject.class); - } catch (JsonSyntaxException ex) { - logger.debug("error: {}", ex.toString()); - } - - if (resultObj != null && resultObj.has("Result")) { - JsonObject subResultObj = resultObj.getAsJsonObject("Result"); - - if (subResultObj.has("Success")) { - boolean resultVal = subResultObj.get("Success").getAsBoolean(); - JsonObject error = subResultObj.getAsJsonObject("Error"); - String errorMsg = ""; - if (error != null) { - errorMsg = error.get("Txt").getAsString(); - } - - if (errorMsg.indexOf("session") >= 0) { - hvacConnector.resetSessionId(false); - hvacConnector.resetSessionId(true); - } - - if (resultVal) { - hvacConnector.onComplete(result.getRequest()); - callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), - resultObj); - - return; - } else if (("datatype not supported").equals(errorMsg)) { - hvacConnector.onComplete(result.getRequest()); - callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), - resultObj); - return; - } else { - logger.debug("error: {}", subResultObj); - hvacConnector.onError(result.getRequest(), callback); - return; - } - } else { - logger.debug("error: invalid response from gateway, missing subResultObj:Success entry"); - hvacConnector.onError(result.getRequest(), callback); - return; - } - - } else { - logger.debug("error: invalid response from gateway, missing Result entry"); - hvacConnector.onError(result.getRequest(), callback); - return; - } - } - } else { - logger.debug("error: content == null"); - hvacConnector.onError(result.getRequest(), callback); - return; - } - } catch (Exception ex) { - logger.debug("An error occurred", ex); - } - } -} From b58e7fe0edb5a5aa30fb4d7cfb07e384c08127af Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 21 Dec 2023 14:58:08 +0100 Subject: [PATCH 103/214] spotless:apply Signed-off-by: Laurent ARNAL --- .../SiemensHvacBridgeBaseThingHandler.java | 1 - .../handler/SiemensHvacHandlerImpl.java | 1 - .../network/SiemensHvacRequestHandler.java | 3 +- .../network/SiemensHvacRequestListener.java | 277 ++++++++++++++++++ 4 files changed, 278 insertions(+), 4 deletions(-) create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java index e5a22566bd9cb..b19d3e3bff3cf 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -123,7 +123,6 @@ private void pollingCode() { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, String.format("Error occurs during gateway initialization: %s", getStackTrace(ex))); } - } public @Nullable SiemensHvacBridgeConfig getBridgeConfiguration() { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index 9709067569a98..55e986c148c97 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -182,7 +182,6 @@ private void pollingCode() { } } - } private void readChannel(Channel channel) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestHandler.java index fbee353875004..33aaf3393ffcd 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestHandler.java @@ -81,5 +81,4 @@ public Request getRequest() { public Result getResult() { return result; } - -} \ No newline at end of file +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java new file mode 100644 index 0000000000000..e788a07e7a02e --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java @@ -0,0 +1,277 @@ +/** + * 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.siemenshvac.internal.network; + +import java.io.EOFException; +import java.net.ConnectException; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.api.Request.BeginListener; +import org.eclipse.jetty.client.api.Request.QueuedListener; +import org.eclipse.jetty.client.api.Response; +import org.eclipse.jetty.client.api.Response.CompleteListener; +import org.eclipse.jetty.client.api.Response.ContentListener; +import org.eclipse.jetty.client.api.Response.FailureListener; +import org.eclipse.jetty.client.api.Response.SuccessListener; +import org.eclipse.jetty.client.api.Result; +import org.eclipse.jetty.client.util.BufferingResponseListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.gson.JsonSyntaxException; + +/** + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault +public class SiemensHvacRequestListener extends BufferingResponseListener + implements SuccessListener, FailureListener, ContentListener, CompleteListener, QueuedListener, BeginListener { + + public enum ErrorSource { + ErrorBridge, + ErrorThings + } + + private static Lock lockObj = new ReentrantLock(); + + private static int onSuccessCount = 0; + private static int onBeginCount = 0; + private static int onQueuedCount = 0; + private static int onCompleteCount = 0; + private static int onFailureCount = 0; + + private final Logger logger = LoggerFactory.getLogger(SiemensHvacRequestListener.class); + + private SiemensHvacRequestHandler requestHandler; + private SiemensHvacConnector hvacConnector; + + /** + * Callback to execute on complete response + */ + private final SiemensHvacCallback callback; + + public static int getQueuedCount() { + return onQueuedCount; + } + + public static int getStartedCount() { + return onBeginCount; + } + + public static int getCompleteCount() { + return onCompleteCount; + } + + public static int getFailureCount() { + return onFailureCount; + } + + public static int getSuccessCount() { + return onSuccessCount; + } + + /** + * Constructor + * + * @param callback Callback which execute method has to be called. + */ + public SiemensHvacRequestListener(SiemensHvacRequestHandler requestHandler) { + this.requestHandler = requestHandler; + this.hvacConnector = requestHandler.getHvacConnector(); + this.callback = requestHandler.getCallback(); + } + + @Override + public void onSuccess(@Nullable Response response) { + lockObj.lock(); + try { + onSuccessCount++; + } finally { + lockObj.unlock(); + } + + if (response != null) { + logger.debug("{} response: {}", response.getRequest().getURI(), response.getStatus()); + } + } + + @Override + public void onFailure(@Nullable Response response, @Nullable Throwable failure) { + lockObj.lock(); + try { + onFailureCount++; + } finally { + lockObj.unlock(); + } + + if (response != null && failure != null) { + Throwable cause = failure.getCause(); + if (cause == null) { + cause = failure; + } + + String msg = cause.getLocalizedMessage(); + + if (cause.getClass() == ConnectException.class) { + logger.debug("ConnectException"); + } else if (cause.getClass() == SocketException.class) { + logger.debug("SocketException"); + } else if (cause.getClass() == SocketTimeoutException.class) { + logger.debug("SocketTimeoutException"); + } else if (cause.getClass() == EOFException.class) { + logger.debug("EOFException"); + } else if (cause.getClass() == TimeoutException.class) { + logger.debug("TimeoutException"); + } else { + logger.debug("unknow"); + } + + logger.debug("response failed: {} {}", response.getRequest().getURI(), msg, failure); + } + } + + @Override + public void onQueued(@Nullable Request request) { + lockObj.lock(); + try { + onQueuedCount++; + } finally { + lockObj.unlock(); + } + } + + @Override + public void onBegin(@Nullable Request request) { + lockObj.lock(); + try { + onBeginCount++; + } finally { + lockObj.unlock(); + } + } + + public void displayStats() { + SiemensHvacRequestListener.displayStats(logger); + } + + public static void displayStats(Logger logger) { + logger.info("DisplayStats :"); + logger.info(" onSuccessCount : {}", onSuccessCount); + logger.info(" onBeginCount : {}", onBeginCount); + logger.info(" onQueuedCount : {}", onQueuedCount); + logger.info(" onCompleteCount : {}", onCompleteCount); + logger.info(" onFailureCount : {}", onFailureCount); + } + + @Override + public void onComplete(@Nullable Result result) { + lockObj.lock(); + try { + onCompleteCount++; + } finally { + lockObj.unlock(); + } + + if (result == null) { + return; + } + + try { + String content = getContentAsString(); + logger.trace("response complete: {}", content); + + if (result.getResponse().getStatus() != 200) { + logger.debug("Error requesting gateway, non success code: {}", result.getResponse().getStatus()); + hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorBridge); + return; + } + + if (content != null) { + if (content.indexOf("") >= 0) { + hvacConnector.onComplete(result.getRequest(), requestHandler); + callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), content); + } else { + JsonObject resultObj = null; + try { + Gson gson = hvacConnector.getGson(); + resultObj = gson.fromJson(content, JsonObject.class); + } catch (JsonSyntaxException ex) { + logger.debug("error: {}", ex.toString()); + } + + if (resultObj != null && resultObj.has("Result")) { + JsonObject subResultObj = resultObj.getAsJsonObject("Result"); + + if (subResultObj.has("Success")) { + boolean resultVal = subResultObj.get("Success").getAsBoolean(); + JsonObject error = subResultObj.getAsJsonObject("Error"); + String errorMsg = ""; + if (error != null) { + errorMsg = error.get("Txt").getAsString(); + } + + if (errorMsg.indexOf("session") >= 0) { + hvacConnector.resetSessionId(false); + hvacConnector.resetSessionId(true); + } + + if (resultVal) { + hvacConnector.onComplete(result.getRequest(), requestHandler); + callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), + resultObj); + + return; + } else if (("datatype not supported").equals(errorMsg)) { + hvacConnector.onComplete(result.getRequest(), requestHandler); + callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), + resultObj); + return; + } else if (("read failed").equals(errorMsg)) { + logger.debug("error: {}", subResultObj); + hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorThings); + } else { + logger.debug("error: {}", subResultObj); + hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorBridge); + return; + } + } else { + logger.debug("error: invalid response from gateway, missing subResultObj:Success entry"); + hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorBridge); + return; + } + + } else { + logger.debug("error: invalid response from gateway, missing Result entry"); + hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorBridge); + return; + } + } + } else { + logger.debug("error: content == null"); + hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorBridge); + return; + } + } catch (Exception ex) { + logger.debug("An error occurred", ex); + } + } +} From 46584ca59f7749cde9c1fcddef4604c7e64566cd Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 21 Dec 2023 15:08:58 +0100 Subject: [PATCH 104/214] fix error in build Signed-off-by: Laurent ARNAL --- .../internal/handler/SiemensHvacBridgeBaseThingHandler.java | 4 ++-- .../siemenshvac/internal/handler/SiemensHvacHandlerImpl.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java index b19d3e3bff3cf..40ad0758c5dc6 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -106,7 +106,7 @@ public static String getStackTrace(final Throwable throwable) { while (current != null) { sb.append(current.getLocalizedMessage()); sb.append(",\r\n"); - if (throwable.getCause() != throwable) { + if (!throwable.getCause().equals(throwable)) { current = current.getCause(); } else { current = null; @@ -131,7 +131,7 @@ private void pollingCode() { @Override public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) { - logger.info("bridge status changed : " + bridgeStatusInfo); + logger.info("bridge status changed : {} ", bridgeStatusInfo); } @Override diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index 55e986c148c97..f22ceef103790 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -100,7 +100,7 @@ public void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nu @Override public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) { - logger.info("bridge status changed : " + bridgeStatusInfo); + logger.info("bridge status changed : {} ", bridgeStatusInfo); } @Override From 5b6eda074cdbbeeaaffa5bbc576762c67ad69a23 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 21 Dec 2023 18:13:56 +0100 Subject: [PATCH 105/214] fix some checkstyles warnings Signed-off-by: Laurent ARNAL --- .../SiemensHvacDeviceDiscoveryService.java | 3 ++- .../SiemensHvacBridgeBaseThingHandler.java | 13 ++++++++++--- .../handler/SiemensHvacHandlerImpl.java | 2 ++ .../network/SiemensHvacConnectorImpl.java | 18 +++++++++--------- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java index 01e183bc62395..8e19efe0ec2bc 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java @@ -95,7 +95,8 @@ public void startScan() { try { lcMetadataRegistry.readMeta(); } catch (SiemensHvacException ex) { - + logger.debug("Exception occurred during execution: {}", ex.getMessage(), ex); + return; } ArrayList devices = lcMetadataRegistry.getDevices(); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java index 40ad0758c5dc6..10d6f8e7441e5 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -82,7 +82,8 @@ public void initialize() { } if (baseUrl == null) { - throw new RuntimeException("baseUrl is mandatory on configuration !"); + logger.debug("baseUrl is mandatory on configuration !"); + return; } if (!baseUrl.startsWith("http://") && !baseUrl.startsWith("https://")) { @@ -106,8 +107,14 @@ public static String getStackTrace(final Throwable throwable) { while (current != null) { sb.append(current.getLocalizedMessage()); sb.append(",\r\n"); - if (!throwable.getCause().equals(throwable)) { - current = current.getCause(); + + Throwable cause = throwable.getCause(); + if (cause != null) { + if (!cause.equals(throwable)) { + current = current.getCause(); + } else { + current = null; + } } else { current = null; } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index f22ceef103790..ff8ffe3eae434 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -181,6 +181,8 @@ private void pollingCode() { updateStatus(ThingStatus.ONLINE); } + lcHvacConnector.displayRequestStats(); + } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 253a3691fa63a..14071ea062de1 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -131,7 +131,7 @@ public void unsetSiemensHvacBridgeBaseThingHandler(SiemensHvacBridgeBaseThingHan @Override public void onComplete(@Nullable Request request, SiemensHvacRequestHandler reqHandler) throws Exception { - UnregisterRequestHandler(reqHandler); + unregisterRequestHandler(reqHandler); } @Override @@ -145,8 +145,8 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacRequestHandl if (reqHandler.getRetryCount() >= 1) { logger.info("unable to handle request, retryCount>5, cancel it"); - UnregisterRequestHandler(reqHandler); - RegisterHandlerError(reqHandler); + unregisterRequestHandler(reqHandler); + registerHandlerError(reqHandler); errorCount++; this.errorSource = errorSource; return; @@ -183,7 +183,6 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacRequestHandl private @Nullable ContentResponse executeRequest(final Request request, @Nullable SiemensHvacCallback callback) throws Exception { - requestCount++; // For asynchronous request, we create a RequestHandler that will enable us to follow request state @@ -196,7 +195,7 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacRequestHandl return executeRequest(request, requestHandler); } - private void UnregisterRequestHandler(SiemensHvacRequestHandler handler) throws SiemensHvacException { + private void unregisterRequestHandler(SiemensHvacRequestHandler handler) throws SiemensHvacException { if (!currentHandlerRegistry.containsKey(handler)) { throw new SiemensHvacException("Internal error, try to unregister not registred handler: " + handler); } @@ -204,7 +203,7 @@ private void UnregisterRequestHandler(SiemensHvacRequestHandler handler) throws currentHandlerRegistry.remove(handler); } - private void RegisterHandlerError(SiemensHvacRequestHandler handler) { + private void registerHandlerError(SiemensHvacRequestHandler handler) { handlerInErrorRegistry.put(handler, handler); } @@ -435,13 +434,14 @@ private void doAuth(boolean http) throws SiemensHvacException { @Override public void displayRequestStats() { - logger.info("DisplayRequestStats : currentRuning {}, error {}", currentHandlerRegistry.keySet().size(), - handlerInErrorRegistry.keySet().size()); + logger.debug("DisplayRequestStats : "); + logger.debug(" currentRuning : {}", currentHandlerRegistry.keySet().size()); + logger.debug(" errors : {}", handlerInErrorRegistry.keySet().size()); } @Override public void waitAllPendingRequest() { - logger.debug("WaitAllPendingRequest:start"); + logger.debug("WaitAllPendingRequest:start2"); try { Thread.sleep(1000); boolean allRequestDone = false; From 5b7564a414175631232cf88101388cb0a0f911ed Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 21 Dec 2023 18:49:11 +0100 Subject: [PATCH 106/214] add a stale request verification mechanism Signed-off-by: Laurent ARNAL --- .../handler/SiemensHvacHandlerImpl.java | 4 +- .../SiemensHvacOZW672BridgeThingHandler.java | 2 +- .../network/SiemensHvacConnectorImpl.java | 31 ++++++++++++- .../network/SiemensHvacRequestHandler.java | 24 ++++++++++ .../network/SiemensHvacRequestListener.java | 44 +++++-------------- 5 files changed, 67 insertions(+), 38 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index ff8ffe3eae434..d6d169e50b9db 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -187,7 +187,7 @@ private void pollingCode() { } private void readChannel(Channel channel) { - logger.debug("{}", channel.getDescription()); + logger.debug("readChannel : {}", channel.getDescription()); ThingHandlerCallback cb = this.getCallback(); boolean isLink = false; @@ -223,7 +223,7 @@ private void readChannel(Channel channel) { return; } readDp(id, uid, type, true); - logger.debug("{}", isLink); + logger.debug("isChannelLink : {}", isLink); } public void decodeReadDp(@Nullable JsonObject response, @Nullable String uid, @Nullable String dp, diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java index b1e50916d0b35..61e9131d318b0 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java @@ -47,7 +47,7 @@ public SiemensHvacOZW672BridgeThingHandler(Bridge bridge, @Nullable NetworkAddre @Override public void initialize() { - logger.debug("Initialize() bridge"); + logger.debug("Initialize() bridge4"); super.initialize(); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 14071ea062de1..6573ef3c919b9 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -166,6 +166,7 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacRequestHandl try { final Request retryRequest = httpClient.newRequest(request.getURI()); request.method(HttpMethod.GET); + reqHandler.setRequest(retryRequest); reqHandler.incrementRetryCount(); if (retryRequest != null) { @@ -189,6 +190,7 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacRequestHandl SiemensHvacRequestHandler requestHandler = null; if (callback != null) { requestHandler = new SiemensHvacRequestHandler(callback, this); + requestHandler.setRequest(request); currentHandlerRegistry.put(requestHandler, requestHandler); } @@ -443,7 +445,6 @@ public void displayRequestStats() { public void waitAllPendingRequest() { logger.debug("WaitAllPendingRequest:start2"); try { - Thread.sleep(1000); boolean allRequestDone = false; int idx = 0; @@ -456,6 +457,10 @@ public void waitAllPendingRequest() { allRequestDone = true; } Thread.sleep(1000); + + if ((idx % 30) == 0) { + CheckStaleRequest(); + } idx++; } } catch (InterruptedException ex) { @@ -465,6 +470,30 @@ public void waitAllPendingRequest() { logger.debug("WaitAllPendingRequest:end WaitAllPendingRequest"); } + public void CheckStaleRequest() { + logger.debug("check stale request::begin"); + for (SiemensHvacRequestHandler handler : handlerInErrorRegistry.keySet()) { + long elapseTime = handler.getElapseTime(); + if (elapseTime > 300) { + String uri = ""; + Request request = handler.getRequest(); + if (request != null) { + uri = request.getURI().toString(); + } + logger.debug("find stale request: {} {}", elapseTime, uri); + + try { + unregisterRequestHandler(handler); + registerHandlerError(handler); + } catch (SiemensHvacException ex) { + logger.debug("error unregistring handler: {}", handler); + } + + } + } + logger.debug("check stale request::end"); + } + @Override public void waitNoNewRequest() { logger.debug("WaitNoNewRequest:start"); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestHandler.java index 33aaf3393ffcd..6df8ccb47b08d 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestHandler.java @@ -12,6 +12,9 @@ */ package org.openhab.binding.siemenshvac.internal.network; +import java.time.Duration; +import java.time.Instant; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.api.Request; @@ -36,6 +39,8 @@ public class SiemensHvacRequestHandler { @Nullable private Result result = null; + private Instant startRequest; + /** * Callback to execute on complete response */ @@ -49,6 +54,7 @@ public class SiemensHvacRequestHandler { public SiemensHvacRequestHandler(SiemensHvacCallback callback, SiemensHvacConnector hvacConnector) { this.callback = callback; this.hvacConnector = hvacConnector; + startRequest = Instant.now(); } public SiemensHvacConnector getHvacConnector() { @@ -81,4 +87,22 @@ public Request getRequest() { public Result getResult() { return result; } + + public void setResponse(@Nullable Response response) { + this.response = response; + } + + public void setRequest(@Nullable Request request) { + this.request = request; + } + + public void setResult(@Nullable Result result) { + this.result = result; + } + + public long getElapseTime() { + Instant finish = Instant.now(); + long elapseTime = Duration.between(startRequest, finish).toSeconds(); + return elapseTime; + } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java index e788a07e7a02e..4449bb4b1671c 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java @@ -17,8 +17,6 @@ import java.net.SocketException; import java.net.SocketTimeoutException; import java.util.concurrent.TimeoutException; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -51,8 +49,6 @@ public enum ErrorSource { ErrorThings } - private static Lock lockObj = new ReentrantLock(); - private static int onSuccessCount = 0; private static int onBeginCount = 0; private static int onQueuedCount = 0; @@ -102,12 +98,8 @@ public SiemensHvacRequestListener(SiemensHvacRequestHandler requestHandler) { @Override public void onSuccess(@Nullable Response response) { - lockObj.lock(); - try { - onSuccessCount++; - } finally { - lockObj.unlock(); - } + onSuccessCount++; + requestHandler.setResponse(response); if (response != null) { logger.debug("{} response: {}", response.getRequest().getURI(), response.getStatus()); @@ -116,12 +108,8 @@ public void onSuccess(@Nullable Response response) { @Override public void onFailure(@Nullable Response response, @Nullable Throwable failure) { - lockObj.lock(); - try { - onFailureCount++; - } finally { - lockObj.unlock(); - } + onFailureCount++; + requestHandler.setResponse(response); if (response != null && failure != null) { Throwable cause = failure.getCause(); @@ -151,22 +139,14 @@ public void onFailure(@Nullable Response response, @Nullable Throwable failure) @Override public void onQueued(@Nullable Request request) { - lockObj.lock(); - try { - onQueuedCount++; - } finally { - lockObj.unlock(); - } + onQueuedCount++; + requestHandler.setRequest(request); } @Override public void onBegin(@Nullable Request request) { - lockObj.lock(); - try { - onBeginCount++; - } finally { - lockObj.unlock(); - } + onBeginCount++; + requestHandler.setRequest(request); } public void displayStats() { @@ -184,12 +164,8 @@ public static void displayStats(Logger logger) { @Override public void onComplete(@Nullable Result result) { - lockObj.lock(); - try { - onCompleteCount++; - } finally { - lockObj.unlock(); - } + onCompleteCount++; + requestHandler.setResult(result); if (result == null) { return; From 0b8a97a5b09016285502e4d76a3264636a2f1690 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Fri, 22 Dec 2023 09:27:17 +0100 Subject: [PATCH 107/214] fix possible specific value return by gateway for DecimalType when no value available Signed-off-by: Laurent ARNAL --- .../internal/handler/SiemensHvacHandlerImpl.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index d6d169e50b9db..6b46fc0c8ed19 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -255,7 +255,11 @@ public void decodeReadDp(@Nullable JsonObject response, @Nullable String uid, @N } if (("Numeric").equals(typer)) { - updateState(updateKey, new DecimalType(value.getAsDouble())); + if ("----".equals(value.getAsString())) { + updateState(updateKey, new DecimalType(0)); + } else { + updateState(updateKey, new DecimalType(value.getAsDouble())); + } } else if ("Enumeration".equals(typer)) { if (enumValue != null) { updateState(updateKey, new DecimalType(enumValue.getAsInt())); From eb442f4af7c0a7ecbea1314718e8cf6b2f44e8cf Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Fri, 22 Dec 2023 09:27:46 +0100 Subject: [PATCH 108/214] add build date to log to see which version is running Signed-off-by: Laurent ARNAL --- .../SiemensHvacOZW672BridgeThingHandler.java | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java index 61e9131d318b0..8ff2e32ec1d3a 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java @@ -12,7 +12,12 @@ */ package org.openhab.binding.siemenshvac.internal.handler; +import java.net.URL; +import java.net.URLConnection; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.Collection; +import java.util.Date; import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -47,10 +52,29 @@ public SiemensHvacOZW672BridgeThingHandler(Bridge bridge, @Nullable NetworkAddre @Override public void initialize() { - logger.debug("Initialize() bridge4"); + logger.debug("Initialize() bridge : {}", getBuildDate()); super.initialize(); } + private String getBuildDate() { + try { + ClassLoader cl = getClass().getClassLoader(); + if (cl != null) { + URL res = cl.getResource(getClass().getCanonicalName().replace('.', '/') + ".class"); + if (res != null) { + URLConnection cnx = res.openConnection(); + Date dt = new Date(cnx.getLastModified()); + DateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); + return df.format(dt); + }a + } + + } catch (Exception ex) { + + } + return "unknow"; + } + @Override public void dispose() { super.dispose(); From fe3ad67641e376e5f79f99e8c2d78964f0dda8ab Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Fri, 22 Dec 2023 09:29:52 +0100 Subject: [PATCH 109/214] review debuging info on StaleRequest checker Signed-off-by: Laurent ARNAL --- .../internal/network/SiemensHvacConnectorImpl.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 6573ef3c919b9..2cc31ce2fbf89 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -458,7 +458,7 @@ public void waitAllPendingRequest() { } Thread.sleep(1000); - if ((idx % 30) == 0) { + if ((idx % 60) == 0) { CheckStaleRequest(); } idx++; @@ -472,6 +472,7 @@ public void waitAllPendingRequest() { public void CheckStaleRequest() { logger.debug("check stale request::begin"); + int staleRequest = 0; for (SiemensHvacRequestHandler handler : handlerInErrorRegistry.keySet()) { long elapseTime = handler.getElapseTime(); if (elapseTime > 300) { @@ -481,6 +482,7 @@ public void CheckStaleRequest() { uri = request.getURI().toString(); } logger.debug("find stale request: {} {}", elapseTime, uri); + staleRequest++; try { unregisterRequestHandler(handler); @@ -491,7 +493,7 @@ public void CheckStaleRequest() { } } - logger.debug("check stale request::end"); + logger.debug("check stale request::end : {}", staleRequest); } @Override From 19e5db265a89a87832fba513402e964e00170ab5 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Fri, 22 Dec 2023 09:40:21 +0100 Subject: [PATCH 110/214] move readChannel loging to prevent log flood Signed-off-by: Laurent ARNAL --- .../internal/handler/SiemensHvacHandlerImpl.java | 6 +++--- .../handler/SiemensHvacOZW672BridgeThingHandler.java | 2 +- .../internal/network/SiemensHvacConnectorImpl.java | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index 6b46fc0c8ed19..b2fcde9f159c9 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -147,6 +147,7 @@ private void pollingCode() { int previousRequestCount = lcHvacConnector.getRequestCount(); int previousErrorCount = lcHvacConnector.getErrorCount(); + logger.debug("readChannels :"); for (Channel channel : chList) { readChannel(channel); } @@ -187,8 +188,6 @@ private void pollingCode() { } private void readChannel(Channel channel) { - logger.debug("readChannel : {}", channel.getDescription()); - ThingHandlerCallback cb = this.getCallback(); boolean isLink = false; @@ -200,6 +199,8 @@ private void readChannel(Channel channel) { return; } + logger.debug("readChannel : {}", channel.getDescription()); + ChannelType tp = channelTypeRegistry.getChannelType(channel.getChannelTypeUID()); if (tp == null) { @@ -223,7 +224,6 @@ private void readChannel(Channel channel) { return; } readDp(id, uid, type, true); - logger.debug("isChannelLink : {}", isLink); } public void decodeReadDp(@Nullable JsonObject response, @Nullable String uid, @Nullable String dp, diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java index 8ff2e32ec1d3a..536f1c452ef11 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java @@ -66,7 +66,7 @@ private String getBuildDate() { Date dt = new Date(cnx.getLastModified()); DateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); return df.format(dt); - }a + } } } catch (Exception ex) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 2cc31ce2fbf89..f9bbf8f25772f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -373,7 +373,6 @@ private void doAuth(boolean http) throws SiemensHvacException { mUri = mUri + "SessionId=" + sessionId; } - logger.debug("Execute request: {}", uri); CookieStore c = httpClient.getCookieStore(); java.net.HttpCookie cookie = new HttpCookie("SessionId", sessionIdHttp); cookie.setPath("/"); From 079f37be3189f18abb47badfc59cc5d10e8ac144 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Fri, 22 Dec 2023 12:14:20 +0100 Subject: [PATCH 111/214] rename initialization methods Signed-off-by: Laurent ARNAL --- .../internal/handler/SiemensHvacBridgeBaseThingHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java index 10d6f8e7441e5..26d2e9362dc75 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -97,7 +97,7 @@ public void initialize() { config = lcConfig; // Will read metadata in background to not block initialize for a long period ! - scheduler.schedule(this::pollingCode, 1, TimeUnit.SECONDS); + scheduler.schedule(this::initializeCode, 1, TimeUnit.SECONDS); } public static String getStackTrace(final Throwable throwable) { @@ -122,7 +122,7 @@ public static String getStackTrace(final Throwable throwable) { return sb.toString(); } - private void pollingCode() { + private void initializeCode() { try { metaDataRegistry.readMeta(); updateStatus(ThingStatus.ONLINE); From b6fb4fbf0971817493b8d538ae3d8481844a82b7 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Fri, 22 Dec 2023 12:15:24 +0100 Subject: [PATCH 112/214] review timeout handling Signed-off-by: Laurent ARNAL --- .../internal/network/SiemensHvacConnector.java | 2 ++ .../internal/network/SiemensHvacConnectorImpl.java | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java index 13db9d1a30e3b..39556331a4c9a 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java @@ -65,4 +65,6 @@ void onError(Request request, SiemensHvacRequestHandler reqListener, SiemensHvacRequestListener.ErrorSource getErrorSource(); void invalidate(); + + void setTimeOut(int timeout); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index f9bbf8f25772f..513903f92d117 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -78,6 +78,7 @@ public class SiemensHvacConnectorImpl implements SiemensHvacConnector { private int requestCount = 0; private int errorCount = 0; + private int timeout = 10; private SiemensHvacRequestListener.ErrorSource errorSource = SiemensHvacRequestListener.ErrorSource.ErrorBridge; private @Nullable SiemensHvacBridgeBaseThingHandler hvacBridgeBaseThingHandler; @@ -213,7 +214,7 @@ private void registerHandlerError(SiemensHvacRequestHandler handler) { @Nullable SiemensHvacRequestHandler requestHandler) throws Exception { // Give a high timeout because we queue a lot of async request, // so enqueued them will take some times ... - request.timeout(240, TimeUnit.SECONDS); + request.timeout(timeout, TimeUnit.SECONDS); ContentResponse response = null; @@ -442,7 +443,7 @@ public void displayRequestStats() { @Override public void waitAllPendingRequest() { - logger.debug("WaitAllPendingRequest:start2"); + logger.debug("WaitAllPendingRequest:start"); try { boolean allRequestDone = false; int idx = 0; @@ -565,4 +566,8 @@ public void invalidate() { currentHandlerRegistry.clear(); handlerInErrorRegistry.clear(); } + + public void setTimeOut(int timeout) { + this.timeout = timeout; + } } From 2523aa5678205dd552fcc43190d522e5edf3ee24 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Fri, 22 Dec 2023 12:15:58 +0100 Subject: [PATCH 113/214] add invalidation methods on provider to enable reinit of the addon Signed-off-by: Laurent ARNAL --- .../SiemensHvacMetadataRegistryImpl.java | 158 +++++++++++------- .../SiemensHvacChannelGroupTypeProvider.java | 2 + ...emensHvacChannelGroupTypeProviderImpl.java | 5 + .../type/SiemensHvacChannelTypeProvider.java | 2 + .../SiemensHvacChannelTypeProviderImpl.java | 5 + .../SiemensHvacConfigDescriptionProvider.java | 2 + ...mensHvacConfigDescriptionProviderImpl.java | 5 + .../type/SiemensHvacThingTypeProvider.java | 2 + .../SiemensHvacThingTypeProviderImpl.java | 5 + .../siemenshvac/internal/type/UidUtils.java | 1 + 10 files changed, 128 insertions(+), 59 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java index 437a8c076c33e..44737ab70cc2f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -19,6 +19,8 @@ import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; @@ -298,10 +300,19 @@ public void readMeta() throws SiemensHvacException { logger.info("siemensHvac:Initialization():ReadCache"); loadMetaDataFromCache(); + // increase the timeout during this phase + // because we queued a lot of request + // and timeout start to run when request is queued (not executed) + + Instant start = Instant.now(); + lcHvacConnector.setTimeOut(600); + logger.info("siemensHvac:Initialization():ReadDeviceList"); readDeviceList(); if (root == null) { + logger.info("siemensHvac:Initialization():No cache information, root==null, reading metadata from device"); + logger.info("siemensHvac:Initialization():BeginReadMenu"); root = new SiemensHvacMetadataMenu(); @@ -334,6 +345,12 @@ public void readMeta() throws SiemensHvacException { unresolveCount = unresolveCount(); } + Instant end = Instant.now(); + lcHvacConnector.setTimeOut(30); + + long elapseTime = Duration.between(start, end).toSeconds(); + logger.info("siemensHvac:Initialization():ReadMetadata in {} s", elapseTime); + logger.info("siemensHvac:Initialization():SaveCache"); saveMetaDataToCache(); @@ -342,10 +359,6 @@ public void readMeta() throws SiemensHvacException { lcDevices = devices; if (lcDevices != null) { for (SiemensHvacMetadataDevice device : lcDevices) { - if (device.getType().indexOf("OZW672") >= 0) { - continue; - } - generateThingsType(device); } } @@ -355,8 +368,6 @@ public void readMeta() throws SiemensHvacException { private void generateThingsType(SiemensHvacMetadataDevice device) { SiemensHvacThingTypeProvider lcThingTypeProvider = thingTypeProvider; - SiemensHvacChannelTypeProvider lcChannelTypeProvider = channelTypeProvider; - SiemensHvacChannelGroupTypeProvider lcChannelGroupTypeProvider = channelGroupTypeProvider; logger.debug("Generate thing types for device : {} / {}", device.getName(), device.getSerialNr()); if (lcThingTypeProvider != null) { ThingTypeUID thingTypeUID = UidUtils.generateThingTypeUID(device); @@ -374,81 +385,94 @@ private void generateThingsType(SiemensHvacMetadataDevice device) { if (menu != null) { var childs = menu.getChilds().values(); for (SiemensHvacMetadata child : childs) { + generateThingsType(child, groupTypes, menu); + } - if (child instanceof SiemensHvacMetadataMenu) { - SiemensHvacMetadataMenu subMenu = (SiemensHvacMetadataMenu) child; + } - List channelDefinitions = new ArrayList<>(); + } - for (SiemensHvacMetadata childDt : subMenu.getChilds().values()) { + tt = createThingType(device, groupTypes); + lcThingTypeProvider.addThingType(tt); + } + } + } - if (childDt instanceof SiemensHvacMetadataDataPoint) { - SiemensHvacMetadataDataPoint dataPoint = (SiemensHvacMetadataDataPoint) childDt; + private void generateThingsType(SiemensHvacMetadata child, List groupTypes, + SiemensHvacMetadataMenu menu) { + SiemensHvacChannelTypeProvider lcChannelTypeProvider = channelTypeProvider; + SiemensHvacChannelGroupTypeProvider lcChannelGroupTypeProvider = channelGroupTypeProvider; - if (dataPoint.getDptType().isEmpty()) { - continue; - } + if (child instanceof SiemensHvacMetadataMenu) { + SiemensHvacMetadataMenu subMenu = (SiemensHvacMetadataMenu) child; - ChannelTypeUID channelTypeUID = UidUtils.generateChannelTypeUID(dataPoint); + List channelDefinitions = new ArrayList<>(); - ChannelType channelType = null; + for (SiemensHvacMetadata childDt : subMenu.getChilds().values()) { - if (channelTypeProvider != null && lcChannelTypeProvider != null) { - channelType = lcChannelTypeProvider.getInternalChannelType(channelTypeUID); - if (channelType == null) { - channelType = createChannelType(dataPoint, channelTypeUID); - lcChannelTypeProvider.addChannelType(channelType); - } - } + try { + if (childDt instanceof SiemensHvacMetadataMenu) { + generateThingsType(childDt, groupTypes, menu); + } + if (childDt instanceof SiemensHvacMetadataDataPoint) { + SiemensHvacMetadataDataPoint dataPoint = (SiemensHvacMetadataDataPoint) childDt; - SiemensHvacMetadataDataPoint dpt = ((SiemensHvacMetadataDataPoint) childDt); + if (dataPoint.getDptType().isEmpty()) { + continue; + } - Map props = new Hashtable(); - props.put("dptId", "" + dpt.getDptId()); - props.put("id", "" + dpt.getId()); - props.put("subId", "" + dpt.getSubId()); - props.put("groupdId", "" + dpt.getGroupId()); + ChannelTypeUID channelTypeUID = UidUtils.generateChannelTypeUID(dataPoint); - String id = dataPoint.getId() + "_" - + UidUtils.sanetizeId(dataPoint.getShortDesc()); + ChannelType channelType = null; - if (channelType != null) { - ChannelDefinition channelDef = new ChannelDefinitionBuilder(id, - channelType.getUID()).withLabel(dataPoint.getShortDesc()) - .withDescription(dataPoint.getLongDesc()).withProperties(props) - .build(); + if (channelTypeProvider != null && lcChannelTypeProvider != null) { + channelType = lcChannelTypeProvider.getInternalChannelType(channelTypeUID); + if (channelType == null) { + channelType = createChannelType(dataPoint, channelTypeUID); + lcChannelTypeProvider.addChannelType(channelType); + } + } - channelDefinitions.add(channelDef); - } - } - } + SiemensHvacMetadataDataPoint dpt = ((SiemensHvacMetadataDataPoint) childDt); - // generate group - ChannelGroupTypeUID groupTypeUID = UidUtils.generateChannelGroupTypeUID(subMenu); - ChannelGroupType groupType = null; + Map props = new Hashtable(); + props.put("dptId", "" + dpt.getDptId()); + props.put("id", "" + dpt.getId()); + props.put("subId", "" + dpt.getSubId()); + props.put("groupdId", "" + dpt.getGroupId()); - if (lcChannelGroupTypeProvider != null) { - groupType = lcChannelGroupTypeProvider.getInternalChannelGroupType(groupTypeUID); + String id = dataPoint.getId() + "_" + UidUtils.sanetizeId(dataPoint.getShortDesc()); - if (groupType == null) { - String groupLabel = subMenu.getShortDesc(); - groupType = ChannelGroupTypeBuilder.instance(groupTypeUID, groupLabel) - .withChannelDefinitions(channelDefinitions).withCategory("") - .withDescription(menu.getLongDesc()).build(); - lcChannelGroupTypeProvider.addChannelGroupType(groupType); - groupTypes.add(groupType); - } - } + if (channelType != null) { + ChannelDefinition channelDef = new ChannelDefinitionBuilder(id, channelType.getUID()) + .withLabel(dataPoint.getShortDesc()).withDescription(dataPoint.getLongDesc()) + .withProperties(props).build(); - } + channelDefinitions.add(channelDef); } } - + } catch (Exception ex) { + logger.info("unable to create channel for: {}", childDt); } + } - tt = createThingType(device, groupTypes); - lcThingTypeProvider.addThingType(tt); + // generate group + ChannelGroupTypeUID groupTypeUID = UidUtils.generateChannelGroupTypeUID(subMenu); + ChannelGroupType groupType = null; + + if (lcChannelGroupTypeProvider != null) { + groupType = lcChannelGroupTypeProvider.getInternalChannelGroupType(groupTypeUID); + + if (groupType == null) { + String groupLabel = subMenu.getShortDesc(); + groupType = ChannelGroupTypeBuilder.instance(groupTypeUID, groupLabel) + .withChannelDefinitions(channelDefinitions).withCategory("") + .withDescription(menu.getLongDesc()).build(); + lcChannelGroupTypeProvider.addChannelGroupType(groupType); + groupTypes.add(groupType); + } } + } } @@ -1212,5 +1236,21 @@ public void invalidate() { if (hvacConnector != null) { hvacConnector.invalidate(); } + + if (channelGroupTypeProvider != null) { + channelGroupTypeProvider.invalidate(); + } + + if (thingTypeProvider != null) { + thingTypeProvider.invalidate(); + } + + if (channelTypeProvider != null) { + channelTypeProvider.invalidate(); + } + + if (configDescriptionProvider != null) { + configDescriptionProvider.invalidate(); + } } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java index 563a8c6d92d14..040c2355a29a9 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java @@ -46,4 +46,6 @@ public interface SiemensHvacChannelGroupTypeProvider extends ChannelGroupTypePro */ @Nullable ChannelGroupType getInternalChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID); + + void invalidate(); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java index 0cd5a73323420..71f9b34cc5255 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java @@ -68,4 +68,9 @@ public Collection getChannelGroupTypes(@Nullable Locale locale } return result; } + + @Override + public void invalidate() { + channelGroupTypesByUID.clear(); + } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java index fe822e315eb13..5ddc1b0c21c50 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java @@ -45,4 +45,6 @@ public interface SiemensHvacChannelTypeProvider extends ChannelTypeProvider { */ @Nullable ChannelType getInternalChannelType(@Nullable ChannelTypeUID channelTypeUID); + + void invalidate(); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java index 62006de0cb240..41881a20d33cc 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java @@ -68,4 +68,9 @@ public Collection getChannelTypes(@Nullable Locale locale) { public @Nullable ChannelType getInternalChannelType(@Nullable ChannelTypeUID channelTypeUID) { return channelTypesByUID.get(channelTypeUID); } + + @Override + public void invalidate() { + channelTypesByUID.clear(); + } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java index 4186b11c84919..cbe83947a8d6b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java @@ -60,4 +60,6 @@ public interface SiemensHvacConfigDescriptionProvider extends ConfigDescriptionP */ @Nullable ConfigDescription getInternalConfigDescription(URI uri); + + void invalidate(); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProviderImpl.java index 67fd82da9a284..e8499fa918661 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProviderImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProviderImpl.java @@ -61,4 +61,9 @@ public ConfigDescription getInternalConfigDescription(URI uri) { public void addConfigDescription(ConfigDescription configDescription) { configDescriptionsByURI.put(configDescription.getUID(), configDescription); } + + @Override + public void invalidate() { + configDescriptionsByURI.clear(); + } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java index c6f10be87ef1b..800a22e57b968 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java @@ -46,4 +46,6 @@ public interface SiemensHvacThingTypeProvider extends ThingTypeProvider { */ @Nullable ThingType getInternalThingType(ThingTypeUID thingTypeUID); + + void invalidate(); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProviderImpl.java index dc3fc5352cbe0..315ff75e19a43 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProviderImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProviderImpl.java @@ -58,4 +58,9 @@ public Collection getThingTypes(@Nullable Locale locale) { public @Nullable ThingType getThingType(ThingTypeUID thingTypeUID, @Nullable Locale locale) { return thingTypesByUID.get(thingTypeUID); } + + @Override + public void invalidate() { + thingTypesByUID.clear(); + } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index 78d4f97b59cc8..db8c1893c71a4 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -49,6 +49,7 @@ public static String sanetizeId(String label) { } result = result.replace(' ', '_'); + result = result.replace(':', '_'); result = result.replace('.', '_'); result = result.replace('\'', '_'); result = result.replace('(', '_'); From fff9ba8d5262ad55ca7e11aeede977c084844aad Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 31 Dec 2023 11:59:15 +0100 Subject: [PATCH 114/214] update pom version Signed-off-by: Laurent ARNAL --- bundles/org.openhab.binding.siemenshvac/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/pom.xml b/bundles/org.openhab.binding.siemenshvac/pom.xml index b892be90efa75..c28b427e09674 100644 --- a/bundles/org.openhab.binding.siemenshvac/pom.xml +++ b/bundles/org.openhab.binding.siemenshvac/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.siemenshvac From fd3b6bf3acca89160479702197be219b63d44dde Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 31 Dec 2023 11:59:31 +0100 Subject: [PATCH 115/214] fix error on stale request handling Signed-off-by: Laurent ARNAL --- .../siemenshvac/internal/network/SiemensHvacConnectorImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 513903f92d117..0d10152836153 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -473,7 +473,7 @@ public void waitAllPendingRequest() { public void CheckStaleRequest() { logger.debug("check stale request::begin"); int staleRequest = 0; - for (SiemensHvacRequestHandler handler : handlerInErrorRegistry.keySet()) { + for (SiemensHvacRequestHandler handler : currentHandlerRegistry.keySet()) { long elapseTime = handler.getElapseTime(); if (elapseTime > 300) { String uri = ""; @@ -567,6 +567,7 @@ public void invalidate() { handlerInErrorRegistry.clear(); } + @Override public void setTimeOut(int timeout) { this.timeout = timeout; } From a1c989ad712d43a144145f8475c401d5ac740624 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 3 Jan 2024 14:18:14 +0100 Subject: [PATCH 116/214] fix README.md as request by @JLaur Signed-off-by: Laurent ARNAL --- .../org.openhab.binding.siemenshvac/README.md | 60 +++++++------------ 1 file changed, 21 insertions(+), 39 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/README.md b/bundles/org.openhab.binding.siemenshvac/README.md index 3569bec157cc7..c2e5a660e103d 100644 --- a/bundles/org.openhab.binding.siemenshvac/README.md +++ b/bundles/org.openhab.binding.siemenshvac/README.md @@ -1,12 +1,12 @@ # SiemensHvac Binding -This binding is to support Siemens Hvac controller ecosystem, and the Web Gateway interface OZW672. +This binding provides support for the Siemens Hvac controller ecosystem, and the Web Gateway interface OZW672. A typical system is composed of: ![Diagram](doc/Diagram.png) There's a lot of different HVAC controllers depending on model in lot of different PAC constructors. -Siemens RVS41.813/327 inside a Atlantic Hybrid Duo was used for the developpement, and is fully supported and tested. +Siemens RVS41.813/327 inside a Atlantic Hybrid Duo was used for the development, and is fully supported and tested. Siemens have a complete set of controller references under the name "Siemens Albatros". Here is a picture of such device. @@ -34,26 +34,17 @@ No test done with OZW772.x series, but it should work as well. After, it will discover thing inside your PAC, mainly main controller of type RVS... Only test in real condition with RVS41.813/327 but should work with all other type as the access interface is standard. - ## Discovery -Discovery of Gateway can be done using Upnp. +Discovery of Gateway can be done using UPnP. Just switch off/on your gateway to make it annonce itself on the network. -The gateway should appears in the Inbox a few minutes after. -Be aware what you will have to modifity the password in Gateway parameters just after the discovering to make it work properly. -Be also aware that first initialization is a little long because we need to read all the metadata from the device. - -Discovery of Hvac device have to be done through the Scan button inside the binding. -Go to the thing page, click on the "+" button, select the siemensHvac binding, and then click Scan. -Your device should appears on the page after a few seconds. - - -## Binding Configuration - -There is no particular configuration to be done. -The only revelant parameters is on the OZW672 thing for the user and password to use to connect to the gateway. -IP should have be discovered automatically via UPNP. +The gateway should appear in the Inbox a few minutes after. +Be aware what you will have to modify the password in Gateway parameters just after the discovery to make it work properly. +Be also aware that first initialization is a little long because the binding needs to read all the metadata from the device. +Discovery of HVAC device have to be done through the Scan button inside the binding. +Go to the Thing page, click on the "+" button, select the SiemensHvac binding, and then click Scan. +Your device should appear on the page after a few seconds. ## Bridge Configuration @@ -63,26 +54,19 @@ baseUrl | yes | | The address of the OZW672 de userName | yes | Administrator | The user name to log into the OZW672 userPass | yes | | The user password to log into the OZW672 - - ## Thing Configuration - - ## Channels -Channels are autodiscovered, you will find them on the RVS things. -They are organized the same way as the LCD screen of your PAC device, by top level menu functionnality, and sub-functionnalities. -Each channel are strongly typed, so for exemple, for heating mode, openhab will provide you with a list of choice supported by the device. - +Channels are auto-discovered, you will find them on the RVS things. +They are organized the same way as the LCD screen of your PAC device, by top level menu functionality, and sub-functionalities. +Each channel is strongly typed, so for example, for heating mode, openHAB will provide you with a list of choices supported by the device. Channel | Description | Type | Unit | Security Access Level | ReadOnly | Advanced -----------------------|----------------------------------------------------------|---------------|----------|-------------------------|-----------|------------------- controlBoilerApproval | Set Boiler Approval (`AUTO`, `OFF`, `ON`) | String | | | R/W | true controlProgram | Set Program (`OFF`, `NORMAL`, `WARMWATER`, `MANUAL`) | String | | | R/W | true - - Channel Type ID | Item Type | Description -----------------------|------------------------------------------|---------------------------------------------- Numeric | Number | Handle basic numeric value @@ -95,7 +79,6 @@ Temperature | Number | Use to handl Setpoint | Number | Handle the setting of a temperature Regime | Number | Enumeration for handling mode change - ## Full Example Things file `.things` @@ -112,18 +95,17 @@ Bridge siemenshvac:ozw672:local "Ozw672"@"Chaufferie" [ baseUrl="https://192.168 } ``` - Items file `.items` ```java -String Boiler_State_Pump_HWS "HWS Pump State [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2259_PpeChargeECS" } -Number Boiler_State_HWS "HWS State [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:2032#2035_Etat_ECS" } -Number:Temperature Flow_Temperature_Real "Flow Temparature Real [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2248_ValReelleTempDep_CC1" } -Number:Temperature Flow_Temperature_Setpoint "Flow Temperature Setpoint [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2249_ConsTDepResultCC1" } -Number Hour_fct_HWS "HWS Hour function" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2263_HeuresFoncPompeECS" } -Number Nb_Start_HWS "HWS Number of start [%.1f]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2266_ComptDemarResEl_ECS" } -Number Thermostat_Temperature "Thermostat tempeature [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2246_TAmbAct_CC1" } -Number Thermostat_Setpoint "Thermostat setpoint [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:1724#1726_ConsConfort_TA_CC1" } -Number Heat_Mode "Heat mode [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:1724#1725_Regime_CC1" } +String Boiler_State_Pump_HWS "HWS Pump State [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2259_PpeChargeECS" } +Number Boiler_State_HWS "HWS State [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:2032#2035_Etat_ECS" } +Number:Temperature Flow_Temperature_Real "Flow Temparature Real [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2248_ValReelleTempDep_CC1" } +Number:Temperature Flow_Temperature_Setpoint "Flow Temperature Setpoint [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2249_ConsTDepResultCC1" } +Number Hour_fct_HWS "HWS Hour function" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2263_HeuresFoncPompeECS" } +Number Nb_Start_HWS "HWS Number of start [%.1f]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2266_ComptDemarResEl_ECS" } +Number:Temperature Thermostat_Temperature "Thermostat tempeature [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:2237#2246_TAmbAct_CC1" } +Number:Temperature Thermostat_Setpoint "Thermostat setpoint [%.1f °C]" { channel = "siemenshvac:RVS41_813_327:local:local:1724#1726_ConsConfort_TA_CC1" } +Number Heat_Mode "Heat mode [%s]" { channel = "siemenshvac:RVS41_813_327:local:local:1724#1725_Regime_CC1" } ``` From 6507097b5c6c499ce247b70824dfdcaf4a21bc6d Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 3 Jan 2024 14:50:47 +0100 Subject: [PATCH 117/214] fix bad author in comments Signed-off-by: Laurent ARNAL --- .../internal/constants/SiemensHvacBindingConstants.java | 2 +- .../openhab/binding/siemenshvac/internal/type/UidUtilsTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java index 27b8e4f69f51e..dd2349983ceb8 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java @@ -19,7 +19,7 @@ * The {@link SiemensHvacBindingConstants} class defines common constants, which are * used across the whole binding. * - * @author Laurent ARNAL - Initial contribution + * @author Laurent Arnal - Initial contribution */ @NonNullByDefault public class SiemensHvacBindingConstants { diff --git a/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java b/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java index 6cac9e167c161..0a46007345805 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java +++ b/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java @@ -18,7 +18,7 @@ import org.junit.jupiter.api.Test; /** - * @author Sönke Küper - Initial contribution + * @author Laurent Arnal - Initial contribution */ @NonNullByDefault public class UidUtilsTest { From 1d58cf56efcda095eeaca74d498f1e63a65cd3ea Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 3 Jan 2024 14:53:20 +0100 Subject: [PATCH 118/214] fix homematic reference in comments ! Signed-off-by: Laurent ARNAL --- .../type/SiemensHvacChannelGroupTypeProvider.java | 12 +----------- .../SiemensHvacChannelGroupTypeProviderImpl.java | 2 +- .../type/SiemensHvacChannelTypeProvider.java | 11 ++++------- .../type/SiemensHvacConfigDescriptionProvider.java | 10 +--------- .../internal/type/SiemensHvacThingTypeProvider.java | 3 +-- 5 files changed, 8 insertions(+), 30 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java index 040c2355a29a9..924aa37642330 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java @@ -32,17 +32,7 @@ public interface SiemensHvacChannelGroupTypeProvider extends ChannelGroupTypePro void addChannelGroupType(ChannelGroupType channelGroupType); /** - * Use this method to lookup a ChannelGroupType which was generated by the - * siemensHvac binding. Other than {@link #getChannelGroupType(ChannelGroupTypeUID)} - * of this provider, it will return also those {@link ChannelGroupType}s - * which are excluded by {@link HomematicThingTypeExcluder} - * - * @param channelGroupTypeUID - * e.g. homematic:HM-WDS40-TH-I-2_0 - * @return ChannelGroupType that was added to HomematicChannelGroupTypeProvider, identified - * by its config-description-uri
- * null if no ChannelGroupType with the given UID was added - * before + * Use this method to lookup a ChannelGroupType which was generated by the siemensHvac binding. */ @Nullable ChannelGroupType getInternalChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java index 71f9b34cc5255..152cbcb437885 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java @@ -26,7 +26,7 @@ import org.osgi.service.component.annotations.Component; /** - * Provides all ChannelGroupTypes from all Homematic bridges. + * Provides all ChannelGroupTypes from all SiemensHvac bridges. * * @author Laurent Arnal - Initial contribution */ diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java index 5ddc1b0c21c50..a6ee41c8a3c3e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java @@ -32,14 +32,11 @@ public interface SiemensHvacChannelTypeProvider extends ChannelTypeProvider { void addChannelType(ChannelType channelType); /** - * Use this method to lookup a ChannelType which was generated by the - * siemensHvac binding. Other than {@link #getChannelType(ChannelTypeUID)} - * of this provider, it will return also those {@link ChannelType}s - * which are excluded by {@link HomematicThingTypeExcluder} - * + * Use this method to lookup a ChannelType which was generated by the siemensHvac binding. + * * @param channelTypeUID - * @return ChannelType that was added to HomematicChannelTypeProvider, identified - * by its config-description-uri
+ * @return ChannelType that was added to SiemensHvacChannelTypeProvider, identified by its + * config-description-uri
* null if no ChannelType with the given UID was added * before */ diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java index cbe83947a8d6b..d70c4de1724d0 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java @@ -47,16 +47,8 @@ public interface SiemensHvacConfigDescriptionProvider extends ConfigDescriptionP /** * Use this method to lookup a ConfigDescription which was generated by the - * homematic binding. Other than {@link #getConfigDescription(URI, Locale)} - * of this provider, it will return also those {@link ConfigDescription}s - * which are excluded by {@link HomematicThingTypeExcluder} + * siemenshvac binding. * - * @param URI config-description-uri - * e.g. thing-type:homematic:HM-WDS40-TH-I-2 - * @return ConfigDescription that was added to HomematicConfigDescriptionProvider, - * identified by its config-description-uri
- * null if no ConfigDescription with the given URI was added - * before */ @Nullable ConfigDescription getInternalConfigDescription(URI uri); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java index 800a22e57b968..5649257f861b9 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java @@ -38,8 +38,7 @@ public interface SiemensHvacThingTypeProvider extends ThingTypeProvider { * excluded by {@link ThingTypeExcluder} * * @param thingTypeUID - * e.g. homematic:HM-Sec-SC - * @return ThingType that was added to HomematicThingTypeProvider, identified + * @return ThingType that was added to SiemensHvacThingTypeProvider, identified * by its thingTypeUID
* null if no ThingType with the given thingTypeUID was added * before From 83ff9a8d483b1566667e4bdff882934eba355bfa Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 3 Jan 2024 14:56:48 +0100 Subject: [PATCH 119/214] fix exception handling Signed-off-by: Laurent ARNAL --- .../network/SiemensHvacRequestListener.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java index 4449bb4b1671c..936cdd1df7d69 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java @@ -119,18 +119,18 @@ public void onFailure(@Nullable Response response, @Nullable Throwable failure) String msg = cause.getLocalizedMessage(); - if (cause.getClass() == ConnectException.class) { - logger.debug("ConnectException"); - } else if (cause.getClass() == SocketException.class) { - logger.debug("SocketException"); - } else if (cause.getClass() == SocketTimeoutException.class) { + if (cause instanceof ConnectException) { + logger.debug("ConnectException", e); + } else if (cause instanceof SocketException) { + logger.debug("SocketException", e); + } else if (cause instanceof SocketTimeoutException.) { logger.debug("SocketTimeoutException"); - } else if (cause.getClass() == EOFException.class) { - logger.debug("EOFException"); - } else if (cause.getClass() == TimeoutException.class) { - logger.debug("TimeoutException"); + } else if (cause instanceof EOFException) { + logger.debug("EOFException", e); + } else if (cause instanceof TimeoutException) { + logger.debug("TimeoutException", e); } else { - logger.debug("unknow"); + logger.debug("unknown"); } logger.debug("response failed: {} {}", response.getRequest().getURI(), msg, failure); From 715f9cf0e95954ad5ccba668274563da488cb5fa Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 3 Jan 2024 14:59:41 +0100 Subject: [PATCH 120/214] fix duration intermediate variable Signed-off-by: Laurent ARNAL --- .../internal/network/SiemensHvacRequestHandler.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestHandler.java index 6df8ccb47b08d..94edb4ce33289 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestHandler.java @@ -100,9 +100,8 @@ public void setResult(@Nullable Result result) { this.result = result; } - public long getElapseTime() { + public long getElapsedTime() { Instant finish = Instant.now(); - long elapseTime = Duration.between(startRequest, finish).toSeconds(); - return elapseTime; + return Duration.between(startRequest, finish).toSeconds(); } } From 8f3ab58e91b110fc2125caddc342f6b65e38844b Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 3 Jan 2024 15:01:34 +0100 Subject: [PATCH 121/214] fix some logging statement from info to trace review test if instance of Signed-off-by: Laurent ARNAL --- .../SiemensHvacMetadataRegistryImpl.java | 47 +++++++++---------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java index 44737ab70cc2f..8850ac4c6670a 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -271,7 +271,7 @@ public void readMeta() throws SiemensHvacException { } if (lcHvacConnector == null) { - logger.info("SiemensHvacMetadataRegistryImpl:ReadMeta() : lHvacConnector not initialize."); + logger.debug("SiemensHvacMetadataRegistryImpl:ReadMeta() : lHvacConnector not initialize."); return; } @@ -279,7 +279,7 @@ public void readMeta() throws SiemensHvacException { SiemensHvacBridgeConfig config = lcHvacConnector.getBridgeConfiguration(); if (config == null) { - logger.info("SiemensHvacMetadataRegistryImpl:ReadMeta() : config not initialize."); + logger.debug("SiemensHvacMetadataRegistryImpl:ReadMeta() : config not initialize."); return; } @@ -291,13 +291,13 @@ public void readMeta() throws SiemensHvacException { } if (user == null) { - logger.info("SiemensHvacMetadataRegistryImpl:ReadMeta() : cannot find user, aborting."); + logger.error("SiemensHvacMetadataRegistryImpl:ReadMeta() : cannot find user, aborting."); return; } - logger.info("siemensHvac:Initialization():Begin_0001"); + logger.trace("siemensHvac:Initialization():Begin_0001"); - logger.info("siemensHvac:Initialization():ReadCache"); + logger.trace("siemensHvac:Initialization():ReadCache"); loadMetaDataFromCache(); // increase the timeout during this phase @@ -307,13 +307,13 @@ public void readMeta() throws SiemensHvacException { Instant start = Instant.now(); lcHvacConnector.setTimeOut(600); - logger.info("siemensHvac:Initialization():ReadDeviceList"); + logger.trace("siemensHvac:Initialization():ReadDeviceList"); readDeviceList(); if (root == null) { - logger.info("siemensHvac:Initialization():No cache information, root==null, reading metadata from device"); + logger.trace("siemensHvac:Initialization():No cache information, root==null, reading metadata from device"); - logger.info("siemensHvac:Initialization():BeginReadMenu"); + logger.trace("siemensHvac:Initialization():BeginReadMenu"); root = new SiemensHvacMetadataMenu(); changeLanguage(user, 1); @@ -326,20 +326,19 @@ public void readMeta() throws SiemensHvacException { lcHvacConnector.waitNoNewRequest(); lcHvacConnector.waitAllPendingRequest(); - logger.info("siemensHvac:Initialization():EndReadMenu"); + logger.trace("siemensHvac:Initialization():EndReadMenu"); } if (root != null) { - logger.info("siemensHvac:Initialization():BeginInitDptMap"); + logger.trace("siemensHvac:Initialization():BeginInitDptMap"); initDptMap(root); - logger.info("siemensHvac:Initialization():EndInitDptMap"); + logger.trace("siemensHvac:Initialization():EndInitDptMap"); } int unresolveCount = unresolveCount(); - // unresolveCount = 0; while (unresolveCount > 0) { - logger.info("siemensHvac:Initialization():BeginResolveDtpMap {}", unresolveCount); + logger.trace("siemensHvac:Initialization():BeginResolveDtpMap {}", unresolveCount); resolveDetails(unresolveCount); lcHvacConnector.waitAllPendingRequest(); unresolveCount = unresolveCount(); @@ -349,12 +348,12 @@ public void readMeta() throws SiemensHvacException { lcHvacConnector.setTimeOut(30); long elapseTime = Duration.between(start, end).toSeconds(); - logger.info("siemensHvac:Initialization():ReadMetadata in {} s", elapseTime); + logger.trace("siemensHvac:Initialization():ReadMetadata in {} s", elapseTime); - logger.info("siemensHvac:Initialization():SaveCache"); + logger.trace("siemensHvac:Initialization():SaveCache"); saveMetaDataToCache(); - logger.info("siemensHvac:Initialization():InitThing"); + logger.trace("siemensHvac:Initialization():InitThing"); getRoot(); lcDevices = devices; if (lcDevices != null) { @@ -363,12 +362,12 @@ public void readMeta() throws SiemensHvacException { } } - logger.debug("siemensHvac:InitDptMap():end"); + logger.trace("siemensHvac:InitDptMap():end"); } private void generateThingsType(SiemensHvacMetadataDevice device) { SiemensHvacThingTypeProvider lcThingTypeProvider = thingTypeProvider; - logger.debug("Generate thing types for device : {} / {}", device.getName(), device.getSerialNr()); + logger.debug("Generate thing types for device: {} / {}", device.getName(), device.getSerialNr()); if (lcThingTypeProvider != null) { ThingTypeUID thingTypeUID = UidUtils.generateThingTypeUID(device); ThingType tt = null; @@ -403,9 +402,7 @@ private void generateThingsType(SiemensHvacMetadata child, List channelDefinitions = new ArrayList<>(); for (SiemensHvacMetadata childDt : subMenu.getChilds().values()) { @@ -414,8 +411,8 @@ private void generateThingsType(SiemensHvacMetadata child, List Date: Wed, 3 Jan 2024 15:07:59 +0100 Subject: [PATCH 122/214] fix some typo Signed-off-by: Laurent ARNAL --- .../handler/SiemensHvacOZW672BridgeThingHandler.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java index 536f1c452ef11..ea62413fc20f1 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java @@ -34,8 +34,7 @@ /** * The {@link SiemensHvacOZW672BridgeThingHandler} is responsible for handling communication to Siemens Gateway using - * HTTP API - * interface. + * HTTP API interface. * * @author Laurent ARNAL - Initial contribution */ @@ -52,7 +51,9 @@ public SiemensHvacOZW672BridgeThingHandler(Bridge bridge, @Nullable NetworkAddre @Override public void initialize() { - logger.debug("Initialize() bridge : {}", getBuildDate()); + if (logger.isDebugEnabled()) { + logger.debug("Initialize() bridge : {}", getBuildDate()); + } super.initialize(); } @@ -70,9 +71,8 @@ private String getBuildDate() { } } catch (Exception ex) { - } - return "unknow"; + return "unknown"; } @Override From 045d98a096c03439619382294b849d18dc417722 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 3 Jan 2024 15:09:15 +0100 Subject: [PATCH 123/214] fix initializer Signed-off-by: Laurent ARNAL --- .../internal/handler/SiemensHvacBridgeConfig.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java index 52de1ba49df2d..aafeea01f5fce 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java @@ -13,7 +13,6 @@ package org.openhab.binding.siemenshvac.internal.handler; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; /** * @@ -22,7 +21,7 @@ @NonNullByDefault public class SiemensHvacBridgeConfig { - public @Nullable String baseUrl; - public @Nullable String userName; - public @Nullable String userPassword; + public String baseUrl = ""; + public String userName = "Administrator"; + public String userPassword = "password"; } From 0c5efd8c12b1bea129316ea2287b1d3d26fa0b6d Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 3 Jan 2024 15:11:19 +0100 Subject: [PATCH 124/214] fix some status update fix some debug clause review some if instance of clause Signed-off-by: Laurent ARNAL --- .../handler/SiemensHvacHandlerImpl.java | 52 +++++++------------ 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index b2fcde9f159c9..759d52a6704ef 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -100,12 +100,11 @@ public void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nu @Override public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) { - logger.info("bridge status changed : {} ", bridgeStatusInfo); } @Override public void initialize() { - updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NOT_YET_READY); + updateStatus(ThingStatus.UNKNOWN); pollingJob = scheduler.scheduleWithFixedDelay(this::pollingCode, 0, 5, TimeUnit.SECONDS); } @@ -114,6 +113,7 @@ public void dispose() { ScheduledFuture lcPollingJob = pollingJob; if (lcPollingJob != null) { lcPollingJob.cancel(true); + pollingJob = null; } } @@ -125,20 +125,15 @@ private void pollingCode() { } if (lcBridge.getStatus() == ThingStatus.OFFLINE) { - logger.debug("Bridge is offline, change thing status to offline!"); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "Bridge is offline"); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); return; } if (lcBridge.getStatus() != ThingStatus.ONLINE) { - logger.debug("Bridge is not ready, don't enter pooling for now!"); + logger.debug("Bridge is not ready, don't enter polling for now!"); return; } - if (getThing().getStatus() != ThingStatus.OFFLINE) { - updateStatus(ThingStatus.ONLINE); - } - long start = System.currentTimeMillis(); var chList = this.getThing().getChannels(); @@ -183,7 +178,6 @@ private void pollingCode() { } lcHvacConnector.displayRequestStats(); - } } @@ -199,7 +193,7 @@ private void readChannel(Channel channel) { return; } - logger.debug("readChannel : {}", channel.getDescription()); + logger.debug("readChannel: {}", channel.getDescription()); ChannelType tp = channelTypeRegistry.getChannelType(channel.getChannelTypeUID()); @@ -216,11 +210,11 @@ private void readChannel(Channel channel) { } if (id == null) { - logger.debug("pollingCode : Id is null {} ", channel); + logger.debug("pollingCode: Id is null {} ", channel); return; } if (type == null) { - logger.debug("pollingCode : type is null {} ", channel); + logger.debug("pollingCode: type is null {} ", channel); return; } readDp(id, uid, type, true); @@ -312,8 +306,8 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { logger.trace("End read : {}", dp); - if (response instanceof JsonObject) { - decodeReadDp((JsonObject) response, uid, dp, type); + if (response instanceof JsonObject jsonResponse) { + decodeReadDp(jsonResponse, uid, dp, type); } } }); @@ -328,7 +322,7 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { logger.debug("siemensHvac:ReadDp:Error during dp reading: {} ; {}", dp, e.getLocalizedMessage()); // Reset sessionId so we redone _auth on error } finally { - logger.trace("End read : {}", dp); + logger.trace("End read: {}", dp); lockObj.unlock(); } } @@ -352,15 +346,12 @@ private void writeDp(String dp, Type dpVal, String type) { String valUpdate = "0"; String valUpdateEnum = ""; - if (dpVal instanceof PercentType) { - PercentType pct = (PercentType) dpVal; - valUpdate = pct.toString(); - } else if (dpVal instanceof DecimalType) { - DecimalType bdc = (DecimalType) dpVal; - valUpdate = bdc.toString(); - } else if (dpVal instanceof StringType) { - StringType bdc = (StringType) dpVal; - valUpdate = bdc.toString(); + if (dpVal instanceof PercentType percentValue) { + valUpdate = percentValue.toString(); + } else if (dpVal instanceof DecimalType decimalValue) { + valUpdate = decimalValue.toString(); + } else if (dpVal instanceof StringType stringValue) { + valUpdate = stringValue.toString(); if ("Enumeration".equals(type)) { String[] valuesUpdateDp = valUpdate.split(":"); @@ -399,9 +390,8 @@ private Command applyState(ChannelType tp, Command command) { BigDecimal step = sd.getStep(); boolean doMods = false; - if (command instanceof DecimalType) { - DecimalType bdc = (DecimalType) command; - double v1 = bdc.doubleValue(); + if (command instanceof DecimalType decimalCommand) { + double v1 = decimalCommand.doubleValue(); if (step != null) { doMods = true; @@ -474,11 +464,9 @@ public void handleCommand(ChannelUID channelUID, Command command) { } } - if (command instanceof State) { + if (command instanceof State commandState) { commandVar = applyState(tp, commandVar); - State state = (State) commandVar; - - this.updateState(channelUID, state); + this.updateState(channelUID, commandState); } if (id != null && type != null) { From 83907627752408127dca6ce92ad01459e4aaa538 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 3 Jan 2024 15:16:40 +0100 Subject: [PATCH 125/214] remove some unneed code Signed-off-by: Laurent ARNAL --- .../handler/SiemensHvacBridgeBaseThingHandler.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java index 26d2e9362dc75..10c2676f0e609 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -138,12 +138,6 @@ private void initializeCode() { @Override public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) { - logger.info("bridge status changed : {} ", bridgeStatusInfo); - } - - @Override - public void updateStatus(ThingStatus status) { - super.updateStatus(status); } @Override @@ -165,7 +159,7 @@ public boolean registerDiscoveryListener(SiemensHvacDeviceDiscoveryService liste public boolean unregisterDiscoveryListener() { SiemensHvacDeviceDiscoveryService lcDiscoveryService = discoveryService; if (lcDiscoveryService != null) { - lcDiscoveryService = null; + discoveryService = null; return true; } From ca46b78640c258ff8e4a2bec2883c63e30d6ba7f Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 3 Jan 2024 15:18:44 +0100 Subject: [PATCH 126/214] fix map intialization fix if instanceof condition Signed-off-by: Laurent ARNAL --- .../discovery/SiemensHvacDeviceDiscoveryService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java index 8e19efe0ec2bc..5de0deca32658 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java @@ -127,7 +127,7 @@ public void startScan() { ThingUID bridgeUID = lcSiemensHvacBridgeHandler.getThing().getUID(); if (thingUID != null) { - Map properties = new HashMap<>(1); + Map properties = new HashMap<>(4); properties.put("name", name); properties.put("type", type); properties.put("addr", addr); @@ -151,8 +151,8 @@ protected synchronized void stopScan() { @Override public void setThingHandler(@Nullable ThingHandler handler) { - if (handler instanceof SiemensHvacBridgeBaseThingHandler) { - siemensHvacBridgeHandler = (SiemensHvacBridgeBaseThingHandler) handler; + if (handler instanceof SiemensHvacBridgeBaseThingHandler siemensHvacBridgeHandler) { + this.siemensHvacBridgeHandler = siemensHvacBridgeHandler; } } From f60e35d49b7e7993a8f9d93d5e3ef220f2ff794b Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 3 Jan 2024 15:20:08 +0100 Subject: [PATCH 127/214] fix location on readme Signed-off-by: Laurent ARNAL --- bundles/org.openhab.binding.siemenshvac/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/README.md b/bundles/org.openhab.binding.siemenshvac/README.md index c2e5a660e103d..77c43a93ef38a 100644 --- a/bundles/org.openhab.binding.siemenshvac/README.md +++ b/bundles/org.openhab.binding.siemenshvac/README.md @@ -84,9 +84,9 @@ Regime | Number | Enumeration Things file `.things` ```java -Bridge siemenshvac:ozw672:local "Ozw672"@"Chaufferie" [ baseUrl="https://192.168.254.42/", userName="Administrator", userPassword="mypass" ] +Bridge siemenshvac:ozw672:local "Ozw672" [ baseUrl="https://192.168.254.42/", userName="Administrator", userPassword="mypass" ] { - Thing RVS41_813_327 local "RVS41.813/327" @ "Chaudiere" [ ] + Thing RVS41_813_327 local "RVS41.813/327" [ ] { Channels: Type Setpoint:temperature "Temperature" [ id="1726" ] From 54d97ac42e2254d75bf6e5799e7d004acb5f5c52 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 3 Jan 2024 15:21:55 +0100 Subject: [PATCH 128/214] fix comment Signed-off-by: Laurent ARNAL --- .../internal/discovery/SiemenesHvacDiscoveryParticipant.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java index 64580dd1b152e..e4e653ccfe3b7 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java @@ -27,6 +27,7 @@ import org.openhab.core.config.discovery.DiscoveryResult; import org.openhab.core.config.discovery.DiscoveryResultBuilder; import org.openhab.core.config.discovery.upnp.UpnpDiscoveryParticipant; +import org.openhab.core.config.discovery.upnp.internal.UpnpDiscoveryService; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; import org.osgi.service.component.annotations.Activate; @@ -34,8 +35,8 @@ import org.osgi.service.component.annotations.Modified; /** - * The {@link SiemenesHvacDiscoveryParticipant} is responsible for discovering new and - * removed siemensHvac bridges. It uses the central {@link UpnpDiscoveryServic}. + * The {@link SiemensHvacDiscoveryParticipant} is responsible for discovering new and + * removed siemensHvac bridges. It uses the central {@link UpnpDiscoveryService}. * * @author Laurent Arnal - Initial contribution */ From fb2131855ad7bb53a6548f382dcc1f72109fba29 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 3 Jan 2024 15:26:09 +0100 Subject: [PATCH 129/214] fix header comments ! Signed-off-by: Laurent ARNAL --- .../internal/constants/SiemensHvacBindingConstants.java | 2 +- .../internal/discovery/SiemenesHvacDiscoveryParticipant.java | 2 +- .../internal/discovery/SiemensHvacDeviceDiscoveryService.java | 2 +- .../internal/factory/SiemensHvacHandlerFactory.java | 2 +- .../internal/handler/SiemensHvacBridgeBaseThingHandler.java | 2 +- .../siemenshvac/internal/handler/SiemensHvacBridgeConfig.java | 2 +- .../siemenshvac/internal/handler/SiemensHvacHandlerImpl.java | 2 +- .../internal/handler/SiemensHvacOZW672BridgeThingHandler.java | 2 +- .../internal/metadata/RuntimeTypeAdapterFactory.java | 2 +- .../siemenshvac/internal/metadata/SiemensHvacMetadata.java | 2 +- .../internal/metadata/SiemensHvacMetadataDataPoint.java | 2 +- .../internal/metadata/SiemensHvacMetadataDevice.java | 2 +- .../internal/metadata/SiemensHvacMetadataLanguage.java | 2 +- .../internal/metadata/SiemensHvacMetadataMenu.java | 2 +- .../internal/metadata/SiemensHvacMetadataPointChild.java | 2 +- .../internal/metadata/SiemensHvacMetadataRegistry.java | 2 +- .../internal/metadata/SiemensHvacMetadataRegistryImpl.java | 2 +- .../internal/metadata/SiemensHvacMetadataUser.java | 2 +- .../siemenshvac/internal/network/SiemensHvacCallback.java | 2 +- .../siemenshvac/internal/network/SiemensHvacConnector.java | 2 +- .../internal/network/SiemensHvacConnectorImpl.java | 2 +- .../internal/network/SiemensHvacRequestHandler.java | 2 +- .../internal/network/SiemensHvacRequestListener.java | 2 +- .../internal/type/SiemensHvacChannelGroupTypeProvider.java | 2 +- .../type/SiemensHvacChannelGroupTypeProviderImpl.java | 2 +- .../internal/type/SiemensHvacChannelTypeProvider.java | 4 ++-- .../internal/type/SiemensHvacChannelTypeProviderImpl.java | 2 +- .../internal/type/SiemensHvacConfigDescriptionProvider.java | 2 +- .../type/SiemensHvacConfigDescriptionProviderImpl.java | 2 +- .../siemenshvac/internal/type/SiemensHvacException.java | 2 +- .../internal/type/SiemensHvacThingTypeProvider.java | 2 +- .../internal/type/SiemensHvacThingTypeProviderImpl.java | 2 +- .../openhab/binding/siemenshvac/internal/type/UidUtils.java | 2 +- .../binding/siemenshvac/internal/type/UidUtilsTest.java | 2 +- 34 files changed, 35 insertions(+), 35 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java index dd2349983ceb8..3d15e81b3d09e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java index e4e653ccfe3b7..f67e5c40770a2 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java index 5de0deca32658..f76b19d66aaac 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java index 537f4d0db9d19..0ab3c22956804 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java index 10c2676f0e609..3afb2fb2a1265 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java index aafeea01f5fce..2025f17454079 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index 759d52a6704ef..8ea1daa550de4 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java index ea62413fc20f1..8704df31e024b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/RuntimeTypeAdapterFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/RuntimeTypeAdapterFactory.java index 42f7598fb0f54..f9deedf8cbbda 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/RuntimeTypeAdapterFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/RuntimeTypeAdapterFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadata.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadata.java index b9c1609a01675..d0597dae0cc7a 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadata.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadata.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataDataPoint.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataDataPoint.java index 444625ad0aecf..51b4a8e4f17f4 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataDataPoint.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataDataPoint.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataDevice.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataDevice.java index 592b02fe9ccf0..c9df927434097 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataDevice.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataDevice.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataLanguage.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataLanguage.java index c4cc5b6b53995..56153f820343e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataLanguage.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataLanguage.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataMenu.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataMenu.java index 82df1e9590b95..4b48b637117e8 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataMenu.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataMenu.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataPointChild.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataPointChild.java index 6e27e4dfc1b33..7c449307fb4f1 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataPointChild.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataPointChild.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistry.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistry.java index ee6ed7c18926b..51ac9f87ea783 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistry.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistry.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java index 8850ac4c6670a..4c31ae0ebc021 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataUser.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataUser.java index d83c3a2e68328..64647fdc53300 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataUser.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataUser.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacCallback.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacCallback.java index 3918d8e79ec4a..32766a7854fea 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacCallback.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacCallback.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java index 39556331a4c9a..4df996fc8cb63 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 0d10152836153..b378d8c3cc1f1 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestHandler.java index 94edb4ce33289..6c14de97aefd2 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java index 936cdd1df7d69..db5cf1c7afec2 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java index 924aa37642330..a6489bcc13437 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProvider.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java index 152cbcb437885..364a631199e6b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelGroupTypeProviderImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java index a6ee41c8a3c3e..6f5e16e9dc4e9 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProvider.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -33,7 +33,7 @@ public interface SiemensHvacChannelTypeProvider extends ChannelTypeProvider { /** * Use this method to lookup a ChannelType which was generated by the siemensHvac binding. - * + * * @param channelTypeUID * @return ChannelType that was added to SiemensHvacChannelTypeProvider, identified by its * config-description-uri
diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java index 41881a20d33cc..234bd4acfe94f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacChannelTypeProviderImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java index d70c4de1724d0..a16ec5f92d42a 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProvider.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProviderImpl.java index e8499fa918661..79cfb5a31eb09 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProviderImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacConfigDescriptionProviderImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacException.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacException.java index 9bf259caf90c2..080fc350487c5 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacException.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacException.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java index 5649257f861b9..5fac0e0e78a4f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProvider.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProviderImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProviderImpl.java index 315ff75e19a43..b8895e74efc1c 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProviderImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/SiemensHvacThingTypeProviderImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index db8c1893c71a4..c40be24337624 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java b/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java index 0a46007345805..6f1457f6641af 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java +++ b/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. From 7b49e4d84d308a16b8d80dd068abd8194c9c16af Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 3 Jan 2024 15:31:37 +0100 Subject: [PATCH 130/214] fix some compilations errors Signed-off-by: Laurent ARNAL --- .../internal/network/SiemensHvacConnectorImpl.java | 2 +- .../internal/network/SiemensHvacRequestListener.java | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index b378d8c3cc1f1..5923dca390483 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -474,7 +474,7 @@ public void CheckStaleRequest() { logger.debug("check stale request::begin"); int staleRequest = 0; for (SiemensHvacRequestHandler handler : currentHandlerRegistry.keySet()) { - long elapseTime = handler.getElapseTime(); + long elapseTime = handler.getElapsedTime(); if (elapseTime > 300) { String uri = ""; Request request = handler.getRequest(); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java index db5cf1c7afec2..6c6201376a871 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java @@ -119,15 +119,15 @@ public void onFailure(@Nullable Response response, @Nullable Throwable failure) String msg = cause.getLocalizedMessage(); - if (cause instanceof ConnectException) { + if (cause instanceof ConnectException e) { logger.debug("ConnectException", e); - } else if (cause instanceof SocketException) { + } else if (cause instanceof SocketException e) { logger.debug("SocketException", e); - } else if (cause instanceof SocketTimeoutException.) { + } else if (cause instanceof SocketTimeoutException e) { logger.debug("SocketTimeoutException"); - } else if (cause instanceof EOFException) { + } else if (cause instanceof EOFException e) { logger.debug("EOFException", e); - } else if (cause instanceof TimeoutException) { + } else if (cause instanceof TimeoutException e) { logger.debug("TimeoutException", e); } else { logger.debug("unknown"); From 5c8cd17fc9bb138a0d769da2d4a453fbeedefcf8 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 4 Jan 2024 09:15:30 +0100 Subject: [PATCH 131/214] remove unused method displayRequestStats() Signed-off-by: Laurent ARNAL --- .../handler/SiemensHvacBridgeBaseThingHandler.java | 6 ++---- .../internal/network/SiemensHvacConnectorImpl.java | 2 -- .../network/SiemensHvacRequestListener.java | 13 ------------- 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java index 3afb2fb2a1265..bd31e79e91543 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -77,11 +77,9 @@ public void initialize() { SiemensHvacBridgeConfig lcConfig = getConfigAs(SiemensHvacBridgeConfig.class); String baseUrl = null; - if (lcConfig.baseUrl != null) { - baseUrl = lcConfig.baseUrl; - } + baseUrl = lcConfig.baseUrl; - if (baseUrl == null) { + if (baseUrl.isEmpty()) { logger.debug("baseUrl is mandatory on configuration !"); return; } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 5923dca390483..5739bb3915c0b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -142,8 +142,6 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacRequestHandl throw new SiemensHvacException("internalError : onError call with reqHandler == null"); } - // reqHandler.displayStats(); - if (reqHandler.getRetryCount() >= 1) { logger.info("unable to handle request, retryCount>5, cancel it"); unregisterRequestHandler(reqHandler); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java index 6c6201376a871..95aac68998d30 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java @@ -149,19 +149,6 @@ public void onBegin(@Nullable Request request) { requestHandler.setRequest(request); } - public void displayStats() { - SiemensHvacRequestListener.displayStats(logger); - } - - public static void displayStats(Logger logger) { - logger.info("DisplayStats :"); - logger.info(" onSuccessCount : {}", onSuccessCount); - logger.info(" onBeginCount : {}", onBeginCount); - logger.info(" onQueuedCount : {}", onQueuedCount); - logger.info(" onCompleteCount : {}", onCompleteCount); - logger.info(" onFailureCount : {}", onFailureCount); - } - @Override public void onComplete(@Nullable Result result) { onCompleteCount++; From 5bf50c3f9e500510baf952b8411dbc32963408c6 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 4 Jan 2024 09:19:43 +0100 Subject: [PATCH 132/214] fix exception handling Signed-off-by: Laurent ARNAL --- .../network/SiemensHvacRequestListener.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java index 95aac68998d30..cd7f35736db8e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java @@ -120,20 +120,18 @@ public void onFailure(@Nullable Response response, @Nullable Throwable failure) String msg = cause.getLocalizedMessage(); if (cause instanceof ConnectException e) { - logger.debug("ConnectException", e); + logger.debug("ConnectException during request : {} {}", response.getRequest().getURI(), msg, e); } else if (cause instanceof SocketException e) { - logger.debug("SocketException", e); + logger.debug("SocketException during request : {} {}", response.getRequest().getURI(), msg, e); } else if (cause instanceof SocketTimeoutException e) { - logger.debug("SocketTimeoutException"); + logger.debug("SocketTimeoutException during request : {} {}", response.getRequest().getURI(), msg, e); } else if (cause instanceof EOFException e) { - logger.debug("EOFException", e); + logger.debug("EOFException during request : {} {}", response.getRequest().getURI(), msg, e); } else if (cause instanceof TimeoutException e) { - logger.debug("TimeoutException", e); + logger.debug("TimeoutException during request : {} {}", response.getRequest().getURI(), msg, e); } else { - logger.debug("unknown"); + logger.debug("Response failed: {} {}", response.getRequest().getURI(), msg, failure); } - - logger.debug("response failed: {} {}", response.getRequest().getURI(), msg, failure); } } From 5378d6f5359e09927405dc7ec4c7b25ba70d6d2f Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 4 Jan 2024 09:29:37 +0100 Subject: [PATCH 133/214] review exception logging Signed-off-by: Laurent ARNAL --- .../internal/handler/SiemensHvacBridgeBaseThingHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java index bd31e79e91543..45f64e2a7d534 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -126,7 +126,7 @@ private void initializeCode() { updateStatus(ThingStatus.ONLINE); } catch (SiemensHvacException ex) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - String.format("Error occurs during gateway initialization: %s", getStackTrace(ex))); + String.format("Error occurs during gateway initialization: %s", ex.getMessage())); } } From 31f34b0f75fd3a1deaf88dcc47313554b9a56d0f Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 4 Jan 2024 09:35:11 +0100 Subject: [PATCH 134/214] removed unused ovveride Signed-off-by: Laurent ARNAL --- .../internal/handler/SiemensHvacBridgeBaseThingHandler.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java index 45f64e2a7d534..f1c3942a9e851 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -26,7 +26,6 @@ import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatusDetail; -import org.openhab.core.thing.ThingStatusInfo; import org.openhab.core.thing.binding.BaseBridgeHandler; import org.openhab.core.types.Command; import org.slf4j.Logger; @@ -134,10 +133,6 @@ private void initializeCode() { return config; } - @Override - public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) { - } - @Override public void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) { super.updateStatus(status, statusDetail, description); From f8d9f4a059f6030186b145904ebd3f586cdcc06d Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 4 Jan 2024 10:18:53 +0100 Subject: [PATCH 135/214] remove password logging in case of exception Signed-off-by: Laurent ARNAL --- .../network/SiemensHvacConnectorImpl.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 5739bb3915c0b..98643cbb76221 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -224,8 +224,8 @@ private void registerHandlerError(SiemensHvacRequestHandler handler) { response = request.send(); } } catch (InterruptedException | TimeoutException | ExecutionException e) { - throw new SiemensHvacException("siemensHvac:Exception by executing request: " + request.getURI() + " ; " - + e.getLocalizedMessage()); + throw new SiemensHvacException("siemensHvac:Exception by executing request: " + + anominized(request.getURI().toString()) + " ; " + e.getLocalizedMessage()); } return response; } @@ -479,7 +479,7 @@ public void CheckStaleRequest() { if (request != null) { uri = request.getURI().toString(); } - logger.debug("find stale request: {} {}", elapseTime, uri); + logger.debug("find stale request: {} {}", elapseTime, anominized(uri)); staleRequest++; try { @@ -494,6 +494,15 @@ public void CheckStaleRequest() { logger.debug("check stale request::end : {}", staleRequest); } + public String anominized(String uri) { + int p0 = uri.indexOf("pwd="); + if (p0 > 0) { + return uri.substring(0, p0) + "pwd=xxxxx"; + } + + return uri; + } + @Override public void waitNoNewRequest() { logger.debug("WaitNoNewRequest:start"); From 20e83735622583dc2d46a74f7e3b5fe8a4f85017 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 14 Jan 2024 16:07:35 +0100 Subject: [PATCH 136/214] fix concurrent hashmap access Signed-off-by: Laurent ARNAL --- .../network/SiemensHvacConnectorImpl.java | 112 +++++++++++++----- 1 file changed, 80 insertions(+), 32 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 98643cbb76221..cc2957cfca8a6 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -22,6 +22,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.ReentrantReadWriteLock; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -63,6 +64,9 @@ public class SiemensHvacConnectorImpl implements SiemensHvacConnector { private Map currentHandlerRegistry = new HashMap(); private Map handlerInErrorRegistry = new HashMap(); + + private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(); + private final Gson gson; private final Gson gsonWithAdapter; @@ -197,15 +201,25 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacRequestHandl } private void unregisterRequestHandler(SiemensHvacRequestHandler handler) throws SiemensHvacException { - if (!currentHandlerRegistry.containsKey(handler)) { - throw new SiemensHvacException("Internal error, try to unregister not registred handler: " + handler); - } + reentrantReadWriteLock.writeLock().lock(); + try { + if (!currentHandlerRegistry.containsKey(handler)) { + throw new SiemensHvacException("Internal error, try to unregister not registred handler: " + handler); + } - currentHandlerRegistry.remove(handler); + currentHandlerRegistry.remove(handler); + } finally { + reentrantReadWriteLock.writeLock().unlock(); + } } private void registerHandlerError(SiemensHvacRequestHandler handler) { - handlerInErrorRegistry.put(handler, handler); + reentrantReadWriteLock.writeLock().lock(); + try { + handlerInErrorRegistry.put(handler, handler); + } finally { + reentrantReadWriteLock.writeLock().unlock(); + } } private @Nullable ContentResponse executeRequest(final Request request, @@ -435,8 +449,8 @@ private void doAuth(boolean http) throws SiemensHvacException { @Override public void displayRequestStats() { logger.debug("DisplayRequestStats : "); - logger.debug(" currentRuning : {}", currentHandlerRegistry.keySet().size()); - logger.debug(" errors : {}", handlerInErrorRegistry.keySet().size()); + logger.debug(" currentRuning : {}", getCurrentHandlerRegistryCount()); + logger.debug(" errors : {}", getHandlerInErrorRegistryCount()); } @Override @@ -448,7 +462,9 @@ public void waitAllPendingRequest() { while (!allRequestDone) { allRequestDone = false; - int currentRequestCount = currentHandlerRegistry.keySet().size(); + reentrantReadWriteLock.readLock().lock(); + int currentRequestCount = getCurrentHandlerRegistryCount(); + logger.debug("WaitAllPendingRequest:waitAllRequestDone {} : {}", idx, currentRequestCount); if (currentRequestCount == 0) { @@ -469,29 +485,36 @@ public void waitAllPendingRequest() { } public void CheckStaleRequest() { - logger.debug("check stale request::begin"); - int staleRequest = 0; - for (SiemensHvacRequestHandler handler : currentHandlerRegistry.keySet()) { - long elapseTime = handler.getElapsedTime(); - if (elapseTime > 300) { - String uri = ""; - Request request = handler.getRequest(); - if (request != null) { - uri = request.getURI().toString(); - } - logger.debug("find stale request: {} {}", elapseTime, anominized(uri)); - staleRequest++; - - try { - unregisterRequestHandler(handler); - registerHandlerError(handler); - } catch (SiemensHvacException ex) { - logger.debug("error unregistring handler: {}", handler); - } + reentrantReadWriteLock.writeLock().lock(); + try { + logger.debug("check stale request::begin"); + int staleRequest = 0; + for (SiemensHvacRequestHandler handler : currentHandlerRegistry.keySet()) { + long elapseTime = handler.getElapsedTime(); + if (elapseTime > 300) { + String uri = ""; + Request request = handler.getRequest(); + if (request != null) { + uri = request.getURI().toString(); + } + logger.debug("find stale request: {} {}", elapseTime, anominized(uri)); + staleRequest++; + + try { + unregisterRequestHandler(handler); + registerHandlerError(handler); + } catch (SiemensHvacException ex) { + logger.debug("error unregistring handler: {}", handler); + } + } } + + logger.debug("check stale request::end : {}", staleRequest); + } finally { + reentrantReadWriteLock.writeLock().unlock(); } - logger.debug("check stale request::end : {}", staleRequest); + } public String anominized(String uri) { @@ -503,15 +526,33 @@ public String anominized(String uri) { return uri; } + private int getCurrentHandlerRegistryCount() { + reentrantReadWriteLock.readLock().lock(); + try { + return currentHandlerRegistry.keySet().size(); + } finally { + reentrantReadWriteLock.readLock().unlock(); + } + } + + private int getHandlerInErrorRegistryCount() { + reentrantReadWriteLock.readLock().lock(); + try { + return handlerInErrorRegistry.keySet().size(); + } finally { + reentrantReadWriteLock.readLock().unlock(); + } + } + @Override public void waitNoNewRequest() { logger.debug("WaitNoNewRequest:start"); try { - int lastRequestCount = currentHandlerRegistry.keySet().size(); + int lastRequestCount = getCurrentHandlerRegistryCount(); boolean newRequest = true; while (newRequest) { Thread.sleep(5000); - int newRequestCount = currentHandlerRegistry.keySet().size(); + int newRequestCount = getCurrentHandlerRegistryCount(); if (newRequestCount != lastRequestCount) { logger.debug("waitNoNewRequest {}/{})", newRequestCount, lastRequestCount); lastRequestCount = newRequestCount; @@ -570,8 +611,15 @@ public SiemensHvacRequestListener.ErrorSource getErrorSource() { public void invalidate() { sessionId = null; sessionIdHttp = null; - currentHandlerRegistry.clear(); - handlerInErrorRegistry.clear(); + + reentrantReadWriteLock.writeLock().lock(); + try { + currentHandlerRegistry.clear(); + handlerInErrorRegistry.clear(); + } finally { + reentrantReadWriteLock.writeLock().unlock(); + } + } @Override From 5c888a5f244346e2e5114dba8a1a15b871e116e8 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 14 Jan 2024 16:11:27 +0100 Subject: [PATCH 137/214] mvn spotless:apply Signed-off-by: Laurent ARNAL --- .../siemenshvac/internal/network/SiemensHvacConnectorImpl.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index cc2957cfca8a6..1a44b7884649b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -514,7 +514,6 @@ public void CheckStaleRequest() { } finally { reentrantReadWriteLock.writeLock().unlock(); } - } public String anominized(String uri) { @@ -619,7 +618,6 @@ public void invalidate() { } finally { reentrantReadWriteLock.writeLock().unlock(); } - } @Override From 3904abacd32b96939f8c4719d6974d195c9bbdca Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 16 Jan 2024 15:44:27 +0100 Subject: [PATCH 138/214] fix unbalanced locking request Signed-off-by: Laurent ARNAL --- .../siemenshvac/internal/network/SiemensHvacConnectorImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 1a44b7884649b..56e8f833dd4df 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -462,7 +462,6 @@ public void waitAllPendingRequest() { while (!allRequestDone) { allRequestDone = false; - reentrantReadWriteLock.readLock().lock(); int currentRequestCount = getCurrentHandlerRegistryCount(); logger.debug("WaitAllPendingRequest:waitAllRequestDone {} : {}", idx, currentRequestCount); From e293f618ec2b2f9fce387baef15879786095be61 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 17 Jan 2024 12:05:12 +0100 Subject: [PATCH 139/214] review error handling on gateway after new bug reporting from Vasek: - Better checking session invalidation. - Fix try/retry mechanism to use good sessionId - Synchronize doAuth to prevent multiple session opening from different threads. - Differentiate network errors from logic errors on retry mecanishm. - Fix ConcurrentModicationException on registerHandler - Automatic recovering from COMMUNICATION_ERROR on next pooling interval. - Lower CheckStaleRequest from 300s to 150s. Signed-off-by: Laurent ARNAL --- .../handler/SiemensHvacHandlerImpl.java | 25 +- .../SiemensHvacMetadataRegistryImpl.java | 4 +- .../network/SiemensHvacConnector.java | 4 +- .../network/SiemensHvacConnectorImpl.java | 246 +++++++++++------- .../network/SiemensHvacRequestListener.java | 34 ++- 5 files changed, 190 insertions(+), 123 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index 8ea1daa550de4..4b19819d4a8be 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -125,13 +125,17 @@ private void pollingCode() { } if (lcBridge.getStatus() == ThingStatus.OFFLINE) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); - return; + if (!ThingStatusDetail.COMMUNICATION_ERROR.equals(lcBridge.getStatusInfo().getStatusDetail())) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); + return; + } } if (lcBridge.getStatus() != ThingStatus.ONLINE) { - logger.debug("Bridge is not ready, don't enter polling for now!"); - return; + if (!ThingStatusDetail.COMMUNICATION_ERROR.equals(lcBridge.getStatusInfo().getStatusDetail())) { + logger.debug("Bridge is not ready, don't enter polling for now!"); + return; + } } long start = System.currentTimeMillis(); @@ -160,7 +164,7 @@ private void pollingCode() { double errorRate = (double) errorCount / requestCount * 100.00; - if (errorRate > 20) { + if (errorRate > 50) { SiemensHvacBridgeBaseThingHandler bridgeHandler = (SiemensHvacBridgeBaseThingHandler) lcBridge .getHandler(); @@ -175,6 +179,17 @@ private void pollingCode() { } } else { updateStatus(ThingStatus.ONLINE); + + SiemensHvacBridgeBaseThingHandler bridgeHandler = (SiemensHvacBridgeBaseThingHandler) lcBridge + .getHandler(); + + // Automatically recover from communication errors if errorRate is ok. + if (bridgeHandler != null) { + if (ThingStatusDetail.COMMUNICATION_ERROR + .equals(bridgeHandler.getThing().getStatusInfo().getStatusDetail())) { + bridgeHandler.updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, ""); + } + } } lcHvacConnector.displayRequestStats(); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java index 4c31ae0ebc021..c4bf28d19e8ee 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -783,8 +783,8 @@ public void changeLanguage(SiemensHvacMetadataUser user, int lang) { request = request + "&language=" + lang + "&submit=OK"; if (lcHvacConnector != null) { lcHvacConnector.doBasicRequest(request); - lcHvacConnector.resetSessionId(false); - lcHvacConnector.resetSessionId(true); + lcHvacConnector.resetSessionId(null, false); + lcHvacConnector.resetSessionId(null, true); } } catch ( diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java index 4df996fc8cb63..34a5a8a119479 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java @@ -43,14 +43,14 @@ public interface SiemensHvacConnector { void onComplete(Request request, SiemensHvacRequestHandler reqListener) throws Exception; void onError(Request request, SiemensHvacRequestHandler reqListener, - SiemensHvacRequestListener.ErrorSource errorSource) throws Exception; + SiemensHvacRequestListener.ErrorSource errorSource, boolean mayRetry) throws Exception; void setSiemensHvacBridgeBaseThingHandler(SiemensHvacBridgeBaseThingHandler hvacBridgeBaseThingHandler); @Nullable SiemensHvacBridgeConfig getBridgeConfiguration(); - void resetSessionId(boolean web); + void resetSessionId(@Nullable String sessionIdToInvalidate, boolean web); void displayRequestStats(); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 56e8f833dd4df..c7aa8c1e7b39d 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -19,10 +19,10 @@ import java.util.Hashtable; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.concurrent.locks.ReentrantReadWriteLock; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -62,10 +62,10 @@ public class SiemensHvacConnectorImpl implements SiemensHvacConnector { private final Logger logger = LoggerFactory.getLogger(SiemensHvacConnectorImpl.class); - private Map currentHandlerRegistry = new HashMap(); - private Map handlerInErrorRegistry = new HashMap(); + private Map currentHandlerRegistry = new ConcurrentHashMap(); + private Map handlerInErrorRegistry = new ConcurrentHashMap(); - private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(); + private Map oldSessionId = new HashMap(); private final Gson gson; private final Gson gsonWithAdapter; @@ -139,15 +139,37 @@ public void onComplete(@Nullable Request request, SiemensHvacRequestHandler reqH unregisterRequestHandler(reqHandler); } + public static String extractSessionId(String query) { + int idx1 = query.indexOf("SessionId="); + int idx2 = query.indexOf("&", idx1 + 1); + if (idx2 < 0) { + idx2 = query.length(); + } + + String sessionId = query.substring(idx1 + 10, idx2); + return sessionId; + } + @Override public void onError(@Nullable Request request, @Nullable SiemensHvacRequestHandler reqHandler, - SiemensHvacRequestListener.ErrorSource errorSource) throws Exception { + SiemensHvacRequestListener.ErrorSource errorSource, boolean mayRetry) throws Exception { if (reqHandler == null || request == null) { throw new SiemensHvacException("internalError : onError call with reqHandler == null"); } - if (reqHandler.getRetryCount() >= 1) { - logger.info("unable to handle request, retryCount>5, cancel it"); + boolean doRetry = mayRetry; + // Don't retry if we have do it multiple time + if (reqHandler.getRetryCount() >= 5) { + doRetry = false; + } + + // Don't retry if we lost session, just abort the request, and wait next loop + if (sessionIdHttp == null || sessionId == null) { + doRetry = false; + } + + if (!doRetry) { + logger.info("unable to handle request, doRetry = false, cancel it"); unregisterRequestHandler(reqHandler); registerHandlerError(reqHandler); errorCount++; @@ -167,7 +189,25 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacRequestHandl } try { - final Request retryRequest = httpClient.newRequest(request.getURI()); + URI uri = request.getURI(); + String query = uri.toString(); + + String sessionIdInQuery = extractSessionId(query); + if (query.indexOf("main.app") >= 0) { + String sessionIdHttpLc = sessionIdHttp; + + if (sessionIdHttpLc != null && !sessionIdHttpLc.equals(sessionIdInQuery)) { + uri = new URI(query.replace(sessionIdInQuery, sessionIdHttpLc)); + } + } else { + String sessionIdLc = sessionId; + + if (sessionIdLc != null && !sessionIdLc.equals(sessionIdInQuery)) { + uri = new URI(query.replace(sessionIdInQuery, sessionIdLc)); + } + } + + final Request retryRequest = httpClient.newRequest(uri); request.method(HttpMethod.GET); reqHandler.setRequest(retryRequest); reqHandler.incrementRetryCount(); @@ -175,7 +215,9 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacRequestHandl if (retryRequest != null) { executeRequest(retryRequest, reqHandler); } - } catch (Exception ex) { + } catch ( + + Exception ex) { logger.debug("Error during gateway request:", ex); throw ex; } @@ -201,24 +243,19 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacRequestHandl } private void unregisterRequestHandler(SiemensHvacRequestHandler handler) throws SiemensHvacException { - reentrantReadWriteLock.writeLock().lock(); - try { + synchronized (currentHandlerRegistry) { + if (!currentHandlerRegistry.containsKey(handler)) { throw new SiemensHvacException("Internal error, try to unregister not registred handler: " + handler); } currentHandlerRegistry.remove(handler); - } finally { - reentrantReadWriteLock.writeLock().unlock(); } } private void registerHandlerError(SiemensHvacRequestHandler handler) { - reentrantReadWriteLock.writeLock().lock(); - try { + synchronized (handlerInErrorRegistry) { handlerInErrorRegistry.put(handler, handler); - } finally { - reentrantReadWriteLock.writeLock().unlock(); } } @@ -261,93 +298,95 @@ private void initConfig() throws SiemensHvacException { } private void doAuth(boolean http) throws SiemensHvacException { - logger.debug("siemensHvac:doAuth()"); - - initConfig(); - - SiemensHvacBridgeConfig config = this.config; - if (config == null) { - throw new SiemensHvacException("Missing SiemensHvacOZW672 Bridge configuration"); - } - - String baseUri = config.baseUrl; - String uri = ""; + synchronized (this) { + logger.debug("siemensHvac:doAuth()"); - if (http) { - uri = "main.app"; - } else { - uri = String.format("api/auth/login.json?user=%s&pwd=%s", config.userName, config.userPassword); - } + initConfig(); - final Request request = httpClient.newRequest(baseUri + uri); - if (http) { - request.method(HttpMethod.POST).param("user", config.userName).param("pwd", config.userPassword); - } else { - request.method(HttpMethod.GET); - } + SiemensHvacBridgeConfig config = this.config; + if (config == null) { + throw new SiemensHvacException("Missing SiemensHvacOZW672 Bridge configuration"); + } - logger.debug("siemensHvac:doAuth:connect()"); + String baseUri = config.baseUrl; + String uri = ""; - try { - ContentResponse response = executeRequest(request); - if (response != null) { - int statusCode = response.getStatus(); + if (http) { + uri = "main.app"; + } else { + uri = String.format("api/auth/login.json?user=%s&pwd=%s", config.userName, config.userPassword); + } - if (statusCode == HttpStatus.OK_200) { - String result = response.getContentAsString(); + final Request request = httpClient.newRequest(baseUri + uri); + if (http) { + request.method(HttpMethod.POST).param("user", config.userName).param("pwd", config.userPassword); + } else { + request.method(HttpMethod.GET); + } - if (http) { - CookieStore cookieStore = httpClient.getCookieStore(); - List cookies = cookieStore.getCookies(); + logger.debug("siemensHvac:doAuth:connect()"); - for (HttpCookie httpCookie : cookies) { - if (httpCookie.getName().equals("SessionId")) { - sessionIdHttp = httpCookie.getValue(); - } + try { + ContentResponse response = executeRequest(request); + if (response != null) { + int statusCode = response.getStatus(); - } + if (statusCode == HttpStatus.OK_200) { + String result = response.getContentAsString(); - if (sessionIdHttp == null) { - logger.debug("Session request auth was unsuccessful in _doAuth()"); - } - } else { - if (result != null) { - JsonObject resultObj = getGson().fromJson(result, JsonObject.class); + if (http) { + CookieStore cookieStore = httpClient.getCookieStore(); + List cookies = cookieStore.getCookies(); - if (resultObj != null && resultObj.has("Result")) { - JsonElement resultVal = resultObj.get("Result"); - JsonObject resultObj2 = resultVal.getAsJsonObject(); + for (HttpCookie httpCookie : cookies) { + if (httpCookie.getName().equals("SessionId")) { + sessionIdHttp = httpCookie.getValue(); + } - if (resultObj2.has("Success")) { - boolean successVal = resultObj2.get("Success").getAsBoolean(); + } - if (successVal) { - if (resultObj.has("SessionId")) { - sessionId = resultObj.get("SessionId").getAsString(); - logger.debug("Have new SessionId: {} ", sessionId); + if (sessionIdHttp == null) { + logger.debug("Session request auth was unsuccessful in _doAuth()"); + } + } else { + if (result != null) { + JsonObject resultObj = getGson().fromJson(result, JsonObject.class); + + if (resultObj != null && resultObj.has("Result")) { + JsonElement resultVal = resultObj.get("Result"); + JsonObject resultObj2 = resultVal.getAsJsonObject(); + + if (resultObj2.has("Success")) { + boolean successVal = resultObj2.get("Success").getAsBoolean(); + + if (successVal) { + if (resultObj.has("SessionId")) { + sessionId = resultObj.get("SessionId").getAsString(); + logger.debug("Have new SessionId: {} ", sessionId); + } } } } - } - logger.debug("siemensHvac:doAuth:decodeResponse:()"); + logger.debug("siemensHvac:doAuth:decodeResponse:()"); + + if (sessionId == null) { + throw new SiemensHvacException( + "Session request auth was unsuccessful in _doAuth(), please verify login parameters"); + } - if (sessionId == null) { - throw new SiemensHvacException( - "Session request auth was unsuccessful in _doAuth(), please verify login parameters"); } } - } } - } - logger.trace("siemensHvac:doAuth:connect()"); + logger.trace("siemensHvac:doAuth:connect()"); - } catch (Exception ex) { - logger.debug("siemensHvac:doAuth:error() {}", ex.getLocalizedMessage()); - throw new SiemensHvacException("Error during authentification", ex); + } catch (Exception ex) { + logger.debug("siemensHvac:doAuth:error() {}", ex.getLocalizedMessage()); + throw new SiemensHvacException("Error during authentification", ex); + } } } @@ -471,7 +510,7 @@ public void waitAllPendingRequest() { } Thread.sleep(1000); - if ((idx % 60) == 0) { + if ((idx % 50) == 0) { CheckStaleRequest(); } idx++; @@ -484,13 +523,13 @@ public void waitAllPendingRequest() { } public void CheckStaleRequest() { - reentrantReadWriteLock.writeLock().lock(); - try { + synchronized (currentHandlerRegistry) { logger.debug("check stale request::begin"); int staleRequest = 0; + for (SiemensHvacRequestHandler handler : currentHandlerRegistry.keySet()) { long elapseTime = handler.getElapsedTime(); - if (elapseTime > 300) { + if (elapseTime > 150) { String uri = ""; Request request = handler.getRequest(); if (request != null) { @@ -510,8 +549,6 @@ public void CheckStaleRequest() { } logger.debug("check stale request::end : {}", staleRequest); - } finally { - reentrantReadWriteLock.writeLock().unlock(); } } @@ -525,20 +562,14 @@ public String anominized(String uri) { } private int getCurrentHandlerRegistryCount() { - reentrantReadWriteLock.readLock().lock(); - try { + synchronized (currentHandlerRegistry) { return currentHandlerRegistry.keySet().size(); - } finally { - reentrantReadWriteLock.readLock().unlock(); } } private int getHandlerInErrorRegistryCount() { - reentrantReadWriteLock.readLock().lock(); - try { + synchronized (handlerInErrorRegistry) { return handlerInErrorRegistry.keySet().size(); - } finally { - reentrantReadWriteLock.readLock().unlock(); } } @@ -582,11 +613,27 @@ public void addDpUpdate(String itemName, Type dp) { } @Override - public void resetSessionId(boolean web) { + public void resetSessionId(@Nullable String sessionIdToInvalidate, boolean web) { if (web) { - sessionIdHttp = null; + if (sessionIdToInvalidate == null) { + sessionIdHttp = null; + } else { + if (!oldSessionId.containsKey(sessionIdToInvalidate) && sessionIdToInvalidate.equals(sessionIdHttp)) { + oldSessionId.put(sessionIdToInvalidate, true); + logger.debug("Invalidate sessionIdHttp:" + sessionIdToInvalidate); + sessionIdHttp = null; + } + } } else { - sessionId = null; + if (sessionIdToInvalidate == null) { + sessionId = null; + } else { + if (!oldSessionId.containsKey(sessionIdToInvalidate) && sessionIdToInvalidate.equals(sessionId)) { + oldSessionId.put(sessionIdToInvalidate, true); + logger.debug("Invalidate sessionId:" + sessionIdToInvalidate); + sessionId = null; + } + } } } @@ -610,12 +657,9 @@ public void invalidate() { sessionId = null; sessionIdHttp = null; - reentrantReadWriteLock.writeLock().lock(); - try { + synchronized (currentHandlerRegistry) { currentHandlerRegistry.clear(); handlerInErrorRegistry.clear(); - } finally { - reentrantReadWriteLock.writeLock().unlock(); } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java index cd7f35736db8e..42ba28ee88e28 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java @@ -159,10 +159,11 @@ public void onComplete(@Nullable Result result) { try { String content = getContentAsString(); logger.trace("response complete: {}", content); + boolean mayRetry = true; if (result.getResponse().getStatus() != 200) { logger.debug("Error requesting gateway, non success code: {}", result.getResponse().getStatus()); - hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorBridge); + hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorBridge, mayRetry); return; } @@ -176,7 +177,7 @@ public void onComplete(@Nullable Result result) { Gson gson = hvacConnector.getGson(); resultObj = gson.fromJson(content, JsonObject.class); } catch (JsonSyntaxException ex) { - logger.debug("error: {}", ex.toString()); + logger.debug("error(1): {}", ex.toString()); } if (resultObj != null && resultObj.has("Result")) { @@ -191,8 +192,12 @@ public void onComplete(@Nullable Result result) { } if (errorMsg.indexOf("session") >= 0) { - hvacConnector.resetSessionId(false); - hvacConnector.resetSessionId(true); + String query = result.getRequest().getURI().getQuery(); + String sessionId = SiemensHvacConnectorImpl.extractSessionId(query); + + hvacConnector.resetSessionId(sessionId, false); + hvacConnector.resetSessionId(sessionId, true); + mayRetry = false; } if (resultVal) { @@ -207,28 +212,31 @@ public void onComplete(@Nullable Result result) { resultObj); return; } else if (("read failed").equals(errorMsg)) { - logger.debug("error: {}", subResultObj); - hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorThings); + logger.debug("error(2): {}", subResultObj); + hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorThings, + mayRetry); } else { - logger.debug("error: {}", subResultObj); - hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorBridge); + logger.debug("error(3): {}", subResultObj); + hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorBridge, + mayRetry); return; } } else { - logger.debug("error: invalid response from gateway, missing subResultObj:Success entry"); - hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorBridge); + logger.debug("error(4): invalid response from gateway, missing subResultObj:Success entry"); + hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorBridge, + mayRetry); return; } } else { - logger.debug("error: invalid response from gateway, missing Result entry"); - hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorBridge); + logger.debug("error(5): invalid response from gateway, missing Result entry"); + hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorBridge, mayRetry); return; } } } else { logger.debug("error: content == null"); - hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorBridge); + hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorBridge, mayRetry); return; } } catch (Exception ex) { From bb240a3cee83a18d4916fe66f73e425d72f09ef5 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 17 Jan 2024 12:12:31 +0100 Subject: [PATCH 140/214] fix logger.debug clause Signed-off-by: Laurent ARNAL --- .../internal/network/SiemensHvacConnectorImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index c7aa8c1e7b39d..766621ea8d596 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -620,7 +620,7 @@ public void resetSessionId(@Nullable String sessionIdToInvalidate, boolean web) } else { if (!oldSessionId.containsKey(sessionIdToInvalidate) && sessionIdToInvalidate.equals(sessionIdHttp)) { oldSessionId.put(sessionIdToInvalidate, true); - logger.debug("Invalidate sessionIdHttp:" + sessionIdToInvalidate); + logger.debug("Invalidate sessionIdHttp:", sessionIdToInvalidate); sessionIdHttp = null; } } @@ -630,7 +630,7 @@ public void resetSessionId(@Nullable String sessionIdToInvalidate, boolean web) } else { if (!oldSessionId.containsKey(sessionIdToInvalidate) && sessionIdToInvalidate.equals(sessionId)) { oldSessionId.put(sessionIdToInvalidate, true); - logger.debug("Invalidate sessionId:" + sessionIdToInvalidate); + logger.debug("Invalidate sessionId:", sessionIdToInvalidate); sessionId = null; } } From 166bfcee31c59c118139571d61e02479e056a18a Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 17 Jan 2024 12:12:31 +0100 Subject: [PATCH 141/214] fix logger.debug clause Signed-off-by: Laurent ARNAL --- .../internal/network/SiemensHvacConnectorImpl.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 766621ea8d596..3e502b1dfdfcb 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -620,7 +620,8 @@ public void resetSessionId(@Nullable String sessionIdToInvalidate, boolean web) } else { if (!oldSessionId.containsKey(sessionIdToInvalidate) && sessionIdToInvalidate.equals(sessionIdHttp)) { oldSessionId.put(sessionIdToInvalidate, true); - logger.debug("Invalidate sessionIdHttp:", sessionIdToInvalidate); + + logger.debug("Invalidate sessionIdHttp: {}", sessionIdToInvalidate); sessionIdHttp = null; } } @@ -630,7 +631,8 @@ public void resetSessionId(@Nullable String sessionIdToInvalidate, boolean web) } else { if (!oldSessionId.containsKey(sessionIdToInvalidate) && sessionIdToInvalidate.equals(sessionId)) { oldSessionId.put(sessionIdToInvalidate, true); - logger.debug("Invalidate sessionId:", sessionIdToInvalidate); + + logger.debug("Invalidate sessionId: {}", sessionIdToInvalidate); sessionId = null; } } From 27c0aae2db007de3d8da5678e7bd622a638d496b Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 17 Jan 2024 12:40:50 +0100 Subject: [PATCH 142/214] fix some checkStyle warnings Signed-off-by: Laurent ARNAL --- .../SiemensHvacMetadataRegistryImpl.java | 26 ++++++++++++------- .../network/SiemensHvacConnectorImpl.java | 5 ++-- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java index c4bf28d19e8ee..5381358473f5e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -1230,24 +1230,30 @@ public void execute(URI uri, int status, @Nullable Object response) { @Override public void invalidate() { root = null; - if (hvacConnector != null) { - hvacConnector.invalidate(); + SiemensHvacConnector lcHavConnector = hvacConnector; + SiemensHvacChannelGroupTypeProvider lcChannelGroupTypeProvider = channelGroupTypeProvider; + SiemensHvacThingTypeProvider lcThingTypeProvider = thingTypeProvider; + SiemensHvacChannelTypeProvider lcChannelTypeProvider = channelTypeProvider; + SiemensHvacConfigDescriptionProvider lcConfigDescriptionProvider = configDescriptionProvider; + + if (lcHavConnector != null) { + lcHavConnector.invalidate(); } - if (channelGroupTypeProvider != null) { - channelGroupTypeProvider.invalidate(); + if (lcChannelGroupTypeProvider != null) { + lcChannelGroupTypeProvider.invalidate(); } - if (thingTypeProvider != null) { - thingTypeProvider.invalidate(); + if (lcThingTypeProvider != null) { + lcThingTypeProvider.invalidate(); } - if (channelTypeProvider != null) { - channelTypeProvider.invalidate(); + if (lcChannelTypeProvider != null) { + lcChannelTypeProvider.invalidate(); } - if (configDescriptionProvider != null) { - configDescriptionProvider.invalidate(); + if (lcConfigDescriptionProvider != null) { + lcConfigDescriptionProvider.invalidate(); } } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 3e502b1dfdfcb..05d51321023de 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -244,7 +244,6 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacRequestHandl private void unregisterRequestHandler(SiemensHvacRequestHandler handler) throws SiemensHvacException { synchronized (currentHandlerRegistry) { - if (!currentHandlerRegistry.containsKey(handler)) { throw new SiemensHvacException("Internal error, try to unregister not registred handler: " + handler); } @@ -511,7 +510,7 @@ public void waitAllPendingRequest() { Thread.sleep(1000); if ((idx % 50) == 0) { - CheckStaleRequest(); + checkStaleRequest(); } idx++; } @@ -522,7 +521,7 @@ public void waitAllPendingRequest() { logger.debug("WaitAllPendingRequest:end WaitAllPendingRequest"); } - public void CheckStaleRequest() { + public void checkStaleRequest() { synchronized (currentHandlerRegistry) { logger.debug("check stale request::begin"); int staleRequest = 0; From 1f92cebf976451a9101c452140a24d4630e758a0 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 17 Jan 2024 14:12:56 +0100 Subject: [PATCH 143/214] capitalized Hvac Signed-off-by: Laurent ARNAL --- bundles/org.openhab.binding.siemenshvac/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/README.md b/bundles/org.openhab.binding.siemenshvac/README.md index 77c43a93ef38a..ef5cf63df1bd2 100644 --- a/bundles/org.openhab.binding.siemenshvac/README.md +++ b/bundles/org.openhab.binding.siemenshvac/README.md @@ -1,10 +1,10 @@ -# SiemensHvac Binding +# SiemensHVAC Binding -This binding provides support for the Siemens Hvac controller ecosystem, and the Web Gateway interface OZW672. +This binding provides support for the Siemens HVAC controller ecosystem, and the Web Gateway interface OZW672. A typical system is composed of: ![Diagram](doc/Diagram.png) - + There's a lot of different HVAC controllers depending on model in lot of different PAC constructors. Siemens RVS41.813/327 inside a Atlantic Hybrid Duo was used for the development, and is fully supported and tested. @@ -43,7 +43,7 @@ Be aware what you will have to modify the password in Gateway parameters just af Be also aware that first initialization is a little long because the binding needs to read all the metadata from the device. Discovery of HVAC device have to be done through the Scan button inside the binding. -Go to the Thing page, click on the "+" button, select the SiemensHvac binding, and then click Scan. +Go to the Thing page, click on the "+" button, select the SiemensHVAC binding, and then click Scan. Your device should appear on the page after a few seconds. ## Bridge Configuration From ae6c8798a336e73760116dbc8475b7c75e9eb3d0 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 3 Apr 2024 14:50:27 +0200 Subject: [PATCH 144/214] review type conversion handling between siemens and openhab type (WIP) Signed-off-by: Laurent ARNAL --- .../SiemensHvacBindingConstants.java | 3 +- .../converter/ConverterException.java | 26 ++++ .../internal/converter/ConverterFactory.java | 61 +++++++++ .../converter/ConverterTypeException.java | 26 ++++ .../internal/converter/TypeConverter.java | 36 ++++++ .../converter/type/AbstractTypeConverter.java | 117 ++++++++++++++++++ .../converter/type/DateTimeTypeConverter.java | 81 ++++++++++++ .../converter/type/EnumTypeConverter.java | 47 +++++++ .../converter/type/NumericTypeConverter.java | 51 ++++++++ .../converter/type/RadioTypeConverter.java | 56 +++++++++ .../converter/type/TextTypeConverter.java | 47 +++++++ .../type/TimeOfDayTypeConverter.java | 51 ++++++++ .../factory/SiemensHvacHandlerFactory.java | 8 +- .../handler/SiemensHvacHandlerImpl.java | 69 +++-------- .../SiemensHvacMetadataRegistryImpl.java | 6 +- .../siemenshvac/internal/type/UidUtils.java | 18 ++- 16 files changed, 641 insertions(+), 62 deletions(-) create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterException.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterTypeException.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TextTypeConverter.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java index 3d15e81b3d09e..dae5ffe5eb226 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java @@ -58,9 +58,10 @@ public class SiemensHvacBindingConstants { public static final String DPT_TYPE_NUMERIC = "Numeric"; public static final String DPT_TYPE_RADIO = "RadioButton"; public static final String DPT_TYPE_DATE_TIME = "DateTime"; - public static final String DPT_TYPE_TIME = "TimeOfDay"; + public static final String DPT_TYPE_TIMEOFDAY = "TimeOfDay"; public static final String DPT_TYPE_SCHEDULER = "Scheduler"; public static final String DPT_TYPE_CALENDAR = "Calendar"; + public static final String DPT_TYPE_TEXT = "Text"; public static final String CATEGORY_THING_HVAC = "HVAC"; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterException.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterException.java new file mode 100644 index 0000000000000..acdf9e4fcf25c --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterException.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2010-2024 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.siemenshvac.internal.converter; + +/** + * Exception if something goes wrong when converting values between openHAB and the binding. + * + * @author Gerhard Riegler - Initial contribution + */ +public class ConverterException extends Exception { + private static final long serialVersionUID = 78045670450002L; + + public ConverterException(String message) { + super(message); + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java new file mode 100644 index 0000000000000..542dd7d76960b --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2010-2024 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.siemenshvac.internal.converter; + +import java.util.HashMap; +import java.util.Map; + +import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; +import org.openhab.binding.siemenshvac.internal.converter.type.DateTimeTypeConverter; +import org.openhab.binding.siemenshvac.internal.converter.type.EnumTypeConverter; +import org.openhab.binding.siemenshvac.internal.converter.type.NumericTypeConverter; +import org.openhab.binding.siemenshvac.internal.converter.type.RadioTypeConverter; +import org.openhab.binding.siemenshvac.internal.converter.type.TextTypeConverter; +import org.openhab.binding.siemenshvac.internal.converter.type.TimeOfDayTypeConverter; +import org.openhab.core.i18n.TimeZoneProvider; + +/** + * A factory for creating converters based on the itemType. + * + * @author Laurent Arnal - Initial contribution + */ +public class ConverterFactory { + private static Map> converterCache = new HashMap<>(); + + public static void registerConverter(TimeZoneProvider timeZoneProvider) { + registerConverter(SiemensHvacBindingConstants.DPT_TYPE_DATE_TIME, new DateTimeTypeConverter(timeZoneProvider)); + registerConverter(SiemensHvacBindingConstants.DPT_TYPE_ENUM, new EnumTypeConverter()); + registerConverter(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC, new NumericTypeConverter()); + registerConverter(SiemensHvacBindingConstants.DPT_TYPE_RADIO, new RadioTypeConverter()); + registerConverter(SiemensHvacBindingConstants.DPT_TYPE_TEXT, new TextTypeConverter()); + registerConverter(SiemensHvacBindingConstants.DPT_TYPE_TIMEOFDAY, new TimeOfDayTypeConverter()); + + } + + public static void registerConverter(String key, TypeConverter tp) { + converterCache.put(key, tp); + } + + /** + * Returns the converter for an itemType. + */ + public static TypeConverter getConverter(String itemType) throws ConverterException { + + TypeConverter converter = converterCache.get(itemType); + if (converter == null) { + throw new ConverterException("Can't find a converter for type '" + itemType + "'"); + } + + return converter; + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterTypeException.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterTypeException.java new file mode 100644 index 0000000000000..79d9297c5e52b --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterTypeException.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2010-2024 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.siemenshvac.internal.converter; + +/** + * Exception if converting between two types is not possible due wrong item type or command. + * + * @author Laurent Arnal - Initial contribution + */ +public class ConverterTypeException extends ConverterException { + private static final long serialVersionUID = 7114173349077221055L; + + public ConverterTypeException(String message) { + super(message); + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java new file mode 100644 index 0000000000000..1c6620f863762 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2010-2024 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.siemenshvac.internal.converter; + +import org.openhab.core.types.State; +import org.openhab.core.types.Type; + +import com.google.gson.JsonObject; + +/** + * Converter interface for converting between openHAB states/commands and siemensHvac values. + * + * @author Laurent Arnal - Initial contribution + */ +public interface TypeConverter { + + /** + * Converts an openHAB type to a SiemensHVac value. + */ + Object convertToBinding(Type type, JsonObject dp) throws ConverterException; + + /** + * Converts a siemensHvac value to an openHAB type. + */ + T convertFromBinding(JsonObject dp) throws ConverterException; +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java new file mode 100644 index 0000000000000..0092b02bca9fa --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2010-2024 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.siemenshvac.internal.converter.type; + +import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.binding.siemenshvac.internal.converter.TypeConverter; +import org.openhab.core.types.Command; +import org.openhab.core.types.State; +import org.openhab.core.types.Type; +import org.openhab.core.types.UnDefType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +/** + * Base class for all Converters with common methods. + * + * @author Laurent Arnal - Initial contribution + */ +public abstract class AbstractTypeConverter implements TypeConverter { + private final Logger logger = LoggerFactory.getLogger(AbstractTypeConverter.class); + + @SuppressWarnings("unchecked") + @Override + public Object convertToBinding(Type type, JsonObject dp) throws ConverterException { + /* + * if (type == UnDefType.NULL) { + * return null; + * } else if (type.getClass().isEnum() && !(this instanceof OnOffTypeConverter) + * && !(this instanceof OpenClosedTypeConverter)) { + * return commandToBinding((Command) type, dp); + * } else if (!toBindingValidation(dp, type.getClass())) { + * String errorMessage = String.format("Can't convert type %s with value '%s' to %s value with %s for '%s'", + * type.getClass().getSimpleName(), type.toString(), dp.getType(), this.getClass().getSimpleName(), + * new HmDatapointInfo(dp)); + * throw new ConverterTypeException(errorMessage); + * } + */ + return toBinding((T) type, dp); + } + + @SuppressWarnings("unchecked") + @Override + public T convertFromBinding(JsonObject dp) throws ConverterException { + + String type = null; + JsonElement value = null; + + if (dp.has("Type")) { + type = dp.get("Type").getAsString().trim(); + } + if (dp.has("Value")) { + value = dp.get("Value"); + } + if (dp.has("EnumValue")) { + value = dp.get("EnumValue"); + } + + if (value == null) { + return (T) UnDefType.NULL; + } + + if (type == null) { + logger.debug("siemensHvac:ReadDP:null type {}", dp); + return (T) UnDefType.NULL; + } + + if (!fromBindingValidation(value, type)) { + logger.debug("Can't convert {} value '{}' with {} for '{}'", type, value, this.getClass().getSimpleName(), + dp); + return (T) UnDefType.NULL; + } + + return fromBinding(value, type); + } + + /** + * Converts an openHAB command to a SiemensHvacValue value. + */ + protected Object commandToBinding(Command command, JsonObject dp) throws ConverterException { + throw new ConverterException("Unsupported command " + command.getClass().getSimpleName() + " for " + + this.getClass().getSimpleName()); + } + + /** + * Returns true, if the conversion from openHAB to the binding is possible. + */ + protected abstract boolean toBindingValidation(JsonObject dp, Class typeClass); + + /** + * Converts the type to a datapoint value. + */ + protected abstract Object toBinding(T type, JsonObject dp) throws ConverterException; + + /** + * Returns true, if the conversion from the binding to openHAB is possible. + */ + protected abstract boolean fromBindingValidation(JsonElement value, String type); + + /** + * Converts the datapoint value to an openHAB type. + */ + protected abstract T fromBinding(JsonElement value, String type) throws ConverterException; + +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java new file mode 100644 index 0000000000000..c482322ae869b --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2010-2024 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.siemenshvac.internal.converter.type; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.ZonedDateTime; + +import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.core.i18n.TimeZoneProvider; +import org.openhab.core.library.types.DateTimeType; +import org.openhab.core.types.Type; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +/** + * Converts between a SiemensHvac datapoint value and an openHAB DecimalType. + * + * @author Laurent Arnal - Initial contribution + */ +public class DateTimeTypeConverter extends AbstractTypeConverter { + + private final TimeZoneProvider timeZoneProvider; + + public DateTimeTypeConverter(final TimeZoneProvider timeZoneProvider) { + this.timeZoneProvider = timeZoneProvider; + } + + @Override + protected boolean toBindingValidation(JsonObject dp, Class typeClass) { + return true; + } + + @Override + protected Object toBinding(DateTimeType type, JsonObject dp) throws ConverterException { + return null; + } + + @Override + protected boolean fromBindingValidation(JsonElement value, String type) { + return true; + } + + @Override + protected DateTimeType fromBinding(JsonElement value, String type) throws ConverterException { + if ("----".equals(value.getAsString())) { + return new DateTimeType(); + } else { + String[] formats = { "EEEE, d. MMMM yyyy hh:mm", "d. MMMM yyyy hh:mm", "d. MMMM" }; + + for (int i = 0; i < formats.length; i++) { + try { + SimpleDateFormat dtf = new SimpleDateFormat(formats[i]); // first example + ZonedDateTime zdt = dtf.parse(value.getAsString()).toInstant() + .atZone(this.timeZoneProvider.getTimeZone()); + + if (i == 2) { + zdt = zdt.withYear(2024); + } + + return new DateTimeType(zdt); + } catch (ParseException ex) { + } + } + // logger.debug("Error decoding date: {}", value.getAsString()); + } + + return new DateTimeType(); + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java new file mode 100644 index 0000000000000..de227336378d2 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2010-2024 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.siemenshvac.internal.converter.type; + +import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.types.Type; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +/** + * Converts between a SiemensHvac datapoint value and an openHAB DecimalType. + * + * @author Laurent Arnal - Initial contribution + */ +public class EnumTypeConverter extends AbstractTypeConverter { + @Override + protected boolean toBindingValidation(JsonObject dp, Class typeClass) { + return true; + } + + @Override + protected Object toBinding(DecimalType type, JsonObject dp) throws ConverterException { + return null; + } + + @Override + protected boolean fromBindingValidation(JsonElement value, String type) { + return true; + } + + @Override + protected DecimalType fromBinding(JsonElement value, String type) throws ConverterException { + return new DecimalType(value.getAsInt()); + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java new file mode 100644 index 0000000000000..9047341a8315b --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2010-2024 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.siemenshvac.internal.converter.type; + +import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.types.Type; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +/** + * Converts between a SiemensHvac datapoint value and an openHAB DecimalType. + * + * @author Laurent Arnal - Initial contribution + */ +public class NumericTypeConverter extends AbstractTypeConverter { + @Override + protected boolean toBindingValidation(JsonObject dp, Class typeClass) { + return true; + } + + @Override + protected Object toBinding(DecimalType type, JsonObject dp) throws ConverterException { + return null; + } + + @Override + protected boolean fromBindingValidation(JsonElement value, String type) { + return true; + } + + @Override + protected DecimalType fromBinding(JsonElement value, String type) throws ConverterException { + if ("----".equals(value.getAsString())) { + return new DecimalType(0); + } else { + return new DecimalType(value.getAsDouble()); + } + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java new file mode 100644 index 0000000000000..c739cf92e2635 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2010-2024 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.siemenshvac.internal.converter.type; + +import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.types.Type; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +/** + * Converts between a SiemensHvac datapoint value and an openHAB DecimalType. + * + * @author Laurent Arnal - Initial contribution + */ +public class RadioTypeConverter extends AbstractTypeConverter { + @Override + protected boolean toBindingValidation(JsonObject dp, Class typeClass) { + return true; + } + + @Override + protected Object toBinding(DecimalType type, JsonObject dp) throws ConverterException { + return null; + } + + @Override + protected boolean fromBindingValidation(JsonElement value, String type) { + return true; + } + + @Override + protected DecimalType fromBinding(JsonElement value, String type) throws ConverterException { + DecimalType updateVal = new DecimalType(); + String valueSt = value.getAsString(); + + if ("Arrêt".equals(valueSt)) { + updateVal = new DecimalType(0); + } else if ("Marche".equals(valueSt)) { + updateVal = new DecimalType(1); + } + + return updateVal; + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TextTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TextTypeConverter.java new file mode 100644 index 0000000000000..91aba25382bac --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TextTypeConverter.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2010-2024 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.siemenshvac.internal.converter.type; + +import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.core.library.types.StringType; +import org.openhab.core.types.Type; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +/** + * Converts between a SiemensHvac datapoint value and an openHAB DecimalType. + * + * @author Laurent Arnal - Initial contribution + */ +public class TextTypeConverter extends AbstractTypeConverter { + @Override + protected boolean toBindingValidation(JsonObject dp, Class typeClass) { + return true; + } + + @Override + protected Object toBinding(StringType type, JsonObject dp) throws ConverterException { + return null; + } + + @Override + protected boolean fromBindingValidation(JsonElement value, String type) { + return true; + } + + @Override + protected StringType fromBinding(JsonElement value, String type) throws ConverterException { + return new StringType(value.getAsString()); + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java new file mode 100644 index 0000000000000..add54e0a8bff2 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2010-2024 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.siemenshvac.internal.converter.type; + +import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.types.Type; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +/** + * Converts between a SiemensHvac datapoint value and an openHAB DecimalType. + * + * @author Laurent Arnal - Initial contribution + */ +public class TimeOfDayTypeConverter extends AbstractTypeConverter { + @Override + protected boolean toBindingValidation(JsonObject dp, Class typeClass) { + return true; + } + + @Override + protected Object toBinding(DecimalType type, JsonObject dp) throws ConverterException { + return null; + } + + @Override + protected boolean fromBindingValidation(JsonElement value, String type) { + return true; + } + + @Override + protected DecimalType fromBinding(JsonElement value, String type) throws ConverterException { + if ("----".equals(value.getAsString())) { + return new DecimalType(0); + } else { + return new DecimalType(value.getAsInt()); + } + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java index 0ab3c22956804..21d807df56456 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java @@ -15,6 +15,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; +import org.openhab.binding.siemenshvac.internal.converter.ConverterFactory; import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacHandlerImpl; import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacOZW672BridgeThingHandler; import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataRegistry; @@ -48,7 +49,6 @@ public class SiemensHvacHandlerFactory extends BaseThingHandlerFactory { private final HttpClientFactory httpClientFactory; private final SiemensHvacMetadataRegistry metaDataRegistry; private final ChannelTypeRegistry channelTypeRegistry; - private final TimeZoneProvider timeZoneProvider; @Activate public SiemensHvacHandlerFactory(final @Reference HttpClientFactory httpClientFactory, @@ -60,7 +60,8 @@ public SiemensHvacHandlerFactory(final @Reference HttpClientFactory httpClientFa this.metaDataRegistry = metaDataRegistry; this.networkAddressService = networkAddressService; this.channelTypeRegistry = channelTypeRegistry; - this.timeZoneProvider = timeZoneProvider; + + ConverterFactory.registerConverter(timeZoneProvider); } @Override @@ -88,8 +89,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { metaDataRegistry); } else if (SiemensHvacBindingConstants.BINDING_ID.equals(thing.getThingTypeUID().getBindingId())) { SiemensHvacHandlerImpl handler = new SiemensHvacHandlerImpl(thing, - metaDataRegistry.getSiemensHvacConnector(), metaDataRegistry, channelTypeRegistry, - timeZoneProvider); + metaDataRegistry.getSiemensHvacConnector(), metaDataRegistry, channelTypeRegistry); return handler; } return null; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index 4b19819d4a8be..e08f1f0ed841e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -13,9 +13,6 @@ package org.openhab.binding.siemenshvac.internal.handler; import java.math.BigDecimal; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.time.ZonedDateTime; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; @@ -23,13 +20,15 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.binding.siemenshvac.internal.converter.ConverterFactory; +import org.openhab.binding.siemenshvac.internal.converter.ConverterTypeException; +import org.openhab.binding.siemenshvac.internal.converter.TypeConverter; import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint; import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataRegistry; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacCallback; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacRequestListener.ErrorSource; -import org.openhab.core.i18n.TimeZoneProvider; -import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.StringType; @@ -52,7 +51,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.gson.JsonElement; import com.google.gson.JsonObject; /** @@ -73,19 +71,16 @@ public class SiemensHvacHandlerImpl extends BaseThingHandler { private final @Nullable SiemensHvacConnector hvacConnector; private final @Nullable SiemensHvacMetadataRegistry metaDataRegistry; private final ChannelTypeRegistry channelTypeRegistry; - private final TimeZoneProvider timeZoneProvider; private long lastWrite = 0; public SiemensHvacHandlerImpl(Thing thing, @Nullable SiemensHvacConnector hvacConnector, - @Nullable SiemensHvacMetadataRegistry metaDataRegistry, ChannelTypeRegistry channelTypeRegistry, - final TimeZoneProvider timeZoneProvider) { + @Nullable SiemensHvacMetadataRegistry metaDataRegistry, ChannelTypeRegistry channelTypeRegistry) { super(thing); this.hvacConnector = hvacConnector; this.metaDataRegistry = metaDataRegistry; this.channelTypeRegistry = channelTypeRegistry; - this.timeZoneProvider = timeZoneProvider; } @Override @@ -232,6 +227,7 @@ private void readChannel(Channel channel) { logger.debug("pollingCode: type is null {} ", channel); return; } + readDp(id, uid, type, true); } @@ -241,54 +237,27 @@ public void decodeReadDp(@Nullable JsonObject response, @Nullable String uid, @N JsonObject subResult = (JsonObject) response.get("Data"); String updateKey = "" + uid; - String typer = ""; - JsonElement value = null; - JsonElement enumValue = null; + + String typer = null; if (subResult.has("Type")) { typer = subResult.get("Type").getAsString().trim(); } - if (subResult.has("Value")) { - value = subResult.get("Value"); - } - if (subResult.has("EnumValue")) { - enumValue = subResult.get("EnumValue"); - } - - if (value == null) { - return; - } - if (type == null) { - logger.debug("siemensHvac:ReadDP:null type {}", dp); - } - - if (("Numeric").equals(typer)) { - if ("----".equals(value.getAsString())) { - updateState(updateKey, new DecimalType(0)); + try { + TypeConverter converter = ConverterFactory.getConverter(typer); + State state = converter.convertFromBinding(subResult); + if (state != null) { + updateState(updateKey, state); } else { - updateState(updateKey, new DecimalType(value.getAsDouble())); - } - } else if ("Enumeration".equals(typer)) { - if (enumValue != null) { - updateState(updateKey, new DecimalType(enumValue.getAsInt())); + logger.debug("Failed to get converted state from datapoint '{}'", dp); } - } else if ("Text".equals(typer)) { - updateState(updateKey, new StringType(value.getAsString())); - } else if ("RadioButton".equals(typer)) { - updateState(updateKey, new StringType(value.getAsString())); - } else if ("DayOfTime".equals(typer) || "DateTime".equals(typer)) { - try { - SimpleDateFormat dtf = new SimpleDateFormat("EEEE, d. MMMM yyyy hh:mm"); // first example - ZonedDateTime zdt = dtf.parse(value.getAsString()).toInstant() - .atZone(this.timeZoneProvider.getTimeZone()); - updateState(updateKey, new DateTimeType(zdt)); - } catch (ParseException ex) { - logger.debug("Error decoding date: {}", value.getAsString()); - } - } else { - updateState(updateKey, new StringType(value.getAsString())); + } catch (ConverterTypeException ex) { + logger.warn("{}, please check the item type and the commands in your scripts", ex.getMessage()); + } catch (ConverterException ex) { + logger.warn("{}, please check the item type and the commands in your scripts", ex.getMessage()); } + } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java index 5381358473f5e..8863f1b9dd9c0 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -604,8 +604,8 @@ public String getItemType(SiemensHvacMetadataDataPoint dpt) { return SiemensHvacBindingConstants.ITEM_TYPE_ENUMERATION; } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_DATE_TIME)) { return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; - } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_TIME)) { - return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_TIMEOFDAY)) { + return SiemensHvacBindingConstants.ITEM_TYPE_NUMBER; } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_RADIO)) { return SiemensHvacBindingConstants.ITEM_TYPE_CONTACT; } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_SCHEDULER)) { @@ -633,7 +633,7 @@ public static String getCategory(SiemensHvacMetadataDataPoint dp) { return SiemensHvacBindingConstants.CATEGORY_CHANNEL_PROPS_TEMP; } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_DATE_TIME)) { return SiemensHvacBindingConstants.CATEGORY_CHANNEL_PROPS_TIME; - } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_TIME)) { + } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_TIMEOFDAY)) { return SiemensHvacBindingConstants.CATEGORY_CHANNEL_PROPS_TIME; } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_SWITCH; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index c40be24337624..22abc0959411b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -159,14 +159,24 @@ public static ChannelTypeUID generateChannelTypeUID(SiemensHvacMetadataDataPoint String shortDesc = dpt.getShortDescEn(); String result = normalizeDescriptor(shortDesc); - if ("DateTime".equals(type)) { + if (SiemensHvacBindingConstants.DPT_TYPE_DATE_TIME.equals(type)) { result = "datetime"; - } else if ("String".equals(type)) { + } else if (SiemensHvacBindingConstants.DPT_TYPE_STRING.equals(type)) { result = "string"; - } else if ("TimeOfDay".equals(type)) { + } else if (SiemensHvacBindingConstants.DPT_TYPE_TEXT.equals(type)) { + result = "string"; + } else if (SiemensHvacBindingConstants.DPT_TYPE_TIMEOFDAY.equals(type)) { + result = "number"; + } else if (SiemensHvacBindingConstants.DPT_TYPE_SCHEDULER.equals(type)) { result = "datetime"; - } else if ("Scheduler".equals(type)) { + } else if (SiemensHvacBindingConstants.DPT_TYPE_CALENDAR.equals(type)) { result = "datetime"; + } else if (SiemensHvacBindingConstants.DPT_TYPE_ENUM.equals(type)) { + result = "number"; + } else if (SiemensHvacBindingConstants.DPT_TYPE_NUMERIC.equals(type)) { + result = "number"; + } else if (SiemensHvacBindingConstants.DPT_TYPE_RADIO.equals(type)) { + result = "contact"; } return new ChannelTypeUID(SiemensHvacBindingConstants.BINDING_ID, result); From 8c634a6a45c7688df3f8e5a3975aa9a2c6683ea7 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Wed, 3 Apr 2024 14:50:27 +0200 Subject: [PATCH 145/214] review type conversion handling between siemens and openhab type (WIP) Signed-off-by: Laurent ARNAL --- .../binding/siemenshvac/internal/converter/ConverterFactory.java | 1 - .../internal/converter/type/AbstractTypeConverter.java | 1 - 2 files changed, 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java index 542dd7d76960b..d59835e43e623 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java @@ -39,7 +39,6 @@ public static void registerConverter(TimeZoneProvider timeZoneProvider) { registerConverter(SiemensHvacBindingConstants.DPT_TYPE_RADIO, new RadioTypeConverter()); registerConverter(SiemensHvacBindingConstants.DPT_TYPE_TEXT, new TextTypeConverter()); registerConverter(SiemensHvacBindingConstants.DPT_TYPE_TIMEOFDAY, new TimeOfDayTypeConverter()); - } public static void registerConverter(String key, TypeConverter tp) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java index 0092b02bca9fa..242b4dc358f67 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java @@ -113,5 +113,4 @@ protected Object commandToBinding(Command command, JsonObject dp) throws Convert * Converts the datapoint value to an openHAB type. */ protected abstract T fromBinding(JsonElement value, String type) throws ConverterException; - } From 70c88584446ab3c6a4dfbd4860236811e1710395 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 4 Apr 2024 10:42:32 +0200 Subject: [PATCH 146/214] review type conversion handling between siemens and openhab type Signed-off-by: Laurent ARNAL --- .../SiemensHvacBindingConstants.java | 7 +- .../internal/converter/ConverterFactory.java | 21 ++++-- .../internal/converter/TypeConverter.java | 25 ++++++- .../converter/type/AbstractTypeConverter.java | 71 +++++++++++------- .../converter/type/CalendarTypeConverter.java | 69 +++++++++++++++++ ...verter.java => CheckboxTypeConverter.java} | 31 ++++++-- .../converter/type/DateTimeTypeConverter.java | 32 ++++++-- .../converter/type/EnumTypeConverter.java | 31 ++++++-- .../converter/type/NumericTypeConverter.java | 35 +++++++-- .../converter/type/RadioTypeConverter.java | 64 ++++++++++++++-- .../type/SchedulerTypeConverter.java | 69 +++++++++++++++++ .../converter/type/StringTypeConverter.java | 74 +++++++++++++++++++ .../type/TimeOfDayTypeConverter.java | 31 ++++++-- .../handler/SiemensHvacHandlerImpl.java | 71 +++++++++++------- .../SiemensHvacMetadataRegistryImpl.java | 34 +++------ .../siemenshvac/internal/type/UidUtils.java | 41 ++++++---- 16 files changed, 564 insertions(+), 142 deletions(-) create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java rename bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/{TextTypeConverter.java => CheckboxTypeConverter.java} (53%) create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SchedulerTypeConverter.java create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/StringTypeConverter.java diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java index dae5ffe5eb226..52512fe6d3c29 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java @@ -43,25 +43,24 @@ public class SiemensHvacBindingConstants { public static final String PROPERTY_VENDOR_NAME = "Siemens"; public static final String ITEM_TYPE_SWITCH = "Switch"; - public static final String ITEM_TYPE_ROLLERSHUTTER = "Rollershutter"; public static final String ITEM_TYPE_CONTACT = "Contact"; public static final String ITEM_TYPE_STRING = "String"; public static final String ITEM_TYPE_NUMBER = "Number"; public static final String ITEM_TYPE_ENUMERATION = "Number"; - public static final String ITEM_TYPE_DIMMER = "Dimmer"; public static final String ITEM_TYPE_DATETIME = "DateTime"; public static final String CONFIG_DESCRIPTION_URI_THING_PREFIX = "thing-type"; - public static final String DPT_TYPE_STRING = "String"; public static final String DPT_TYPE_ENUM = "Enumeration"; public static final String DPT_TYPE_NUMERIC = "Numeric"; public static final String DPT_TYPE_RADIO = "RadioButton"; public static final String DPT_TYPE_DATE_TIME = "DateTime"; public static final String DPT_TYPE_TIMEOFDAY = "TimeOfDay"; + public static final String DPT_TYPE_STRING = "String"; + + public static final String DPT_TYPE_CHECKBOX = "CheckBox"; public static final String DPT_TYPE_SCHEDULER = "Scheduler"; public static final String DPT_TYPE_CALENDAR = "Calendar"; - public static final String DPT_TYPE_TEXT = "Text"; public static final String CATEGORY_THING_HVAC = "HVAC"; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java index d59835e43e623..38be9e9686cc1 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java @@ -16,11 +16,14 @@ import java.util.Map; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; +import org.openhab.binding.siemenshvac.internal.converter.type.CalendarTypeConverter; +import org.openhab.binding.siemenshvac.internal.converter.type.CheckboxTypeConverter; import org.openhab.binding.siemenshvac.internal.converter.type.DateTimeTypeConverter; import org.openhab.binding.siemenshvac.internal.converter.type.EnumTypeConverter; import org.openhab.binding.siemenshvac.internal.converter.type.NumericTypeConverter; import org.openhab.binding.siemenshvac.internal.converter.type.RadioTypeConverter; -import org.openhab.binding.siemenshvac.internal.converter.type.TextTypeConverter; +import org.openhab.binding.siemenshvac.internal.converter.type.SchedulerTypeConverter; +import org.openhab.binding.siemenshvac.internal.converter.type.StringTypeConverter; import org.openhab.binding.siemenshvac.internal.converter.type.TimeOfDayTypeConverter; import org.openhab.core.i18n.TimeZoneProvider; @@ -30,29 +33,33 @@ * @author Laurent Arnal - Initial contribution */ public class ConverterFactory { - private static Map> converterCache = new HashMap<>(); + private static Map converterCache = new HashMap<>(); public static void registerConverter(TimeZoneProvider timeZoneProvider) { registerConverter(SiemensHvacBindingConstants.DPT_TYPE_DATE_TIME, new DateTimeTypeConverter(timeZoneProvider)); registerConverter(SiemensHvacBindingConstants.DPT_TYPE_ENUM, new EnumTypeConverter()); registerConverter(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC, new NumericTypeConverter()); registerConverter(SiemensHvacBindingConstants.DPT_TYPE_RADIO, new RadioTypeConverter()); - registerConverter(SiemensHvacBindingConstants.DPT_TYPE_TEXT, new TextTypeConverter()); + registerConverter(SiemensHvacBindingConstants.DPT_TYPE_STRING, new StringTypeConverter()); registerConverter(SiemensHvacBindingConstants.DPT_TYPE_TIMEOFDAY, new TimeOfDayTypeConverter()); + + registerConverter(SiemensHvacBindingConstants.DPT_TYPE_CHECKBOX, new CheckboxTypeConverter()); + registerConverter(SiemensHvacBindingConstants.DPT_TYPE_SCHEDULER, new SchedulerTypeConverter()); + registerConverter(SiemensHvacBindingConstants.DPT_TYPE_CALENDAR, new CalendarTypeConverter()); } - public static void registerConverter(String key, TypeConverter tp) { + public static void registerConverter(String key, TypeConverter tp) { converterCache.put(key, tp); } /** * Returns the converter for an itemType. */ - public static TypeConverter getConverter(String itemType) throws ConverterException { + public static TypeConverter getConverter(String itemType) throws ConverterTypeException { - TypeConverter converter = converterCache.get(itemType); + TypeConverter converter = converterCache.get(itemType); if (converter == null) { - throw new ConverterException("Can't find a converter for type '" + itemType + "'"); + throw new ConverterTypeException("Can't find a converter for type '" + itemType + "'"); } return converter; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java index 1c6620f863762..b47a2f69c830b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.siemenshvac.internal.converter; -import org.openhab.core.types.State; import org.openhab.core.types.Type; import com.google.gson.JsonObject; @@ -22,15 +21,33 @@ * * @author Laurent Arnal - Initial contribution */ -public interface TypeConverter { +public interface TypeConverter { /** * Converts an openHAB type to a SiemensHVac value. */ - Object convertToBinding(Type type, JsonObject dp) throws ConverterException; + Object convertToBinding(Type type) throws ConverterException; /** * Converts a siemensHvac value to an openHAB type. */ - T convertFromBinding(JsonObject dp) throws ConverterException; + Type convertFromBinding(JsonObject dp) throws ConverterException; + + /** + * get underlying channel type to construct channel type UID + * + */ + String getChannelType(boolean writeAccess); + + /** + * get underlying item type on openhab side for this SiemensHvac type + * + */ + String getItemType(boolean writeAccess); + + /** + * tell if this type have different subvariant or not + * + */ + boolean hasVariant(); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java index 242b4dc358f67..839af9e1567f0 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java @@ -13,9 +13,9 @@ package org.openhab.binding.siemenshvac.internal.converter.type; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.binding.siemenshvac.internal.converter.ConverterTypeException; import org.openhab.binding.siemenshvac.internal.converter.TypeConverter; import org.openhab.core.types.Command; -import org.openhab.core.types.State; import org.openhab.core.types.Type; import org.openhab.core.types.UnDefType; import org.slf4j.Logger; @@ -29,31 +29,27 @@ * * @author Laurent Arnal - Initial contribution */ -public abstract class AbstractTypeConverter implements TypeConverter { +public abstract class AbstractTypeConverter implements TypeConverter { private final Logger logger = LoggerFactory.getLogger(AbstractTypeConverter.class); - @SuppressWarnings("unchecked") @Override - public Object convertToBinding(Type type, JsonObject dp) throws ConverterException { - /* - * if (type == UnDefType.NULL) { - * return null; - * } else if (type.getClass().isEnum() && !(this instanceof OnOffTypeConverter) - * && !(this instanceof OpenClosedTypeConverter)) { - * return commandToBinding((Command) type, dp); - * } else if (!toBindingValidation(dp, type.getClass())) { - * String errorMessage = String.format("Can't convert type %s with value '%s' to %s value with %s for '%s'", - * type.getClass().getSimpleName(), type.toString(), dp.getType(), this.getClass().getSimpleName(), - * new HmDatapointInfo(dp)); - * throw new ConverterTypeException(errorMessage); - * } - */ - return toBinding((T) type, dp); + public Object convertToBinding(Type type) throws ConverterException { + + if (type == UnDefType.NULL) { + return null; + } else if (type.getClass().isEnum()) { + return commandToBinding((Command) type); + } else if (!toBindingValidation(type)) { + String errorMessage = String.format("Can't convert type %s with value '%s' to %s value", + type.getClass().getSimpleName(), type.toString(), this.getClass().getSimpleName()); + throw new ConverterTypeException(errorMessage); + } + + return toBinding(type); } - @SuppressWarnings("unchecked") @Override - public T convertFromBinding(JsonObject dp) throws ConverterException { + public Type convertFromBinding(JsonObject dp) throws ConverterException { String type = null; JsonElement value = null; @@ -69,18 +65,18 @@ public T convertFromBinding(JsonObject dp) throws ConverterException { } if (value == null) { - return (T) UnDefType.NULL; + return UnDefType.NULL; } if (type == null) { logger.debug("siemensHvac:ReadDP:null type {}", dp); - return (T) UnDefType.NULL; + return UnDefType.NULL; } if (!fromBindingValidation(value, type)) { logger.debug("Can't convert {} value '{}' with {} for '{}'", type, value, this.getClass().getSimpleName(), dp); - return (T) UnDefType.NULL; + return UnDefType.NULL; } return fromBinding(value, type); @@ -89,7 +85,7 @@ public T convertFromBinding(JsonObject dp) throws ConverterException { /** * Converts an openHAB command to a SiemensHvacValue value. */ - protected Object commandToBinding(Command command, JsonObject dp) throws ConverterException { + protected Object commandToBinding(Command command) throws ConverterException { throw new ConverterException("Unsupported command " + command.getClass().getSimpleName() + " for " + this.getClass().getSimpleName()); } @@ -97,12 +93,12 @@ protected Object commandToBinding(Command command, JsonObject dp) throws Convert /** * Returns true, if the conversion from openHAB to the binding is possible. */ - protected abstract boolean toBindingValidation(JsonObject dp, Class typeClass); + protected abstract boolean toBindingValidation(Type type); /** * Converts the type to a datapoint value. */ - protected abstract Object toBinding(T type, JsonObject dp) throws ConverterException; + protected abstract Object toBinding(Type type) throws ConverterException; /** * Returns true, if the conversion from the binding to openHAB is possible. @@ -112,5 +108,26 @@ protected Object commandToBinding(Command command, JsonObject dp) throws Convert /** * Converts the datapoint value to an openHAB type. */ - protected abstract T fromBinding(JsonElement value, String type) throws ConverterException; + protected abstract Type fromBinding(JsonElement value, String type) throws ConverterException; + + /** + * get underlying channel type to construct channel type UID + * + */ + @Override + public abstract String getChannelType(boolean writeAccess); + + /** + * get underlying item type on openhab side for this SiemensHvac type + * + */ + @Override + public abstract String getItemType(boolean writeAccess); + + /** + * tell if this type have different subvariant or not + * + */ + @Override + public abstract boolean hasVariant(); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java new file mode 100644 index 0000000000000..d9e414da600f1 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2010-2024 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.siemenshvac.internal.converter.type; + +import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; +import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.core.library.types.DateTimeType; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.types.Type; + +import com.google.gson.JsonElement; + +/** + * Converts between a SiemensHvac datapoint value and an openHAB DecimalType. + * + * @author Laurent Arnal - Initial contribution + */ +public class CalendarTypeConverter extends AbstractTypeConverter { + @Override + protected boolean toBindingValidation(Type type) { + return true; + } + + @Override + protected Object toBinding(Type type) throws ConverterException { + Object valUpdate = null; + + if (type instanceof DateTimeType dateTime) { + valUpdate = dateTime.toString(); + } + + return valUpdate; + } + + @Override + protected boolean fromBindingValidation(JsonElement value, String type) { + return true; + } + + @Override + protected DecimalType fromBinding(JsonElement value, String type) throws ConverterException { + throw new ConverterException("NIY"); + } + + @Override + public String getChannelType(boolean writeAccess) { + return "datetime"; + } + + @Override + public String getItemType(boolean writeAccess) { + return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; + } + + @Override + public boolean hasVariant() { + return false; + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TextTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java similarity index 53% rename from bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TextTypeConverter.java rename to bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java index 91aba25382bac..4d8a733f59133 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TextTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java @@ -12,27 +12,27 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; +import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; -import org.openhab.core.library.types.StringType; +import org.openhab.core.library.types.DecimalType; import org.openhab.core.types.Type; import com.google.gson.JsonElement; -import com.google.gson.JsonObject; /** * Converts between a SiemensHvac datapoint value and an openHAB DecimalType. * * @author Laurent Arnal - Initial contribution */ -public class TextTypeConverter extends AbstractTypeConverter { +public class CheckboxTypeConverter extends AbstractTypeConverter { @Override - protected boolean toBindingValidation(JsonObject dp, Class typeClass) { + protected boolean toBindingValidation(Type type) { return true; } @Override - protected Object toBinding(StringType type, JsonObject dp) throws ConverterException { - return null; + protected Object toBinding(Type type) throws ConverterException { + throw new ConverterException("NIY"); } @Override @@ -41,7 +41,22 @@ protected boolean fromBindingValidation(JsonElement value, String type) { } @Override - protected StringType fromBinding(JsonElement value, String type) throws ConverterException { - return new StringType(value.getAsString()); + protected DecimalType fromBinding(JsonElement value, String type) throws ConverterException { + throw new ConverterException("NIY"); + } + + @Override + public String getChannelType(boolean writeAccess) { + return "contact"; + } + + @Override + public String getItemType(boolean writeAccess) { + return SiemensHvacBindingConstants.ITEM_TYPE_CONTACT; + } + + @Override + public boolean hasVariant() { + return false; } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java index c482322ae869b..a7a589fa9ce75 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java @@ -16,20 +16,20 @@ import java.text.SimpleDateFormat; import java.time.ZonedDateTime; +import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.types.Type; import com.google.gson.JsonElement; -import com.google.gson.JsonObject; /** * Converts between a SiemensHvac datapoint value and an openHAB DecimalType. * * @author Laurent Arnal - Initial contribution */ -public class DateTimeTypeConverter extends AbstractTypeConverter { +public class DateTimeTypeConverter extends AbstractTypeConverter { private final TimeZoneProvider timeZoneProvider; @@ -38,13 +38,19 @@ public DateTimeTypeConverter(final TimeZoneProvider timeZoneProvider) { } @Override - protected boolean toBindingValidation(JsonObject dp, Class typeClass) { + protected boolean toBindingValidation(Type type) { return true; } @Override - protected Object toBinding(DateTimeType type, JsonObject dp) throws ConverterException { - return null; + protected Object toBinding(Type type) throws ConverterException { + Object valUpdate = null; + + if (type instanceof DateTimeType dateTime) { + valUpdate = dateTime.toString(); + } + + return valUpdate; } @Override @@ -78,4 +84,20 @@ protected DateTimeType fromBinding(JsonElement value, String type) throws Conver return new DateTimeType(); } + + @Override + public String getChannelType(boolean writeAccess) { + return "datetime"; + } + + @Override + public String getItemType(boolean writeAccess) { + return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; + } + + @Override + public boolean hasVariant() { + return false; + } + } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java index de227336378d2..7f22a827c1453 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java @@ -12,27 +12,33 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; +import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.library.types.DecimalType; import org.openhab.core.types.Type; import com.google.gson.JsonElement; -import com.google.gson.JsonObject; /** * Converts between a SiemensHvac datapoint value and an openHAB DecimalType. * * @author Laurent Arnal - Initial contribution */ -public class EnumTypeConverter extends AbstractTypeConverter { +public class EnumTypeConverter extends AbstractTypeConverter { @Override - protected boolean toBindingValidation(JsonObject dp, Class typeClass) { + protected boolean toBindingValidation(Type type) { return true; } @Override - protected Object toBinding(DecimalType type, JsonObject dp) throws ConverterException { - return null; + protected Object toBinding(Type type) throws ConverterException { + Object valUpdate = null; + + if (type instanceof DecimalType decimalValue) { + valUpdate = decimalValue.toString(); + } + + return valUpdate; } @Override @@ -44,4 +50,19 @@ protected boolean fromBindingValidation(JsonElement value, String type) { protected DecimalType fromBinding(JsonElement value, String type) throws ConverterException { return new DecimalType(value.getAsInt()); } + + @Override + public String getChannelType(boolean writeAccess) { + return "number"; + } + + @Override + public String getItemType(boolean writeAccess) { + return SiemensHvacBindingConstants.ITEM_TYPE_ENUMERATION; + } + + @Override + public boolean hasVariant() { + return true; + } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java index 9047341a8315b..ef3b11c55f237 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java @@ -12,27 +12,37 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; +import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.QuantityType; import org.openhab.core.types.Type; import com.google.gson.JsonElement; -import com.google.gson.JsonObject; /** * Converts between a SiemensHvac datapoint value and an openHAB DecimalType. * * @author Laurent Arnal - Initial contribution */ -public class NumericTypeConverter extends AbstractTypeConverter { +public class NumericTypeConverter extends AbstractTypeConverter { @Override - protected boolean toBindingValidation(JsonObject dp, Class typeClass) { + protected boolean toBindingValidation(Type type) { return true; } @Override - protected Object toBinding(DecimalType type, JsonObject dp) throws ConverterException { - return null; + protected Object toBinding(Type type) throws ConverterException { + Object valUpdate = null; + + if (type instanceof QuantityType quantityType) { + Number num = (quantityType); + valUpdate = num.doubleValue(); + } else if (type instanceof DecimalType decimalValue) { + valUpdate = decimalValue.toString(); + } + + return valUpdate; } @Override @@ -48,4 +58,19 @@ protected DecimalType fromBinding(JsonElement value, String type) throws Convert return new DecimalType(value.getAsDouble()); } } + + @Override + public String getChannelType(boolean writeAccess) { + return "number"; + } + + @Override + public String getItemType(boolean writeAccess) { + return SiemensHvacBindingConstants.ITEM_TYPE_NUMBER; + } + + @Override + public boolean hasVariant() { + return true; + } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java index c739cf92e2635..5611d3130cf8d 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java @@ -12,27 +12,52 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; +import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.types.Command; import org.openhab.core.types.Type; import com.google.gson.JsonElement; -import com.google.gson.JsonObject; /** * Converts between a SiemensHvac datapoint value and an openHAB DecimalType. * * @author Laurent Arnal - Initial contribution */ -public class RadioTypeConverter extends AbstractTypeConverter { +public class RadioTypeConverter extends AbstractTypeConverter { @Override - protected boolean toBindingValidation(JsonObject dp, Class typeClass) { + protected boolean toBindingValidation(Type type) { return true; } @Override - protected Object toBinding(DecimalType type, JsonObject dp) throws ConverterException { - return null; + protected Object toBinding(Type type) throws ConverterException { + Object valUpdate = null; + + if (type instanceof DecimalType decimalValue) { + valUpdate = decimalValue.toString(); + } + + return valUpdate; + } + + @Override + protected Object commandToBinding(Command command) throws ConverterException { + Object valUpdate = null; + + if (command instanceof DecimalType decimalValue) { + valUpdate = decimalValue.toString(); + } else if (command instanceof OnOffType onOffType) { + if (onOffType.equals(OnOffType.OFF)) { + valUpdate = 0; + } else if (onOffType.equals(OnOffType.ON)) { + valUpdate = 1; + } + } + + return valUpdate; } @Override @@ -45,12 +70,39 @@ protected DecimalType fromBinding(JsonElement value, String type) throws Convert DecimalType updateVal = new DecimalType(); String valueSt = value.getAsString(); - if ("Arrêt".equals(valueSt)) { + if ("Non".equals(valueSt)) { + updateVal = new DecimalType(0); + } else if ("Arrêt".equals(valueSt)) { updateVal = new DecimalType(0); } else if ("Marche".equals(valueSt)) { updateVal = new DecimalType(1); + } else if ("Oui".equals(valueSt)) { + updateVal = new DecimalType(1); } return updateVal; } + + @Override + public String getChannelType(boolean writeAccess) { + if (writeAccess) { + return "switch"; + } else { + return "contact"; + } + } + + @Override + public String getItemType(boolean writeAccess) { + if (writeAccess) { + return SiemensHvacBindingConstants.ITEM_TYPE_SWITCH; + } else { + return SiemensHvacBindingConstants.ITEM_TYPE_CONTACT; + } + } + + @Override + public boolean hasVariant() { + return true; + } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SchedulerTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SchedulerTypeConverter.java new file mode 100644 index 0000000000000..c9aaeb5d6c901 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SchedulerTypeConverter.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2010-2024 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.siemenshvac.internal.converter.type; + +import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; +import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.core.library.types.DateTimeType; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.types.Type; + +import com.google.gson.JsonElement; + +/** + * Converts between a SiemensHvac datapoint value and an openHAB DecimalType. + * + * @author Laurent Arnal - Initial contribution + */ +public class SchedulerTypeConverter extends AbstractTypeConverter { + @Override + protected boolean toBindingValidation(Type type) { + return true; + } + + @Override + protected Object toBinding(Type type) throws ConverterException { + Object valUpdate = null; + + if (type instanceof DateTimeType dateTime) { + valUpdate = dateTime.toString(); + } + + return valUpdate; + } + + @Override + protected boolean fromBindingValidation(JsonElement value, String type) { + return true; + } + + @Override + protected DecimalType fromBinding(JsonElement value, String type) throws ConverterException { + throw new ConverterException("NIY"); + } + + @Override + public String getChannelType(boolean writeAccess) { + return "datetime"; + } + + @Override + public String getItemType(boolean writeAccess) { + return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; + } + + @Override + public boolean hasVariant() { + return false; + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/StringTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/StringTypeConverter.java new file mode 100644 index 0000000000000..84402e34d89e9 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/StringTypeConverter.java @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2010-2024 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.siemenshvac.internal.converter.type; + +import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; +import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.types.Type; + +import com.google.gson.JsonElement; + +/** + * Converts between a SiemensHvac datapoint value and an openHAB DecimalType. + * + * @author Laurent Arnal - Initial contribution + */ +public class StringTypeConverter extends AbstractTypeConverter { + @Override + protected boolean toBindingValidation(Type type) { + return true; + } + + @Override + protected Object toBinding(Type type) throws ConverterException { + Object valUpdate = null; + + if (type instanceof PercentType percentValue) { + valUpdate = percentValue.toString(); + } else if (type instanceof DecimalType decimalValue) { + valUpdate = decimalValue.toString(); + } else if (type instanceof StringType stringValue) { + valUpdate = stringValue.toString(); + } + + return valUpdate; + } + + @Override + protected boolean fromBindingValidation(JsonElement value, String type) { + return true; + } + + @Override + protected StringType fromBinding(JsonElement value, String type) throws ConverterException { + return new StringType(value.getAsString()); + } + + @Override + public String getChannelType(boolean writeAccess) { + return "string"; + } + + @Override + public String getItemType(boolean writeAccess) { + return SiemensHvacBindingConstants.ITEM_TYPE_STRING; + } + + @Override + public boolean hasVariant() { + return false; + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java index add54e0a8bff2..bd4d57a6dddcf 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java @@ -12,27 +12,33 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; +import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.library.types.DecimalType; import org.openhab.core.types.Type; import com.google.gson.JsonElement; -import com.google.gson.JsonObject; /** * Converts between a SiemensHvac datapoint value and an openHAB DecimalType. * * @author Laurent Arnal - Initial contribution */ -public class TimeOfDayTypeConverter extends AbstractTypeConverter { +public class TimeOfDayTypeConverter extends AbstractTypeConverter { @Override - protected boolean toBindingValidation(JsonObject dp, Class typeClass) { + protected boolean toBindingValidation(Type type) { return true; } @Override - protected Object toBinding(DecimalType type, JsonObject dp) throws ConverterException { - return null; + protected Object toBinding(Type type) throws ConverterException { + Object valUpdate = null; + + if (type instanceof DecimalType decimalValue) { + valUpdate = decimalValue.toString(); + } + + return valUpdate; } @Override @@ -48,4 +54,19 @@ protected DecimalType fromBinding(JsonElement value, String type) throws Convert return new DecimalType(value.getAsInt()); } } + + @Override + public String getChannelType(boolean writeAccess) { + return "number"; + } + + @Override + public String getItemType(boolean writeAccess) { + return SiemensHvacBindingConstants.ITEM_TYPE_NUMBER; + } + + @Override + public boolean hasVariant() { + return false; + } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index e08f1f0ed841e..41312b6a167f9 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -30,8 +30,6 @@ import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacRequestListener.ErrorSource; import org.openhab.core.library.types.DecimalType; -import org.openhab.core.library.types.PercentType; -import org.openhab.core.library.types.StringType; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelUID; @@ -245,8 +243,8 @@ public void decodeReadDp(@Nullable JsonObject response, @Nullable String uid, @N } try { - TypeConverter converter = ConverterFactory.getConverter(typer); - State state = converter.convertFromBinding(subResult); + TypeConverter converter = ConverterFactory.getConverter(typer); + State state = (State) converter.convertFromBinding(subResult); if (state != null) { updateState(updateKey, state); } else { @@ -327,34 +325,47 @@ private void writeDp(String dp, Type dpVal, String type) { logger.trace("Start write: {}", dp); lastWrite = System.currentTimeMillis(); - String valUpdate = "0"; - String valUpdateEnum = ""; + Object valUpdate = "0"; - if (dpVal instanceof PercentType percentValue) { - valUpdate = percentValue.toString(); - } else if (dpVal instanceof DecimalType decimalValue) { - valUpdate = decimalValue.toString(); - } else if (dpVal instanceof StringType stringValue) { - valUpdate = stringValue.toString(); + try { + TypeConverter converter = ConverterFactory.getConverter(type); + + valUpdate = converter.convertToBinding(dpVal); + if (valUpdate != null) { + String request = String.format("api/menutree/write_datapoint.json?Id=%s&Value=%s&Type=%s", dp, + valUpdate, type); - if ("Enumeration".equals(type)) { - String[] valuesUpdateDp = valUpdate.split(":"); - valUpdateEnum = valuesUpdateDp[0]; + if (lcHvacConnector != null) { + logger.trace("Write request for: {} ", valUpdate); + JsonObject response = lcHvacConnector.doRequest(request); + + logger.trace("Write request response: {} ", response); + } - // For enumeration, we always update using the raw value - valUpdate = valUpdateEnum; + } else { + logger.debug("Failed to get converted state from datapoint '{}'", dp); } + } catch (ConverterTypeException ex) { + logger.warn("{}, please check the item type and the commands in your scripts", ex.getMessage()); + } catch (ConverterException ex) { + logger.warn("{}, please check the item type and the commands in your scripts", ex.getMessage()); } - String request = String.format("api/menutree/write_datapoint.json?Id=%s&Value=%s&Type=%s", dp, valUpdate, - type); - - if (lcHvacConnector != null) { - logger.trace("Write request for: {} ", valUpdate); - JsonObject response = lcHvacConnector.doRequest(request); + /* + * if (dpVal instanceof PercentType percentValue) { + * valUpdate = percentValue.toString(); + * } else if (dpVal instanceof DecimalType decimalValue) { + * valUpdate = decimalValue.toString(); + * } else if (dpVal instanceof StringType stringValue) { + * valUpdate = stringValue.toString(); + * + * if ("Enumeration".equals(type)) { + * String[] valuesUpdateDp = valUpdate.split(":"); + * valUpdate = valuesUpdateDp[0]; + * } + * } + */ - logger.trace("Write request response: {} ", response); - } } catch (Exception e) { logger.debug("siemensHvac:ReadDp:Error during dp reading: {}: {}", dp, e.getLocalizedMessage()); // Reset sessionId so we redone _auth on error @@ -380,11 +391,14 @@ private Command applyState(ChannelType tp, Command command) { if (step != null) { doMods = true; int divider = 1; - if (step.floatValue() == 0.5) { + + if (step.doubleValue() == 0.5) { divider = 2; - } else if (step.floatValue() == 0.1) { + } else if (step.doubleValue() == 0.1) { divider = 10; - } else if (step.floatValue() == 0.01) { + } else if (step.doubleValue() == 0.02) { + divider = 50; + } else if (step.doubleValue() == 0.01) { divider = 100; } v1 = v1 * divider; @@ -407,6 +421,7 @@ private Command applyState(ChannelType tp, Command command) { } } return result; + } @Override diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java index 8863f1b9dd9c0..b08d993622731 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -32,6 +32,9 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; +import org.openhab.binding.siemenshvac.internal.converter.ConverterFactory; +import org.openhab.binding.siemenshvac.internal.converter.ConverterTypeException; +import org.openhab.binding.siemenshvac.internal.converter.TypeConverter; import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeConfig; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacCallback; import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector; @@ -473,7 +476,8 @@ private void generateThingsType(SiemensHvacMetadata child, List Date: Thu, 4 Apr 2024 10:42:32 +0200 Subject: [PATCH 147/214] review type conversion handling between siemens and openhab type Signed-off-by: Laurent ARNAL --- .../internal/converter/type/DateTimeTypeConverter.java | 1 - 1 file changed, 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java index a7a589fa9ce75..9e7960e860375 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java @@ -99,5 +99,4 @@ public String getItemType(boolean writeAccess) { public boolean hasVariant() { return false; } - } From b6c9f0e717c1c5f65ecbf221021973c2da510495 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 4 Apr 2024 10:42:32 +0200 Subject: [PATCH 148/214] review type conversion handling between siemens and openhab type Signed-off-by: Laurent ARNAL --- .../siemenshvac/internal/handler/SiemensHvacHandlerImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index 41312b6a167f9..85f4b2f79d453 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -421,7 +421,6 @@ private Command applyState(ChannelType tp, Command command) { } } return result; - } @Override From 027af652bd3d4d69323239e4ed88001fef66b70c Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 14 Apr 2024 21:37:06 +0200 Subject: [PATCH 149/214] fix contact handling (WIP) Signed-off-by: Laurent ARNAL --- .../internal/converter/TypeConverter.java | 5 ++-- .../converter/type/AbstractTypeConverter.java | 17 ++++++----- .../converter/type/CalendarTypeConverter.java | 5 ++-- .../converter/type/CheckboxTypeConverter.java | 5 ++-- .../converter/type/DateTimeTypeConverter.java | 5 ++-- .../converter/type/EnumTypeConverter.java | 5 ++-- .../converter/type/NumericTypeConverter.java | 5 ++-- .../converter/type/RadioTypeConverter.java | 29 ++++++++++++------- .../type/SchedulerTypeConverter.java | 5 ++-- .../converter/type/StringTypeConverter.java | 5 ++-- .../type/TimeOfDayTypeConverter.java | 5 ++-- .../handler/SiemensHvacHandlerImpl.java | 18 ++++++------ .../SiemensHvacMetadataRegistryImpl.java | 17 +++++++++-- 13 files changed, 78 insertions(+), 48 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java index b47a2f69c830b..0248b0460eea2 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.siemenshvac.internal.converter; +import org.openhab.core.thing.type.ChannelType; import org.openhab.core.types.Type; import com.google.gson.JsonObject; @@ -26,12 +27,12 @@ public interface TypeConverter { /** * Converts an openHAB type to a SiemensHVac value. */ - Object convertToBinding(Type type) throws ConverterException; + Object convertToBinding(Type type, ChannelType tp) throws ConverterException; /** * Converts a siemensHvac value to an openHAB type. */ - Type convertFromBinding(JsonObject dp) throws ConverterException; + Type convertFromBinding(JsonObject dp, ChannelType tp) throws ConverterException; /** * get underlying channel type to construct channel type UID diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java index 839af9e1567f0..79c0e3781b0ad 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java @@ -15,6 +15,7 @@ import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.binding.siemenshvac.internal.converter.ConverterTypeException; import org.openhab.binding.siemenshvac.internal.converter.TypeConverter; +import org.openhab.core.thing.type.ChannelType; import org.openhab.core.types.Command; import org.openhab.core.types.Type; import org.openhab.core.types.UnDefType; @@ -33,23 +34,23 @@ public abstract class AbstractTypeConverter implements TypeConverter { private final Logger logger = LoggerFactory.getLogger(AbstractTypeConverter.class); @Override - public Object convertToBinding(Type type) throws ConverterException { + public Object convertToBinding(Type type, ChannelType tp) throws ConverterException { if (type == UnDefType.NULL) { return null; } else if (type.getClass().isEnum()) { - return commandToBinding((Command) type); + return commandToBinding((Command) type, tp); } else if (!toBindingValidation(type)) { String errorMessage = String.format("Can't convert type %s with value '%s' to %s value", type.getClass().getSimpleName(), type.toString(), this.getClass().getSimpleName()); throw new ConverterTypeException(errorMessage); } - return toBinding(type); + return toBinding(type, tp); } @Override - public Type convertFromBinding(JsonObject dp) throws ConverterException { + public Type convertFromBinding(JsonObject dp, ChannelType tp) throws ConverterException { String type = null; JsonElement value = null; @@ -79,13 +80,13 @@ public Type convertFromBinding(JsonObject dp) throws ConverterException { return UnDefType.NULL; } - return fromBinding(value, type); + return fromBinding(value, type, tp); } /** * Converts an openHAB command to a SiemensHvacValue value. */ - protected Object commandToBinding(Command command) throws ConverterException { + protected Object commandToBinding(Command command, ChannelType tp) throws ConverterException { throw new ConverterException("Unsupported command " + command.getClass().getSimpleName() + " for " + this.getClass().getSimpleName()); } @@ -98,7 +99,7 @@ protected Object commandToBinding(Command command) throws ConverterException { /** * Converts the type to a datapoint value. */ - protected abstract Object toBinding(Type type) throws ConverterException; + protected abstract Object toBinding(Type type, ChannelType tp) throws ConverterException; /** * Returns true, if the conversion from the binding to openHAB is possible. @@ -108,7 +109,7 @@ protected Object commandToBinding(Command command) throws ConverterException { /** * Converts the datapoint value to an openHAB type. */ - protected abstract Type fromBinding(JsonElement value, String type) throws ConverterException; + protected abstract Type fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException; /** * get underlying channel type to construct channel type UID diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java index d9e414da600f1..ee2b52ecb64ce 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java @@ -16,6 +16,7 @@ import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; +import org.openhab.core.thing.type.ChannelType; import org.openhab.core.types.Type; import com.google.gson.JsonElement; @@ -32,7 +33,7 @@ protected boolean toBindingValidation(Type type) { } @Override - protected Object toBinding(Type type) throws ConverterException { + protected Object toBinding(Type type, ChannelType tp) throws ConverterException { Object valUpdate = null; if (type instanceof DateTimeType dateTime) { @@ -48,7 +49,7 @@ protected boolean fromBindingValidation(JsonElement value, String type) { } @Override - protected DecimalType fromBinding(JsonElement value, String type) throws ConverterException { + protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { throw new ConverterException("NIY"); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java index 4d8a733f59133..3d1c1b5966824 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java @@ -15,6 +15,7 @@ import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.library.types.DecimalType; +import org.openhab.core.thing.type.ChannelType; import org.openhab.core.types.Type; import com.google.gson.JsonElement; @@ -31,7 +32,7 @@ protected boolean toBindingValidation(Type type) { } @Override - protected Object toBinding(Type type) throws ConverterException { + protected Object toBinding(Type type, ChannelType tp) throws ConverterException { throw new ConverterException("NIY"); } @@ -41,7 +42,7 @@ protected boolean fromBindingValidation(JsonElement value, String type) { } @Override - protected DecimalType fromBinding(JsonElement value, String type) throws ConverterException { + protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { throw new ConverterException("NIY"); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java index 9e7960e860375..8c0ddf78fe233 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java @@ -20,6 +20,7 @@ import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.library.types.DateTimeType; +import org.openhab.core.thing.type.ChannelType; import org.openhab.core.types.Type; import com.google.gson.JsonElement; @@ -43,7 +44,7 @@ protected boolean toBindingValidation(Type type) { } @Override - protected Object toBinding(Type type) throws ConverterException { + protected Object toBinding(Type type, ChannelType tp) throws ConverterException { Object valUpdate = null; if (type instanceof DateTimeType dateTime) { @@ -59,7 +60,7 @@ protected boolean fromBindingValidation(JsonElement value, String type) { } @Override - protected DateTimeType fromBinding(JsonElement value, String type) throws ConverterException { + protected DateTimeType fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { if ("----".equals(value.getAsString())) { return new DateTimeType(); } else { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java index 7f22a827c1453..137923099f7a6 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java @@ -15,6 +15,7 @@ import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.library.types.DecimalType; +import org.openhab.core.thing.type.ChannelType; import org.openhab.core.types.Type; import com.google.gson.JsonElement; @@ -31,7 +32,7 @@ protected boolean toBindingValidation(Type type) { } @Override - protected Object toBinding(Type type) throws ConverterException { + protected Object toBinding(Type type, ChannelType tp) throws ConverterException { Object valUpdate = null; if (type instanceof DecimalType decimalValue) { @@ -47,7 +48,7 @@ protected boolean fromBindingValidation(JsonElement value, String type) { } @Override - protected DecimalType fromBinding(JsonElement value, String type) throws ConverterException { + protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { return new DecimalType(value.getAsInt()); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java index ef3b11c55f237..fe616826f397f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java @@ -16,6 +16,7 @@ import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.QuantityType; +import org.openhab.core.thing.type.ChannelType; import org.openhab.core.types.Type; import com.google.gson.JsonElement; @@ -32,7 +33,7 @@ protected boolean toBindingValidation(Type type) { } @Override - protected Object toBinding(Type type) throws ConverterException { + protected Object toBinding(Type type, ChannelType tp) throws ConverterException { Object valUpdate = null; if (type instanceof QuantityType quantityType) { @@ -51,7 +52,7 @@ protected boolean fromBindingValidation(JsonElement value, String type) { } @Override - protected DecimalType fromBinding(JsonElement value, String type) throws ConverterException { + protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { if ("----".equals(value.getAsString())) { return new DecimalType(0); } else { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java index 5611d3130cf8d..a69473fab723d 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java @@ -12,11 +12,16 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; +import java.util.List; + import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; +import org.openhab.core.thing.type.ChannelType; import org.openhab.core.types.Command; +import org.openhab.core.types.StateDescription; +import org.openhab.core.types.StateOption; import org.openhab.core.types.Type; import com.google.gson.JsonElement; @@ -33,7 +38,7 @@ protected boolean toBindingValidation(Type type) { } @Override - protected Object toBinding(Type type) throws ConverterException { + protected Object toBinding(Type type, ChannelType tp) throws ConverterException { Object valUpdate = null; if (type instanceof DecimalType decimalValue) { @@ -44,7 +49,7 @@ protected Object toBinding(Type type) throws ConverterException { } @Override - protected Object commandToBinding(Command command) throws ConverterException { + protected Object commandToBinding(Command command, ChannelType tp) throws ConverterException { Object valUpdate = null; if (command instanceof DecimalType decimalValue) { @@ -66,18 +71,20 @@ protected boolean fromBindingValidation(JsonElement value, String type) { } @Override - protected DecimalType fromBinding(JsonElement value, String type) throws ConverterException { + protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { DecimalType updateVal = new DecimalType(); String valueSt = value.getAsString(); - if ("Non".equals(valueSt)) { - updateVal = new DecimalType(0); - } else if ("Arrêt".equals(valueSt)) { - updateVal = new DecimalType(0); - } else if ("Marche".equals(valueSt)) { - updateVal = new DecimalType(1); - } else if ("Oui".equals(valueSt)) { - updateVal = new DecimalType(1); + StateDescription sd = tp.getState(); + + List options = sd.getOptions(); + StateOption offOpt = options.get(0); + StateOption onOpt = options.get(1); + + if (onOpt.getLabel().equals(valueSt)) { + updateVal = new DecimalType(onOpt.getValue()); + } else if (offOpt.getLabel().equals(valueSt)) { + updateVal = new DecimalType(offOpt.getValue()); } return updateVal; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SchedulerTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SchedulerTypeConverter.java index c9aaeb5d6c901..c1076f31f4765 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SchedulerTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SchedulerTypeConverter.java @@ -16,6 +16,7 @@ import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; +import org.openhab.core.thing.type.ChannelType; import org.openhab.core.types.Type; import com.google.gson.JsonElement; @@ -32,7 +33,7 @@ protected boolean toBindingValidation(Type type) { } @Override - protected Object toBinding(Type type) throws ConverterException { + protected Object toBinding(Type type, ChannelType tp) throws ConverterException { Object valUpdate = null; if (type instanceof DateTimeType dateTime) { @@ -48,7 +49,7 @@ protected boolean fromBindingValidation(JsonElement value, String type) { } @Override - protected DecimalType fromBinding(JsonElement value, String type) throws ConverterException { + protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { throw new ConverterException("NIY"); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/StringTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/StringTypeConverter.java index 84402e34d89e9..fce7c76583dc0 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/StringTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/StringTypeConverter.java @@ -17,6 +17,7 @@ import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.StringType; +import org.openhab.core.thing.type.ChannelType; import org.openhab.core.types.Type; import com.google.gson.JsonElement; @@ -33,7 +34,7 @@ protected boolean toBindingValidation(Type type) { } @Override - protected Object toBinding(Type type) throws ConverterException { + protected Object toBinding(Type type, ChannelType tp) throws ConverterException { Object valUpdate = null; if (type instanceof PercentType percentValue) { @@ -53,7 +54,7 @@ protected boolean fromBindingValidation(JsonElement value, String type) { } @Override - protected StringType fromBinding(JsonElement value, String type) throws ConverterException { + protected StringType fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { return new StringType(value.getAsString()); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java index bd4d57a6dddcf..92a723831f230 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java @@ -15,6 +15,7 @@ import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.library.types.DecimalType; +import org.openhab.core.thing.type.ChannelType; import org.openhab.core.types.Type; import com.google.gson.JsonElement; @@ -31,7 +32,7 @@ protected boolean toBindingValidation(Type type) { } @Override - protected Object toBinding(Type type) throws ConverterException { + protected Object toBinding(Type type, ChannelType tp) throws ConverterException { Object valUpdate = null; if (type instanceof DecimalType decimalValue) { @@ -47,7 +48,7 @@ protected boolean fromBindingValidation(JsonElement value, String type) { } @Override - protected DecimalType fromBinding(JsonElement value, String type) throws ConverterException { + protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { if ("----".equals(value.getAsString())) { return new DecimalType(0); } else { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index 85f4b2f79d453..cf64f4947649d 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -226,10 +226,10 @@ private void readChannel(Channel channel) { return; } - readDp(id, uid, type, true); + readDp(id, uid, tp, type, true); } - public void decodeReadDp(@Nullable JsonObject response, @Nullable String uid, @Nullable String dp, + public void decodeReadDp(@Nullable JsonObject response, @Nullable String uid, @Nullable String dp, ChannelType tp, @Nullable String type) { if (response != null && response.has("Data")) { JsonObject subResult = (JsonObject) response.get("Data"); @@ -244,7 +244,7 @@ public void decodeReadDp(@Nullable JsonObject response, @Nullable String uid, @N try { TypeConverter converter = ConverterFactory.getConverter(typer); - State state = (State) converter.convertFromBinding(subResult); + State state = (State) converter.convertFromBinding(subResult, tp); if (state != null) { updateState(updateKey, state); } else { @@ -259,7 +259,7 @@ public void decodeReadDp(@Nullable JsonObject response, @Nullable String uid, @N } } - private void readDp(String dp, String uid, String type, boolean async) { + private void readDp(String dp, String uid, ChannelType tp, String type, boolean async) { SiemensHvacConnector lcHvacConnector = hvacConnector; if ("-1".equals(dp)) { @@ -289,7 +289,7 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { logger.trace("End read : {}", dp); if (response instanceof JsonObject jsonResponse) { - decodeReadDp(jsonResponse, uid, dp, type); + decodeReadDp(jsonResponse, uid, dp, tp, type); } } }); @@ -297,7 +297,7 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { } else { if (lcHvacConnector != null) { JsonObject js = lcHvacConnector.doRequest(request); - decodeReadDp(js, uid, dp, type); + decodeReadDp(js, uid, dp, tp, type); } } } catch (Exception e) { @@ -309,7 +309,7 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { } } - private void writeDp(String dp, Type dpVal, String type) { + private void writeDp(String dp, Type dpVal, ChannelType tp, String type) { SiemensHvacConnector lcHvacConnector = hvacConnector; if (lcHvacConnector != null) { @@ -330,7 +330,7 @@ private void writeDp(String dp, Type dpVal, String type) { try { TypeConverter converter = ConverterFactory.getConverter(type); - valUpdate = converter.convertToBinding(dpVal); + valUpdate = converter.convertToBinding(dpVal, tp); if (valUpdate != null) { String request = String.format("api/menutree/write_datapoint.json?Id=%s&Value=%s&Type=%s", dp, valUpdate, type); @@ -468,7 +468,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { } if (id != null && type != null) { - writeDp(id, commandVar, dptType); + writeDp(id, commandVar, tp, dptType); } } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java index b08d993622731..5f4b63bd6d8f0 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -506,9 +506,22 @@ private ChannelType createChannelType(SiemensHvacMetadataDataPoint dpt, ChannelT } description = descBuilder.toString(); label = channelTypeUID.getId(); - } + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_RADIO)) { + StringBuilder descBuilder = new StringBuilder(); + descBuilder.append("Radio:"); + SiemensHvacMetadataPointChild child = dpt.getChild().get(0); + + StateOption stOpt0 = new StateOption("0", child.getOpt0()); + descBuilder.append(String.format("(%s:%s)", "0", child.getOpt0())); + options.add(stOpt0); - if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { + StateOption stOpt1 = new StateOption("1", child.getOpt1()); + descBuilder.append(String.format("(%s:%s)", "1", child.getOpt1())); + options.add(stOpt1); + + description = descBuilder.toString(); + label = channelTypeUID.getId(); + } else if (dpt.getDptType().equals(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { BigDecimal min = new BigDecimal(dpt.getMin()); BigDecimal max = new BigDecimal(dpt.getMax()); BigDecimal step = new BigDecimal(dpt.getResolution()); From 5de868a13b630176d4bdbc465221ca7e12d8d694 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 14 Apr 2024 21:37:06 +0200 Subject: [PATCH 150/214] fix contact handling (WIP) Signed-off-by: Laurent ARNAL From 1647baee365e8bfba28b32a5206b4fcdc159036a Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 14 Apr 2024 21:37:06 +0200 Subject: [PATCH 151/214] fix contact handling (WIP) Signed-off-by: Laurent ARNAL From 39d4d861962a4502602704a4a1450e27e14469ed Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 14 Apr 2024 21:37:06 +0200 Subject: [PATCH 152/214] fix contact handling (WIP) Signed-off-by: Laurent ARNAL From dad40e0b85e228bb6a67e9c21b185c9203c559f1 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 16 Apr 2024 13:08:52 +0200 Subject: [PATCH 153/214] fix state description on Radiobutton Signed-off-by: Laurent ARNAL --- .../SiemensHvacMetadataRegistryImpl.java | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java index 5f4b63bd6d8f0..d04eb3f21b324 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -511,13 +511,23 @@ private ChannelType createChannelType(SiemensHvacMetadataDataPoint dpt, ChannelT descBuilder.append("Radio:"); SiemensHvacMetadataPointChild child = dpt.getChild().get(0); - StateOption stOpt0 = new StateOption("0", child.getOpt0()); - descBuilder.append(String.format("(%s:%s)", "0", child.getOpt0())); - options.add(stOpt0); - - StateOption stOpt1 = new StateOption("1", child.getOpt1()); - descBuilder.append(String.format("(%s:%s)", "1", child.getOpt1())); - options.add(stOpt1); + if (dpt.getWriteAccess()) { + StateOption stOpt0 = new StateOption("OFF", child.getOpt0()); + descBuilder.append(String.format("(%s:%s)", "OFF", child.getOpt0())); + options.add(stOpt0); + + StateOption stOpt1 = new StateOption("ON", child.getOpt1()); + descBuilder.append(String.format("(%s:%s)", "ON", child.getOpt1())); + options.add(stOpt1); + } else { + StateOption stOpt0 = new StateOption("CLOSED", child.getOpt0()); + descBuilder.append(String.format("(%s:%s)", "OFF", child.getOpt0())); + options.add(stOpt0); + + StateOption stOpt1 = new StateOption("OPEN", child.getOpt1()); + descBuilder.append(String.format("(%s:%s)", "ON", child.getOpt1())); + options.add(stOpt1); + } description = descBuilder.toString(); label = channelTypeUID.getId(); From 0613800194edb743984371d97c8ad0b3e6b1d8f9 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 30 Apr 2024 17:58:01 +0200 Subject: [PATCH 154/214] fix unregisterRequestHandler to unregister handler only once Signed-off-by: Laurent ARNAL --- .../internal/network/SiemensHvacConnectorImpl.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 05d51321023de..dce29a89be0ee 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -244,11 +244,9 @@ public void onError(@Nullable Request request, @Nullable SiemensHvacRequestHandl private void unregisterRequestHandler(SiemensHvacRequestHandler handler) throws SiemensHvacException { synchronized (currentHandlerRegistry) { - if (!currentHandlerRegistry.containsKey(handler)) { - throw new SiemensHvacException("Internal error, try to unregister not registred handler: " + handler); + if (currentHandlerRegistry.containsKey(handler)) { + currentHandlerRegistry.remove(handler); } - - currentHandlerRegistry.remove(handler); } } From 61cd02bd22cea1ea8ab9b51745bbf91431c787f7 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Mon, 6 May 2024 14:28:06 +0200 Subject: [PATCH 155/214] div fixes for last jlaur review Signed-off-by: Laurent ARNAL --- .../converter/ConverterException.java | 4 +-- .../internal/converter/ConverterFactory.java | 1 + .../converter/ConverterTypeException.java | 2 +- .../converter/type/RadioTypeConverter.java | 22 +++++++------- .../SiemensHvacMetadataRegistryImpl.java | 2 +- .../network/SiemensHvacConnectorImpl.java | 8 ++--- .../network/SiemensHvacRequestListener.java | 10 +++---- .../siemenshvac/internal/type/UidUtils.java | 29 ++++--------------- .../main/resources/OH-INF/thing/ozw672.xml | 1 + 9 files changed, 32 insertions(+), 47 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterException.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterException.java index acdf9e4fcf25c..fb971a6ac15ec 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterException.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterException.java @@ -15,10 +15,10 @@ /** * Exception if something goes wrong when converting values between openHAB and the binding. * - * @author Gerhard Riegler - Initial contribution + * @author Laurent Arnal - Initial contribution */ public class ConverterException extends Exception { - private static final long serialVersionUID = 78045670450002L; + private static final long serialVersionUID = 42567425458545L; public ConverterException(String message) { super(message); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java index 38be9e9686cc1..12e3c9cb64bcc 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java @@ -32,6 +32,7 @@ * * @author Laurent Arnal - Initial contribution */ + public class ConverterFactory { private static Map converterCache = new HashMap<>(); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterTypeException.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterTypeException.java index 79d9297c5e52b..ad71181894d3f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterTypeException.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterTypeException.java @@ -18,7 +18,7 @@ * @author Laurent Arnal - Initial contribution */ public class ConverterTypeException extends ConverterException { - private static final long serialVersionUID = 7114173349077221055L; + private static final long serialVersionUID = 2546248551752214152L; public ConverterTypeException(String message) { super(message); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java index a69473fab723d..16ac62f46299f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java @@ -54,10 +54,10 @@ protected Object commandToBinding(Command command, ChannelType tp) throws Conver if (command instanceof DecimalType decimalValue) { valUpdate = decimalValue.toString(); - } else if (command instanceof OnOffType onOffType) { - if (onOffType.equals(OnOffType.OFF)) { + } else if (command instanceof OnOffType onOffValue) { + if (onOffValue.equals(OnOffType.OFF)) { valUpdate = 0; - } else if (onOffType.equals(OnOffType.ON)) { + } else if (onOffValue.equals(OnOffType.ON)) { valUpdate = 1; } } @@ -77,14 +77,16 @@ protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp StateDescription sd = tp.getState(); - List options = sd.getOptions(); - StateOption offOpt = options.get(0); - StateOption onOpt = options.get(1); + if (sd != null) { + List options = sd.getOptions(); + StateOption offOpt = options.get(0); + StateOption onOpt = options.get(1); - if (onOpt.getLabel().equals(valueSt)) { - updateVal = new DecimalType(onOpt.getValue()); - } else if (offOpt.getLabel().equals(valueSt)) { - updateVal = new DecimalType(offOpt.getValue()); + if (valueSt.equals(onOpt.getLabel())) { + updateVal = new DecimalType(onOpt.getValue()); + } else if (valueSt.equals(offOpt.getLabel())) { + updateVal = new DecimalType(offOpt.getValue()); + } } return updateVal; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java index d04eb3f21b324..7d87364bce84e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -779,7 +779,7 @@ public void readUserInfo() throws SiemensHvacException { } } catch (Exception e) { logger.error("siemensHvac:ResolveDpt:Error during reading user info: {}", e.getLocalizedMessage()); - throw new SiemensHvacException("Error durring reading user info", e); + throw new SiemensHvacException("Error during reading user info", e); // Reset sessionId so we redone _auth on error } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index dce29a89be0ee..74cffd9312a60 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -62,10 +62,10 @@ public class SiemensHvacConnectorImpl implements SiemensHvacConnector { private final Logger logger = LoggerFactory.getLogger(SiemensHvacConnectorImpl.class); - private Map currentHandlerRegistry = new ConcurrentHashMap(); - private Map handlerInErrorRegistry = new ConcurrentHashMap(); + private Map currentHandlerRegistry = new ConcurrentHashMap<>(); + private Map handlerInErrorRegistry = new ConcurrentHashMap<>(); - private Map oldSessionId = new HashMap(); + private Map oldSessionId = new HashMap<>(); private final Gson gson; private final Gson gsonWithAdapter; @@ -154,7 +154,7 @@ public static String extractSessionId(String query) { public void onError(@Nullable Request request, @Nullable SiemensHvacRequestHandler reqHandler, SiemensHvacRequestListener.ErrorSource errorSource, boolean mayRetry) throws Exception { if (reqHandler == null || request == null) { - throw new SiemensHvacException("internalError : onError call with reqHandler == null"); + throw new SiemensHvacException("internalError: onError call with reqHandler == null"); } boolean doRetry = mayRetry; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java index 42ba28ee88e28..b91d4c7851d8e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java @@ -120,15 +120,15 @@ public void onFailure(@Nullable Response response, @Nullable Throwable failure) String msg = cause.getLocalizedMessage(); if (cause instanceof ConnectException e) { - logger.debug("ConnectException during request : {} {}", response.getRequest().getURI(), msg, e); + logger.debug("ConnectException during request: {} {}", response.getRequest().getURI(), msg, e); } else if (cause instanceof SocketException e) { - logger.debug("SocketException during request : {} {}", response.getRequest().getURI(), msg, e); + logger.debug("SocketException during request: {} {}", response.getRequest().getURI(), msg, e); } else if (cause instanceof SocketTimeoutException e) { - logger.debug("SocketTimeoutException during request : {} {}", response.getRequest().getURI(), msg, e); + logger.debug("SocketTimeoutException during request: {} {}", response.getRequest().getURI(), msg, e); } else if (cause instanceof EOFException e) { - logger.debug("EOFException during request : {} {}", response.getRequest().getURI(), msg, e); + logger.debug("EOFException during request: {} {}", response.getRequest().getURI(), msg, e); } else if (cause instanceof TimeoutException e) { - logger.debug("TimeoutException during request : {} {}", response.getRequest().getURI(), msg, e); + logger.debug("TimeoutException during request: {} {}", response.getRequest().getURI(), msg, e); } else { logger.debug("Response failed: {} {}", response.getRequest().getURI(), msg, failure); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index 371181eabd3d0..8836b4175e785 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -22,10 +22,7 @@ import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint; import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDevice; import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataMenu; -import org.openhab.core.thing.Bridge; -import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ThingTypeUID; -import org.openhab.core.thing.ThingUID; import org.openhab.core.thing.type.ChannelGroupTypeUID; import org.openhab.core.thing.type.ChannelTypeUID; @@ -103,10 +100,9 @@ private static String normalizeDescriptor(String descriptor) { } result = result.replace(" mon", ""); - result = result.replace(" yue", ""); + result = result.replace(" tue", ""); result = result.replace(" wed", ""); result = result.replace(" thu", ""); - result = result.replace(" tue", ""); result = result.replace(" fri", ""); result = result.replace(" sat", ""); result = result.replace(" sun", ""); @@ -157,7 +153,7 @@ private static String normalizeDescriptor(String descriptor) { /** * Generates the ChannelTypeUID for the given datapoint with deviceType, channelNumber and datapointName. */ - public static ChannelTypeUID generateChannelTypeUID(SiemensHvacMetadataDataPoint dpt) throws Exception { + public static ChannelTypeUID generateChannelTypeUID(SiemensHvacMetadataDataPoint dpt) throws SiemensHvacException { String type = dpt.getDptType(); String shortDesc = dpt.getShortDescEn(); @@ -169,13 +165,13 @@ public static ChannelTypeUID generateChannelTypeUID(SiemensHvacMetadataDataPoint result = tp.getChannelType(dpt.getWriteAccess()); } } catch (ConverterTypeException ex) { - throw new SiemensHvacException(String.format("Can't find convertor for type: %s", type), ex); + throw new SiemensHvacException(String.format("Can't find converter for type: %s", type), ex); } return new ChannelTypeUID(SiemensHvacBindingConstants.BINDING_ID, result); } - public static ChannelTypeUID generateChannelTypeUID2(SiemensHvacMetadataDataPoint dpt) throws Exception { + public static ChannelTypeUID generateChannelTypeUID2(SiemensHvacMetadataDataPoint dpt) throws SiemensHvacException { String type = dpt.getDptType(); String shortDesc = dpt.getShortDescEn(); String result = normalizeDescriptor(shortDesc); @@ -193,25 +189,10 @@ public static ChannelTypeUID generateChannelTypeUID2(SiemensHvacMetadataDataPoin return new ChannelTypeUID(SiemensHvacBindingConstants.BINDING_ID, result); } - /** - * Generates the ThingUID for the given device in the given bridge. - */ - public static ThingUID generateThingUID(Bridge bridge) { - ThingTypeUID thingTypeUID = generateThingTypeUID(""); - return new ThingUID(thingTypeUID, bridge.getUID(), ""); - } - - /** - * Generates the ChannelUID for the given datapoint with channelNumber and datapointName. - */ - public static ChannelUID generateChannelUID(ThingUID thingUID) { - return new ChannelUID(thingUID, ""); - } - /** * Generates the ChannelTypeUID for the given datapoint with deviceType and channelNumber. */ public static ChannelGroupTypeUID generateChannelGroupTypeUID(SiemensHvacMetadataMenu menu) { - return new ChannelGroupTypeUID(SiemensHvacBindingConstants.BINDING_ID, String.format("%s", menu.getId())); + return new ChannelGroupTypeUID(SiemensHvacBindingConstants.BINDING_ID, String.valueOf(menu.getId())); } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml index 43c109b1cbb6b..a72622b15003f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml @@ -24,6 +24,7 @@ Administrator
+ password User password of the Siemens Hvac gateway false From 101f6da3ee2c006b56e278e81ffc267c1ed67330 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Mon, 6 May 2024 14:51:55 +0200 Subject: [PATCH 156/214] review checkstyle warnings Signed-off-by: Laurent ARNAL --- .../internal/converter/ConverterException.java | 3 +++ .../internal/converter/ConverterFactory.java | 3 ++- .../internal/converter/ConverterTypeException.java | 3 +++ .../siemenshvac/internal/converter/TypeConverter.java | 4 ++++ .../converter/type/AbstractTypeConverter.java | 11 ++++++----- .../converter/type/CalendarTypeConverter.java | 5 ++++- .../converter/type/CheckboxTypeConverter.java | 5 ++++- .../converter/type/DateTimeTypeConverter.java | 5 ++++- .../internal/converter/type/EnumTypeConverter.java | 5 ++++- .../internal/converter/type/NumericTypeConverter.java | 5 ++++- .../internal/converter/type/RadioTypeConverter.java | 7 +++++-- .../converter/type/SchedulerTypeConverter.java | 5 ++++- .../internal/converter/type/StringTypeConverter.java | 5 ++++- .../converter/type/TimeOfDayTypeConverter.java | 5 ++++- .../internal/handler/SiemensHvacHandlerImpl.java | 9 ++++----- .../binding/siemenshvac/internal/type/UidUtils.java | 1 - 16 files changed, 59 insertions(+), 22 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterException.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterException.java index fb971a6ac15ec..d9b73d81070fb 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterException.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterException.java @@ -12,11 +12,14 @@ */ package org.openhab.binding.siemenshvac.internal.converter; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * Exception if something goes wrong when converting values between openHAB and the binding. * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault public class ConverterException extends Exception { private static final long serialVersionUID = 42567425458545L; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java index 12e3c9cb64bcc..6500f39bf8d75 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java @@ -15,6 +15,7 @@ import java.util.HashMap; import java.util.Map; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.type.CalendarTypeConverter; import org.openhab.binding.siemenshvac.internal.converter.type.CheckboxTypeConverter; @@ -33,6 +34,7 @@ * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault public class ConverterFactory { private static Map converterCache = new HashMap<>(); @@ -57,7 +59,6 @@ public static void registerConverter(String key, TypeConverter tp) { * Returns the converter for an itemType. */ public static TypeConverter getConverter(String itemType) throws ConverterTypeException { - TypeConverter converter = converterCache.get(itemType); if (converter == null) { throw new ConverterTypeException("Can't find a converter for type '" + itemType + "'"); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterTypeException.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterTypeException.java index ad71181894d3f..3c3c6ba6db8bb 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterTypeException.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterTypeException.java @@ -12,11 +12,14 @@ */ package org.openhab.binding.siemenshvac.internal.converter; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * Exception if converting between two types is not possible due wrong item type or command. * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault public class ConverterTypeException extends ConverterException { private static final long serialVersionUID = 2546248551752214152L; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java index 0248b0460eea2..a296a410265ef 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.siemenshvac.internal.converter; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.thing.type.ChannelType; import org.openhab.core.types.Type; @@ -22,11 +24,13 @@ * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault public interface TypeConverter { /** * Converts an openHAB type to a SiemensHVac value. */ + @Nullable Object convertToBinding(Type type, ChannelType tp) throws ConverterException; /** diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java index 79c0e3781b0ad..a53d572d13b0a 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.binding.siemenshvac.internal.converter.ConverterTypeException; import org.openhab.binding.siemenshvac.internal.converter.TypeConverter; @@ -30,12 +32,12 @@ * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault public abstract class AbstractTypeConverter implements TypeConverter { private final Logger logger = LoggerFactory.getLogger(AbstractTypeConverter.class); @Override - public Object convertToBinding(Type type, ChannelType tp) throws ConverterException { - + public @Nullable Object convertToBinding(Type type, ChannelType tp) throws ConverterException { if (type == UnDefType.NULL) { return null; } else if (type.getClass().isEnum()) { @@ -51,7 +53,6 @@ public Object convertToBinding(Type type, ChannelType tp) throws ConverterExcept @Override public Type convertFromBinding(JsonObject dp, ChannelType tp) throws ConverterException { - String type = null; JsonElement value = null; @@ -86,7 +87,7 @@ public Type convertFromBinding(JsonObject dp, ChannelType tp) throws ConverterEx /** * Converts an openHAB command to a SiemensHvacValue value. */ - protected Object commandToBinding(Command command, ChannelType tp) throws ConverterException { + protected @Nullable Object commandToBinding(Command command, ChannelType tp) throws ConverterException { throw new ConverterException("Unsupported command " + command.getClass().getSimpleName() + " for " + this.getClass().getSimpleName()); } @@ -99,7 +100,7 @@ protected Object commandToBinding(Command command, ChannelType tp) throws Conver /** * Converts the type to a datapoint value. */ - protected abstract Object toBinding(Type type, ChannelType tp) throws ConverterException; + protected abstract @Nullable Object toBinding(Type type, ChannelType tp) throws ConverterException; /** * Returns true, if the conversion from the binding to openHAB is possible. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java index ee2b52ecb64ce..0b4ddd08d40e9 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.library.types.DateTimeType; @@ -26,6 +28,7 @@ * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault public class CalendarTypeConverter extends AbstractTypeConverter { @Override protected boolean toBindingValidation(Type type) { @@ -33,7 +36,7 @@ protected boolean toBindingValidation(Type type) { } @Override - protected Object toBinding(Type type, ChannelType tp) throws ConverterException { + protected @Nullable Object toBinding(Type type, ChannelType tp) throws ConverterException { Object valUpdate = null; if (type instanceof DateTimeType dateTime) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java index 3d1c1b5966824..f55d985df35d3 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.library.types.DecimalType; @@ -25,6 +27,7 @@ * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault public class CheckboxTypeConverter extends AbstractTypeConverter { @Override protected boolean toBindingValidation(Type type) { @@ -32,7 +35,7 @@ protected boolean toBindingValidation(Type type) { } @Override - protected Object toBinding(Type type, ChannelType tp) throws ConverterException { + protected @Nullable Object toBinding(Type type, ChannelType tp) throws ConverterException { throw new ConverterException("NIY"); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java index 8c0ddf78fe233..fbd19e604c9ab 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java @@ -16,6 +16,8 @@ import java.text.SimpleDateFormat; import java.time.ZonedDateTime; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.i18n.TimeZoneProvider; @@ -30,6 +32,7 @@ * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault public class DateTimeTypeConverter extends AbstractTypeConverter { private final TimeZoneProvider timeZoneProvider; @@ -44,7 +47,7 @@ protected boolean toBindingValidation(Type type) { } @Override - protected Object toBinding(Type type, ChannelType tp) throws ConverterException { + protected @Nullable Object toBinding(Type type, ChannelType tp) throws ConverterException { Object valUpdate = null; if (type instanceof DateTimeType dateTime) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java index 137923099f7a6..dda7aa7196615 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.library.types.DecimalType; @@ -25,6 +27,7 @@ * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault public class EnumTypeConverter extends AbstractTypeConverter { @Override protected boolean toBindingValidation(Type type) { @@ -32,7 +35,7 @@ protected boolean toBindingValidation(Type type) { } @Override - protected Object toBinding(Type type, ChannelType tp) throws ConverterException { + protected @Nullable Object toBinding(Type type, ChannelType tp) throws ConverterException { Object valUpdate = null; if (type instanceof DecimalType decimalValue) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java index fe616826f397f..4aea4e4a071a9 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.library.types.DecimalType; @@ -26,6 +28,7 @@ * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault public class NumericTypeConverter extends AbstractTypeConverter { @Override protected boolean toBindingValidation(Type type) { @@ -33,7 +36,7 @@ protected boolean toBindingValidation(Type type) { } @Override - protected Object toBinding(Type type, ChannelType tp) throws ConverterException { + protected @Nullable Object toBinding(Type type, ChannelType tp) throws ConverterException { Object valUpdate = null; if (type instanceof QuantityType quantityType) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java index 16ac62f46299f..4e25b4f53aaa7 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java @@ -14,6 +14,8 @@ import java.util.List; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.library.types.DecimalType; @@ -31,6 +33,7 @@ * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault public class RadioTypeConverter extends AbstractTypeConverter { @Override protected boolean toBindingValidation(Type type) { @@ -38,7 +41,7 @@ protected boolean toBindingValidation(Type type) { } @Override - protected Object toBinding(Type type, ChannelType tp) throws ConverterException { + protected @Nullable Object toBinding(Type type, ChannelType tp) throws ConverterException { Object valUpdate = null; if (type instanceof DecimalType decimalValue) { @@ -49,7 +52,7 @@ protected Object toBinding(Type type, ChannelType tp) throws ConverterException } @Override - protected Object commandToBinding(Command command, ChannelType tp) throws ConverterException { + protected @Nullable Object commandToBinding(Command command, ChannelType tp) throws ConverterException { Object valUpdate = null; if (command instanceof DecimalType decimalValue) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SchedulerTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SchedulerTypeConverter.java index c1076f31f4765..6fbd1a33ca063 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SchedulerTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SchedulerTypeConverter.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.library.types.DateTimeType; @@ -26,6 +28,7 @@ * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault public class SchedulerTypeConverter extends AbstractTypeConverter { @Override protected boolean toBindingValidation(Type type) { @@ -33,7 +36,7 @@ protected boolean toBindingValidation(Type type) { } @Override - protected Object toBinding(Type type, ChannelType tp) throws ConverterException { + protected @Nullable Object toBinding(Type type, ChannelType tp) throws ConverterException { Object valUpdate = null; if (type instanceof DateTimeType dateTime) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/StringTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/StringTypeConverter.java index fce7c76583dc0..16d0728aa9cc7 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/StringTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/StringTypeConverter.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.library.types.DecimalType; @@ -27,6 +29,7 @@ * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault public class StringTypeConverter extends AbstractTypeConverter { @Override protected boolean toBindingValidation(Type type) { @@ -34,7 +37,7 @@ protected boolean toBindingValidation(Type type) { } @Override - protected Object toBinding(Type type, ChannelType tp) throws ConverterException { + protected @Nullable Object toBinding(Type type, ChannelType tp) throws ConverterException { Object valUpdate = null; if (type instanceof PercentType percentValue) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java index 92a723831f230..9335a0d8ca07e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.library.types.DecimalType; @@ -25,6 +27,7 @@ * * @author Laurent Arnal - Initial contribution */ +@NonNullByDefault public class TimeOfDayTypeConverter extends AbstractTypeConverter { @Override protected boolean toBindingValidation(Type type) { @@ -32,7 +35,7 @@ protected boolean toBindingValidation(Type type) { } @Override - protected Object toBinding(Type type, ChannelType tp) throws ConverterException { + protected @Nullable Object toBinding(Type type, ChannelType tp) throws ConverterException { Object valUpdate = null; if (type instanceof DecimalType decimalValue) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index cf64f4947649d..2d754ec0c6366 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -243,12 +243,11 @@ public void decodeReadDp(@Nullable JsonObject response, @Nullable String uid, @N } try { - TypeConverter converter = ConverterFactory.getConverter(typer); - State state = (State) converter.convertFromBinding(subResult, tp); - if (state != null) { + if (typer != null) { + TypeConverter converter = ConverterFactory.getConverter(typer); + + State state = (State) converter.convertFromBinding(subResult, tp); updateState(updateKey, state); - } else { - logger.debug("Failed to get converted state from datapoint '{}'", dp); } } catch (ConverterTypeException ex) { logger.warn("{}, please check the item type and the commands in your scripts", ex.getMessage()); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index 8836b4175e785..f00922c7c0c54 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -154,7 +154,6 @@ private static String normalizeDescriptor(String descriptor) { * Generates the ChannelTypeUID for the given datapoint with deviceType, channelNumber and datapointName. */ public static ChannelTypeUID generateChannelTypeUID(SiemensHvacMetadataDataPoint dpt) throws SiemensHvacException { - String type = dpt.getDptType(); String shortDesc = dpt.getShortDescEn(); String result = normalizeDescriptor(shortDesc); From d9a87429fd59bf0ff08d468d33028085ead1561c Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Mon, 6 May 2024 15:42:18 +0200 Subject: [PATCH 157/214] remove normalizer Signed-off-by: Laurent ARNAL --- .../siemenshvac/internal/type/UidUtils.java | 42 +++---------------- 1 file changed, 5 insertions(+), 37 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index f00922c7c0c54..5a002c242964d 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -12,8 +12,6 @@ */ package org.openhab.binding.siemenshvac.internal.type; -import java.text.Normalizer; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterFactory; @@ -25,6 +23,8 @@ import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.type.ChannelGroupTypeUID; import org.openhab.core.thing.type.ChannelTypeUID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Utility class for generating some UIDs. @@ -33,6 +33,8 @@ */ @NonNullByDefault public class UidUtils { + private static final Logger logger = LoggerFactory.getLogger(UidUtils.class); + /** * The methods remove specific local character (like 'é'/'ê','â') so we have a correctly formated UID from a * localize item label @@ -43,23 +45,7 @@ public class UidUtils { public static String sanetizeId(String label) { String result = label; - if (!Normalizer.isNormalized(label, Normalizer.Form.NFKD)) { - result = Normalizer.normalize(label, Normalizer.Form.NFKD); - result = result.replaceAll("\\p{M}", ""); - } - - result = result.replace(' ', '_'); - result = result.replace(':', '_'); - result = result.replace('.', '_'); - result = result.replace('\'', '_'); - result = result.replace('(', '_'); - result = result.replace(')', '_'); - result = result.replace('&', '_'); - result = result.replace('/', '_'); - result = result.replace('°', '_'); - result = result.replace('+', '_'); - - return result; + return result.replaceAll("[^a-zA-Z0-9_]", "_").toLowerCase(); } /** @@ -170,24 +156,6 @@ public static ChannelTypeUID generateChannelTypeUID(SiemensHvacMetadataDataPoint return new ChannelTypeUID(SiemensHvacBindingConstants.BINDING_ID, result); } - public static ChannelTypeUID generateChannelTypeUID2(SiemensHvacMetadataDataPoint dpt) throws SiemensHvacException { - String type = dpt.getDptType(); - String shortDesc = dpt.getShortDescEn(); - String result = normalizeDescriptor(shortDesc); - - if ("DateTime".equals(type)) { - result = "datetime"; - } else if ("String".equals(type)) { - result = "string"; - } else if ("TimeOfDay".equals(type)) { - result = "datetime"; - } else if ("Scheduler".equals(type)) { - result = "datetime"; - } - - return new ChannelTypeUID(SiemensHvacBindingConstants.BINDING_ID, result); - } - /** * Generates the ChannelTypeUID for the given datapoint with deviceType and channelNumber. */ From e5d6b53c6f20dbf69d41c9e06cb27fe4fcf68011 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Mon, 6 May 2024 15:48:32 +0200 Subject: [PATCH 158/214] put back normalizer because unit testing fail because of accent handling Signed-off-by: Laurent ARNAL --- .../siemenshvac/internal/type/UidUtils.java | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index 5a002c242964d..29b7155569dd3 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.siemenshvac.internal.type; +import java.text.Normalizer; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterFactory; @@ -23,8 +25,6 @@ import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.type.ChannelGroupTypeUID; import org.openhab.core.thing.type.ChannelTypeUID; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Utility class for generating some UIDs. @@ -33,8 +33,6 @@ */ @NonNullByDefault public class UidUtils { - private static final Logger logger = LoggerFactory.getLogger(UidUtils.class); - /** * The methods remove specific local character (like 'é'/'ê','â') so we have a correctly formated UID from a * localize item label @@ -45,7 +43,23 @@ public class UidUtils { public static String sanetizeId(String label) { String result = label; - return result.replaceAll("[^a-zA-Z0-9_]", "_").toLowerCase(); + if (!Normalizer.isNormalized(label, Normalizer.Form.NFKD)) { + result = Normalizer.normalize(label, Normalizer.Form.NFKD); + result = result.replaceAll("\\p{M}", ""); + } + + result = result.replace(' ', '_'); + result = result.replace(':', '_'); + result = result.replace('.', '_'); + result = result.replace('\'', '_'); + result = result.replace('(', '_'); + result = result.replace(')', '_'); + result = result.replace('&', '_'); + result = result.replace('/', '_'); + result = result.replace('°', '_'); + result = result.replace('+', '_'); + + return result; } /** From 1da0fd739f232a762345adac9d5bdde7559b6579 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Mon, 6 May 2024 15:48:32 +0200 Subject: [PATCH 159/214] put back normalizer because unit testing fail because of accent handling Signed-off-by: Laurent ARNAL From d007a8425f0366c4e67bdd91384a1a7159dd2ce8 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 7 May 2024 11:36:50 +0200 Subject: [PATCH 160/214] fixes for last jlaur review Signed-off-by: Laurent ARNAL --- .../converter/type/DateTimeTypeConverter.java | 37 +++++++++++++------ .../type/TimeOfDayTypeConverter.java | 5 ++- .../SiemensHvacBridgeBaseThingHandler.java | 7 ++-- .../src/main/resources/OH-INF/addon/addon.xml | 29 +++++++++++++-- .../OH-INF/i18n/siemenshvac.properties | 22 +++++++++++ .../OH-INF/i18n/siemenshvac_fr.properties | 23 ++++++++++++ 6 files changed, 102 insertions(+), 21 deletions(-) create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr.properties diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java index fbd19e604c9ab..491a75c57a8c8 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java @@ -12,9 +12,12 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; -import java.text.ParseException; -import java.text.SimpleDateFormat; +import java.time.LocalDateTime; import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoField; +import java.util.Locale; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -67,26 +70,36 @@ protected DateTimeType fromBinding(JsonElement value, String type, ChannelType t if ("----".equals(value.getAsString())) { return new DateTimeType(); } else { - String[] formats = { "EEEE, d. MMMM yyyy hh:mm", "d. MMMM yyyy hh:mm", "d. MMMM" }; + String[] formats = { "EEEE, d. LLLL yyyy HH:mm[:ss]", "d. LLLL yyyy HH:mm[:ss]", "d. LLLL yyyy", + "d. LLLL" }; for (int i = 0; i < formats.length; i++) { try { - SimpleDateFormat dtf = new SimpleDateFormat(formats[i]); // first example - ZonedDateTime zdt = dtf.parse(value.getAsString()).toInstant() - .atZone(this.timeZoneProvider.getTimeZone()); - - if (i == 2) { - zdt = zdt.withYear(2024); + DateTimeFormatterBuilder formatterBuilder = new DateTimeFormatterBuilder().parseCaseInsensitive() + .appendPattern(formats[i]).parseDefaulting(ChronoField.HOUR_OF_DAY, 0) + .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0) + .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0); + + if (i == 3) { + formatterBuilder = formatterBuilder.parseDefaulting(ChronoField.YEAR, + ZonedDateTime.now().getYear()); } + LocalDateTime parsedDate = LocalDateTime.parse(value.getAsString(), + formatterBuilder.toFormatter(Locale.FRENCH)); + + ZonedDateTime zdt = parsedDate.atZone(this.timeZoneProvider.getTimeZone()); + return new DateTimeType(zdt); - } catch (ParseException ex) { + } catch (DateTimeParseException ex) { + if (i == formats.length - 1) { + throw new ConverterException("Can't parse the date for :" + value); + } } } - // logger.debug("Error decoding date: {}", value.getAsString()); } - return new DateTimeType(); + return new DateTimeType(ZonedDateTime.now(this.timeZoneProvider.getTimeZone())); } @Override diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java index 9335a0d8ca07e..a2ff2662f3661 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java @@ -19,6 +19,7 @@ import org.openhab.core.library.types.DecimalType; import org.openhab.core.thing.type.ChannelType; import org.openhab.core.types.Type; +import org.openhab.core.types.UnDefType; import com.google.gson.JsonElement; @@ -51,9 +52,9 @@ protected boolean fromBindingValidation(JsonElement value, String type) { } @Override - protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { + protected Type fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { if ("----".equals(value.getAsString())) { - return new DecimalType(0); + return UnDefType.UNDEF; } else { return new DecimalType(value.getAsInt()); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java index f1c3942a9e851..a77573bb18bec 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -70,8 +70,7 @@ public void dispose() { @Override public void initialize() { - updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NOT_YET_READY, - "Waiting bridge initialization, reading metadata in background"); + updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "@text/offline.waiting-bridge-initialization"); SiemensHvacBridgeConfig lcConfig = getConfigAs(SiemensHvacBridgeConfig.class); String baseUrl = null; @@ -79,7 +78,7 @@ public void initialize() { baseUrl = lcConfig.baseUrl; if (baseUrl.isEmpty()) { - logger.debug("baseUrl is mandatory on configuration !"); + logger.debug("@text/thing-handler.msg.baseUrlMandatory"); return; } @@ -125,7 +124,7 @@ private void initializeCode() { updateStatus(ThingStatus.ONLINE); } catch (SiemensHvacException ex) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - String.format("Error occurs during gateway initialization: %s", ex.getMessage())); + String.format("@text/thing-handler.msg.errorGatewayInit", ex.getMessage())); } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml index 90ac868b4fbc6..c2fdd67922533 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml @@ -1,11 +1,34 @@ - + xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 + https://openhab.org/schemas/addon-1.0.0.xsd"> + binding SiemensHvac Binding This is the binding for SiemensHvac. - local + + String + + + + + + upnp + + + + manufacturer + Signify + + + + diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties new file mode 100644 index 0000000000000..2a8222e3eb064 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties @@ -0,0 +1,22 @@ +# add-on + +addon.siemenshvac.name = SiemensHvac Binding +addon.siemenshvac.description = This is the binding for SiemensHvac. + +# thing types + +thing-type.siemenshvac.ozw672.label = OZW672 IP Gateway +thing-type.siemenshvac.ozw672.description = This is a OZW672 IP interface + +# thing types config + +thing-type.config.siemenshvac.ozw672.baseUrl.label = Base URL +thing-type.config.siemenshvac.ozw672.baseUrl.description = The URL of the Siemens Hvac IP gateway. Must be in format http://hostname/ or https://hostname/. Don't forget the trailing '/' +thing-type.config.siemenshvac.ozw672.userName.label = User Name +thing-type.config.siemenshvac.ozw672.userName.description = User name of the Siemens Hvac gateway +thing-type.config.siemenshvac.ozw672.userPassword.label = User Password +thing-type.config.siemenshvac.ozw672.userPassword.description = User password of the Siemens Hvac gateway + +offline.waiting-bridge-initialization = Waiting bridge initialization, reading metadata in background + +thing-handler.msg.baseUrlMandatory = baseUrl is mandatory on configuration ! \ No newline at end of file diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr.properties b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr.properties new file mode 100644 index 0000000000000..fe2b0d649f0d4 --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr.properties @@ -0,0 +1,23 @@ +# add-on + +addon.siemenshvac.name = SiemensHvac Binding +addon.siemenshvac.description = Binding pour SiemensHvac. + +# thing types + +thing-type.siemenshvac.ozw672.label = Passerelle IP OZW672 +thing-type.siemenshvac.ozw672.description = Interface IP OZW672 + +# thing types config + +thing-type.config.siemenshvac.ozw672.baseUrl.label = URL de base +thing-type.config.siemenshvac.ozw672.baseUrl.description = L' URL de la passerelle IP Siemens Hvac. Doit être de la forme http://hostname/ or https://hostname/. Ne pas oublier le dernier slash'/' +thing-type.config.siemenshvac.ozw672.userName.label = Nom de l'utilisateur +thing-type.config.siemenshvac.ozw672.userName.description = Nom de l'utilisateur de la passerelle Siemens Hvac +thing-type.config.siemenshvac.ozw672.userPassword.label = Mot de passe de l'utilisateur +thing-type.config.siemenshvac.ozw672.userPassword.description = Mot de passe de l'utilisateur de la passerelle Siemens Hvac + +offline.waiting-bridge-initialization = Attente de l''initialisation de la passerelle, lecture des méta-données en tâche de fond + +thing-handler.msg.baseUrlMandatory = l'URL de base est obligatoire dans la configuration ! +thing-handler.msg.errorGatewayInit = Erreur pendant l'initialisation de la passerelle: %s \ No newline at end of file From a237d996d6f3e06f456aa8f829713a65e23d22ba Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 7 May 2024 11:44:46 +0200 Subject: [PATCH 161/214] fix CI error Signed-off-by: Laurent ARNAL --- .../src/main/resources/OH-INF/addon/addon.xml | 6 ++---- .../src/main/resources/OH-INF/i18n/siemenshvac.properties | 3 ++- .../main/resources/OH-INF/i18n/siemenshvac_fr.properties | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml index c2fdd67922533..72ae6cacb8663 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml @@ -1,8 +1,6 @@ - + binding diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties index 2a8222e3eb064..441b15f957ee4 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties @@ -19,4 +19,5 @@ thing-type.config.siemenshvac.ozw672.userPassword.description = User password of offline.waiting-bridge-initialization = Waiting bridge initialization, reading metadata in background -thing-handler.msg.baseUrlMandatory = baseUrl is mandatory on configuration ! \ No newline at end of file +thing-handler.msg.baseUrlMandatory = baseUrl is mandatory on configuration ! +thing-handler.msg.errorGatewayInit = Error occurs during gateway initialization: %s diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr.properties b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr.properties index fe2b0d649f0d4..d11e42a5fe750 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr.properties +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr.properties @@ -20,4 +20,4 @@ thing-type.config.siemenshvac.ozw672.userPassword.description = Mot de passe de offline.waiting-bridge-initialization = Attente de l''initialisation de la passerelle, lecture des méta-données en tâche de fond thing-handler.msg.baseUrlMandatory = l'URL de base est obligatoire dans la configuration ! -thing-handler.msg.errorGatewayInit = Erreur pendant l'initialisation de la passerelle: %s \ No newline at end of file +thing-handler.msg.errorGatewayInit = Erreur pendant l'initialisation de la passerelle: %s From 82eb3bf2b1c0f9674ec384d5e98b58ad2a142360 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 7 May 2024 11:48:06 +0200 Subject: [PATCH 162/214] spotless:apply Signed-off-by: Laurent ARNAL --- .../src/main/resources/OH-INF/addon/addon.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml index 72ae6cacb8663..e9b4a155747fc 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml @@ -1,5 +1,7 @@ - From a2fc00b49cc84f586b2a68f8ef968fb104df2511 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 7 May 2024 11:53:00 +0200 Subject: [PATCH 163/214] fix CI error Signed-off-by: Laurent ARNAL --- .../src/main/resources/OH-INF/addon/addon.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml index e9b4a155747fc..a1bb5631af335 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml @@ -2,7 +2,7 @@ + https://openhab.org/schemas/addon-1.0.0.xsd"> binding From 74e5340ffa3244f65c84e4b1804ca4a7801448fa Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 7 May 2024 11:56:55 +0200 Subject: [PATCH 164/214] fix CI errors Signed-off-by: Laurent ARNAL --- .../src/main/resources/OH-INF/addon/addon.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml index a1bb5631af335..1e35cd9bfcaef 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml @@ -1,8 +1,7 @@ + xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd"> binding From 9fd33f8fc859853b33b0ac0431ca9678d5a67141 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 7 May 2024 12:02:41 +0200 Subject: [PATCH 165/214] fix CI errors Signed-off-by: Laurent ARNAL --- .../src/main/resources/OH-INF/addon/addon.xml | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml index 1e35cd9bfcaef..dad7b586efc2e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml @@ -1,5 +1,5 @@ - @@ -12,22 +12,4 @@ String - - - - upnp - - - - manufacturer - Signify - - - - From 6fa6aaeda2f4698efa6d9076bb6cd9063e993f6f Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 7 May 2024 16:32:51 +0200 Subject: [PATCH 166/214] add autodiscovering feature Signed-off-by: Laurent ARNAL --- .../src/main/resources/OH-INF/addon/addon.xml | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml index dad7b586efc2e..ff0d1bf5597ac 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml @@ -1,15 +1,24 @@ + xmlns:addon="https://openhab.org/schemas/addon/v1.0.0" + xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd "> binding SiemensHvac Binding This is the binding for SiemensHvac. local + + + + upnp + - String - - + + modelName + Web Server OZW672.01 + + + + From 0fbec96fcd361ad913eef602d2b6a416ffcfc6cb Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 7 May 2024 16:38:34 +0200 Subject: [PATCH 167/214] spotless:apply Signed-off-by: Laurent ARNAL --- .../src/main/resources/OH-INF/addon/addon.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml index ff0d1bf5597ac..4471d9eade2d6 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml @@ -1,15 +1,15 @@ + xmlns:addon="https://openhab.org/schemas/addon/v1.0.0" + xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd "> binding SiemensHvac Binding This is the binding for SiemensHvac. local - - + + upnp From 0c3c6a739bd02fef5696903b8d25f2444e3dac56 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 14 May 2024 12:24:50 +0200 Subject: [PATCH 168/214] div fixes based on last JLaur review Signed-off-by: Laurent ARNAL --- .../org.openhab.binding.siemenshvac/README.md | 7 +- .../SiemensHvacBindingConstants.java | 19 +-- .../internal/converter/TypeConverter.java | 3 +- .../converter/type/AbstractTypeConverter.java | 5 +- .../converter/type/CalendarTypeConverter.java | 4 +- .../converter/type/CheckboxTypeConverter.java | 4 +- .../converter/type/DateTimeTypeConverter.java | 10 +- .../converter/type/EnumTypeConverter.java | 4 +- .../converter/type/NumericTypeConverter.java | 4 +- .../converter/type/RadioTypeConverter.java | 12 +- .../type/SchedulerTypeConverter.java | 4 +- .../converter/type/StringTypeConverter.java | 4 +- .../type/TimeOfDayTypeConverter.java | 7 +- .../SiemensHvacDeviceDiscoveryService.java | 7 +- .../SiemensHvacBridgeBaseThingHandler.java | 4 +- .../handler/SiemensHvacHandlerImpl.java | 46 +----- .../SiemensHvacOZW672BridgeThingHandler.java | 2 +- .../SiemensHvacMetadataRegistryImpl.java | 94 +++++------- .../network/SiemensHvacConnector.java | 5 +- .../network/SiemensHvacConnectorImpl.java | 139 +++++++++--------- .../network/SiemensHvacRequestListener.java | 3 +- .../siemenshvac/internal/type/UidUtils.java | 16 +- .../OH-INF/i18n/siemenshvac.properties | 49 +++--- .../OH-INF/i18n/siemenshvac_fr.properties | 23 --- .../main/resources/OH-INF/thing/ozw672.xml | 2 +- 25 files changed, 191 insertions(+), 286 deletions(-) delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr.properties diff --git a/bundles/org.openhab.binding.siemenshvac/README.md b/bundles/org.openhab.binding.siemenshvac/README.md index ef5cf63df1bd2..425c5970869df 100644 --- a/bundles/org.openhab.binding.siemenshvac/README.md +++ b/bundles/org.openhab.binding.siemenshvac/README.md @@ -28,8 +28,9 @@ With this binding, you will be able to: Support many different things as the thing type is handle by autodiscovery. Mainly, it will first discover the gateway. -Currently test and support is the OZW672.x series. -No test done with OZW772.x series, but it should work as well. +Currently test was done with the OZW672.x series. +No test was conduct using the OZW772.x series, but it should work as well. +You can request support to the community forum is you encounter troubles with an untested device. After, it will discover thing inside your PAC, mainly main controller of type RVS... Only test in real condition with RVS41.813/327 but should work with all other type as the access interface is standard. @@ -54,8 +55,6 @@ baseUrl | yes | | The address of the OZW672 de userName | yes | Administrator | The user name to log into the OZW672 userPass | yes | | The user password to log into the OZW672 -## Thing Configuration - ## Channels Channels are auto-discovered, you will find them on the RVS things. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java index 52512fe6d3c29..bbe43e7cf4aed 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java @@ -29,12 +29,8 @@ public class SiemensHvacBindingConstants { public static final String CONFIG_DESCRIPTION_URI_CHANNEL = "channel-type:siemenshvac:config"; // List of all Thing Type UIDs - // Thing Type UIDs public static final ThingTypeUID THING_TYPE_OZW672 = new ThingTypeUID(BINDING_ID, "ozw672"); - // List of all Channel ids - public static final String CHANNEL_1 = "channel1"; - public static final String IP_ADDRESS = "ipAddress"; public static final String BASE_URL = "baseUrl"; @@ -42,13 +38,6 @@ public class SiemensHvacBindingConstants { public static final String PROPERTY_VENDOR_NAME = "Siemens"; - public static final String ITEM_TYPE_SWITCH = "Switch"; - public static final String ITEM_TYPE_CONTACT = "Contact"; - public static final String ITEM_TYPE_STRING = "String"; - public static final String ITEM_TYPE_NUMBER = "Number"; - public static final String ITEM_TYPE_ENUMERATION = "Number"; - public static final String ITEM_TYPE_DATETIME = "DateTime"; - public static final String CONFIG_DESCRIPTION_URI_THING_PREFIX = "thing-type"; public static final String DPT_TYPE_ENUM = "Enumeration"; @@ -65,13 +54,9 @@ public class SiemensHvacBindingConstants { public static final String CATEGORY_THING_HVAC = "HVAC"; public static final String CATEGORY_CHANNEL_WIDGETS_NUMBER = "Number"; - public static final String CATEGORY_CHANNEL_WIDGETS_SLIDER = "Slider"; public static final String CATEGORY_CHANNEL_WIDGETS_SWITCH = "Switch"; - public static final String CATEGORY_CHANNEL_WIDGETS_TEXT = "Text"; - public static final String CATEGORY_CHANNEL_WIDGETS_GROUP = "Group"; - - public static final String CATEGORY_CHANNEL_PROPS_TEMP = "Temperature"; - public static final String CATEGORY_CHANNEL_PROPS_TIME = "Time"; + public static final String CATEGORY_CHANNEL_WIDGETS_TEMP = "Temperature"; + public static final String CATEGORY_CHANNEL_WIDGETS_TIME = "Time"; public static final String CATEGORY_CHANNEL_CONTROL_HEATING = "Heating"; } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java index a296a410265ef..94feda6b00fab 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java @@ -15,6 +15,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.thing.type.ChannelType; +import org.openhab.core.types.State; import org.openhab.core.types.Type; import com.google.gson.JsonObject; @@ -36,7 +37,7 @@ public interface TypeConverter { /** * Converts a siemensHvac value to an openHAB type. */ - Type convertFromBinding(JsonObject dp, ChannelType tp) throws ConverterException; + State convertFromBinding(JsonObject dp, ChannelType tp) throws ConverterException; /** * get underlying channel type to construct channel type UID diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java index a53d572d13b0a..4174ae407ee72 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java @@ -19,6 +19,7 @@ import org.openhab.binding.siemenshvac.internal.converter.TypeConverter; import org.openhab.core.thing.type.ChannelType; import org.openhab.core.types.Command; +import org.openhab.core.types.State; import org.openhab.core.types.Type; import org.openhab.core.types.UnDefType; import org.slf4j.Logger; @@ -52,7 +53,7 @@ public abstract class AbstractTypeConverter implements TypeConverter { } @Override - public Type convertFromBinding(JsonObject dp, ChannelType tp) throws ConverterException { + public State convertFromBinding(JsonObject dp, ChannelType tp) throws ConverterException { String type = null; JsonElement value = null; @@ -110,7 +111,7 @@ public Type convertFromBinding(JsonObject dp, ChannelType tp) throws ConverterEx /** * Converts the datapoint value to an openHAB type. */ - protected abstract Type fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException; + protected abstract State fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException; /** * get underlying channel type to construct channel type UID diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java index 0b4ddd08d40e9..951328969dd94 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java @@ -14,8 +14,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.thing.type.ChannelType; @@ -63,7 +63,7 @@ public String getChannelType(boolean writeAccess) { @Override public String getItemType(boolean writeAccess) { - return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; + return CoreItemFactory.DATETIME; } @Override diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java index f55d985df35d3..cef45a71482eb 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java @@ -14,8 +14,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.DecimalType; import org.openhab.core.thing.type.ChannelType; import org.openhab.core.types.Type; @@ -56,7 +56,7 @@ public String getChannelType(boolean writeAccess) { @Override public String getItemType(boolean writeAccess) { - return SiemensHvacBindingConstants.ITEM_TYPE_CONTACT; + return CoreItemFactory.CONTACT; } @Override diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java index 491a75c57a8c8..ec71a5694bb31 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java @@ -21,9 +21,9 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.core.i18n.TimeZoneProvider; +import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.thing.type.ChannelType; import org.openhab.core.types.Type; @@ -68,7 +68,7 @@ protected boolean fromBindingValidation(JsonElement value, String type) { @Override protected DateTimeType fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { if ("----".equals(value.getAsString())) { - return new DateTimeType(); + return new DateTimeType(ZonedDateTime.now(this.timeZoneProvider.getTimeZone())); } else { String[] formats = { "EEEE, d. LLLL yyyy HH:mm[:ss]", "d. LLLL yyyy HH:mm[:ss]", "d. LLLL yyyy", "d. LLLL" }; @@ -82,7 +82,7 @@ protected DateTimeType fromBinding(JsonElement value, String type, ChannelType t if (i == 3) { formatterBuilder = formatterBuilder.parseDefaulting(ChronoField.YEAR, - ZonedDateTime.now().getYear()); + ZonedDateTime.now(this.timeZoneProvider.getTimeZone()).getYear()); } LocalDateTime parsedDate = LocalDateTime.parse(value.getAsString(), @@ -93,7 +93,7 @@ protected DateTimeType fromBinding(JsonElement value, String type, ChannelType t return new DateTimeType(zdt); } catch (DateTimeParseException ex) { if (i == formats.length - 1) { - throw new ConverterException("Can't parse the date for :" + value); + throw new ConverterException("Can't parse the date for:" + value); } } } @@ -109,7 +109,7 @@ public String getChannelType(boolean writeAccess) { @Override public String getItemType(boolean writeAccess) { - return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; + return CoreItemFactory.DATETIME; } @Override diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java index dda7aa7196615..df3a8c31a4142 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java @@ -14,8 +14,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.DecimalType; import org.openhab.core.thing.type.ChannelType; import org.openhab.core.types.Type; @@ -62,7 +62,7 @@ public String getChannelType(boolean writeAccess) { @Override public String getItemType(boolean writeAccess) { - return SiemensHvacBindingConstants.ITEM_TYPE_ENUMERATION; + return CoreItemFactory.NUMBER; } @Override diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java index 4aea4e4a071a9..df96d4fc28200 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java @@ -14,8 +14,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.thing.type.ChannelType; @@ -70,7 +70,7 @@ public String getChannelType(boolean writeAccess) { @Override public String getItemType(boolean writeAccess) { - return SiemensHvacBindingConstants.ITEM_TYPE_NUMBER; + return CoreItemFactory.NUMBER; } @Override diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java index 4e25b4f53aaa7..e02c5dc31626d 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java @@ -16,15 +16,17 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.thing.type.ChannelType; import org.openhab.core.types.Command; +import org.openhab.core.types.State; import org.openhab.core.types.StateDescription; import org.openhab.core.types.StateOption; import org.openhab.core.types.Type; +import org.openhab.core.types.UnDefType; import com.google.gson.JsonElement; @@ -74,8 +76,8 @@ protected boolean fromBindingValidation(JsonElement value, String type) { } @Override - protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { - DecimalType updateVal = new DecimalType(); + protected State fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { + State updateVal = UnDefType.UNDEF; String valueSt = value.getAsString(); StateDescription sd = tp.getState(); @@ -107,9 +109,9 @@ public String getChannelType(boolean writeAccess) { @Override public String getItemType(boolean writeAccess) { if (writeAccess) { - return SiemensHvacBindingConstants.ITEM_TYPE_SWITCH; + return CoreItemFactory.SWITCH; } else { - return SiemensHvacBindingConstants.ITEM_TYPE_CONTACT; + return CoreItemFactory.CONTACT; } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SchedulerTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SchedulerTypeConverter.java index 6fbd1a33ca063..f49812e7b0845 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SchedulerTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SchedulerTypeConverter.java @@ -14,8 +14,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.thing.type.ChannelType; @@ -63,7 +63,7 @@ public String getChannelType(boolean writeAccess) { @Override public String getItemType(boolean writeAccess) { - return SiemensHvacBindingConstants.ITEM_TYPE_DATETIME; + return CoreItemFactory.DATETIME; } @Override diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/StringTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/StringTypeConverter.java index 16d0728aa9cc7..84a762ec6aff9 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/StringTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/StringTypeConverter.java @@ -14,8 +14,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.StringType; @@ -68,7 +68,7 @@ public String getChannelType(boolean writeAccess) { @Override public String getItemType(boolean writeAccess) { - return SiemensHvacBindingConstants.ITEM_TYPE_STRING; + return CoreItemFactory.STRING; } @Override diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java index a2ff2662f3661..aeed3f63f108c 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java @@ -14,10 +14,11 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.DecimalType; import org.openhab.core.thing.type.ChannelType; +import org.openhab.core.types.State; import org.openhab.core.types.Type; import org.openhab.core.types.UnDefType; @@ -52,7 +53,7 @@ protected boolean fromBindingValidation(JsonElement value, String type) { } @Override - protected Type fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { + protected State fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { if ("----".equals(value.getAsString())) { return UnDefType.UNDEF; } else { @@ -67,7 +68,7 @@ public String getChannelType(boolean writeAccess) { @Override public String getItemType(boolean writeAccess) { - return SiemensHvacBindingConstants.ITEM_TYPE_NUMBER; + return CoreItemFactory.NUMBER; } @Override diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java index f76b19d66aaac..8471511d8faf5 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java @@ -112,10 +112,6 @@ public void startScan() { String addr = device.getAddr(); String serialNr = device.getSerialNr(); - if (name.indexOf("OZW672") >= 0) { - continue; - } - logger.debug("Find devices: {} / {} / {} / {}", name, type, addr, serialNr); String typeSn = UidUtils.sanetizeId(type); @@ -128,7 +124,7 @@ public void startScan() { if (thingUID != null) { Map properties = new HashMap<>(4); - properties.put("name", name); + properties.put(Thing.PROPERTY_MODEL_ID, name); properties.put("type", type); properties.put("addr", addr); properties.put(Thing.PROPERTY_SERIAL_NUMBER, serialNr); @@ -153,6 +149,7 @@ protected synchronized void stopScan() { public void setThingHandler(@Nullable ThingHandler handler) { if (handler instanceof SiemensHvacBridgeBaseThingHandler siemensHvacBridgeHandler) { this.siemensHvacBridgeHandler = siemensHvacBridgeHandler; + this.siemensHvacBridgeHandler.registerDiscoveryListener(this); } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java index a77573bb18bec..2f8011b9034b9 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -78,7 +78,7 @@ public void initialize() { baseUrl = lcConfig.baseUrl; if (baseUrl.isEmpty()) { - logger.debug("@text/thing-handler.msg.baseUrlMandatory"); + logger.debug("@text/offline.error-gateway-init"); return; } @@ -124,7 +124,7 @@ private void initializeCode() { updateStatus(ThingStatus.ONLINE); } catch (SiemensHvacException ex) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - String.format("@text/thing-handler.msg.errorGatewayInit", ex.getMessage())); + String.format("@text/offline.error-gateway-init", ex.getMessage())); } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index 2d754ec0c6366..d659fc29e7ec0 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -81,16 +81,6 @@ public SiemensHvacHandlerImpl(Thing thing, @Nullable SiemensHvacConnector hvacCo this.channelTypeRegistry = channelTypeRegistry; } - @Override - protected void updateStatus(ThingStatus status) { - super.updateStatus(status); - } - - @Override - public void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) { - super.updateStatus(status, statusDetail, description); - } - @Override public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) { } @@ -139,7 +129,7 @@ private void pollingCode() { int previousRequestCount = lcHvacConnector.getRequestCount(); int previousErrorCount = lcHvacConnector.getErrorCount(); - logger.debug("readChannels :"); + logger.debug("readChannels:"); for (Channel channel : chList) { readChannel(channel); } @@ -164,11 +154,11 @@ private void pollingCode() { if (lcHvacConnector.getErrorSource() == ErrorSource.ErrorBridge) { if (bridgeHandler != null) { bridgeHandler.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - String.format("Communication ErrorRate to gateway is too high : %f", errorRate)); + String.format("Communication ErrorRate to gateway is too high: %f", errorRate)); } } else if (lcHvacConnector.getErrorSource() == ErrorSource.ErrorThings) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - String.format("Communication ErrorRate to thing is too high : %f", errorRate)); + String.format("Communication ErrorRate to thing is too high: %f", errorRate)); } } else { updateStatus(ThingStatus.ONLINE); @@ -246,7 +236,7 @@ public void decodeReadDp(@Nullable JsonObject response, @Nullable String uid, @N if (typer != null) { TypeConverter converter = ConverterFactory.getConverter(typer); - State state = (State) converter.convertFromBinding(subResult, tp); + State state = converter.convertFromBinding(subResult, tp); updateState(updateKey, state); } } catch (ConverterTypeException ex) { @@ -268,7 +258,7 @@ private void readDp(String dp, String uid, ChannelType tp, String type, boolean try { lockObj.lock(); - logger.trace("Start read : {}", dp); + logger.trace("Start read: {}", dp); String request = "api/menutree/read_datapoint.json?Id=" + dp; logger.debug("siemensHvac:ReadDp:DoRequest(): {}", request); @@ -285,7 +275,7 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { return; } - logger.trace("End read : {}", dp); + logger.trace("End read: {}", dp); if (response instanceof JsonObject jsonResponse) { decodeReadDp(jsonResponse, uid, dp, tp, type); @@ -299,9 +289,6 @@ public void execute(java.net.URI uri, int status, @Nullable Object response) { decodeReadDp(js, uid, dp, tp, type); } } - } catch (Exception e) { - logger.debug("siemensHvac:ReadDp:Error during dp reading: {} ; {}", dp, e.getLocalizedMessage()); - // Reset sessionId so we redone _auth on error } finally { logger.trace("End read: {}", dp); lockObj.unlock(); @@ -349,25 +336,6 @@ private void writeDp(String dp, Type dpVal, ChannelType tp, String type) { } catch (ConverterException ex) { logger.warn("{}, please check the item type and the commands in your scripts", ex.getMessage()); } - - /* - * if (dpVal instanceof PercentType percentValue) { - * valUpdate = percentValue.toString(); - * } else if (dpVal instanceof DecimalType decimalValue) { - * valUpdate = decimalValue.toString(); - * } else if (dpVal instanceof StringType stringValue) { - * valUpdate = stringValue.toString(); - * - * if ("Enumeration".equals(type)) { - * String[] valuesUpdateDp = valUpdate.split(":"); - * valUpdate = valuesUpdateDp[0]; - * } - * } - */ - - } catch (Exception e) { - logger.debug("siemensHvac:ReadDp:Error during dp reading: {}: {}", dp, e.getLocalizedMessage()); - // Reset sessionId so we redone _auth on error } finally { logger.debug("End write: {}", dp); lockObj.unlock(); @@ -415,7 +383,7 @@ private Command applyState(ChannelType tp, Command command) { } if (doMods) { - result = new DecimalType("" + v1); + result = new DecimalType(v1); } } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java index 8704df31e024b..e22e7d7897cfe 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java @@ -52,7 +52,7 @@ public SiemensHvacOZW672BridgeThingHandler(Bridge bridge, @Nullable NetworkAddre @Override public void initialize() { if (logger.isDebugEnabled()) { - logger.debug("Initialize() bridge : {}", getBuildDate()); + logger.debug("Initialize() bridge: {}", getBuildDate()); } super.initialize(); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java index 7d87364bce84e..121cbfa8bbc10 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -246,8 +246,7 @@ public int unresolveCount() { SiemensHvacMetadata node = dptMap.get(key); if (node != null) { - if (node.getClass() == SiemensHvacMetadataDataPoint.class) { - SiemensHvacMetadataDataPoint dpi = (SiemensHvacMetadataDataPoint) node; + if (node instanceof SiemensHvacMetadataDataPoint dpi) { if (!dpi.getDetailsResolved()) { count++; } @@ -274,7 +273,7 @@ public void readMeta() throws SiemensHvacException { } if (lcHvacConnector == null) { - logger.debug("SiemensHvacMetadataRegistryImpl:ReadMeta() : lHvacConnector not initialize."); + logger.debug("SiemensHvacMetadataRegistryImpl:ReadMeta(): lHvacConnector not initialize."); return; } @@ -282,8 +281,7 @@ public void readMeta() throws SiemensHvacException { SiemensHvacBridgeConfig config = lcHvacConnector.getBridgeConfiguration(); if (config == null) { - logger.debug("SiemensHvacMetadataRegistryImpl:ReadMeta() : config not initialize."); - return; + throw new SiemensHvacException("@offline.config-not-init"); } SiemensHvacMetadataUser user = null; @@ -294,8 +292,7 @@ public void readMeta() throws SiemensHvacException { } if (user == null) { - logger.error("SiemensHvacMetadataRegistryImpl:ReadMeta() : cannot find user, aborting."); - return; + throw new SiemensHvacException("@offline.user-not-find"); } logger.trace("siemensHvac:Initialization():Begin_0001"); @@ -415,43 +412,40 @@ private void generateThingsType(SiemensHvacMetadata child, List props = new Hashtable(); - props.put("dptId", "" + dpt.getDptId()); - props.put("id", "" + dpt.getId()); - props.put("subId", "" + dpt.getSubId()); - props.put("groupdId", "" + dpt.getGroupId()); + props.put("dptId", "" + metadataDataPoint.getDptId()); + props.put("id", "" + metadataDataPoint.getId()); + props.put("subId", "" + metadataDataPoint.getSubId()); + props.put("groupdId", "" + metadataDataPoint.getGroupId()); - String id = dataPoint.getId() + "-" + UidUtils.sanetizeId(dataPoint.getShortDesc()); + String id = metadataDataPoint.getId() + "-" + + UidUtils.sanetizeId(metadataDataPoint.getShortDesc()); if (channelType != null) { ChannelDefinition channelDef = new ChannelDefinitionBuilder(id, channelType.getUID()) - .withLabel(dataPoint.getShortDesc()).withDescription(dataPoint.getLongDesc()) - .withProperties(props).build(); + .withLabel(metadataDataPoint.getShortDesc()) + .withDescription(metadataDataPoint.getLongDesc()).withProperties(props).build(); channelDefinitions.add(channelDef); } } - } catch (Exception ex) { + } catch (SiemensHvacException ex) { logger.warn("Unable to create channel for: {}", childDt); } } @@ -477,7 +471,7 @@ private void generateThingsType(SiemensHvacMetadata child, List cookies = cookieStore.getCookies(); + if (statusCode == HttpStatus.OK_200) { + String result = response.getContentAsString(); - for (HttpCookie httpCookie : cookies) { - if (httpCookie.getName().equals("SessionId")) { - sessionIdHttp = httpCookie.getValue(); - } + if (http) { + CookieStore cookieStore = httpClient.getCookieStore(); + List cookies = cookieStore.getCookies(); + for (HttpCookie httpCookie : cookies) { + if (httpCookie.getName().equals("SessionId")) { + sessionIdHttp = httpCookie.getValue(); } - if (sessionIdHttp == null) { - logger.debug("Session request auth was unsuccessful in _doAuth()"); - } - } else { - if (result != null) { - JsonObject resultObj = getGson().fromJson(result, JsonObject.class); - - if (resultObj != null && resultObj.has("Result")) { - JsonElement resultVal = resultObj.get("Result"); - JsonObject resultObj2 = resultVal.getAsJsonObject(); - - if (resultObj2.has("Success")) { - boolean successVal = resultObj2.get("Success").getAsBoolean(); - - if (successVal) { - if (resultObj.has("SessionId")) { - sessionId = resultObj.get("SessionId").getAsString(); - logger.debug("Have new SessionId: {} ", sessionId); - } + } + + if (sessionIdHttp == null) { + logger.debug("Session request auth was unsuccessful in _doAuth()"); + } + } else { + if (result != null) { + JsonObject resultObj = getGson().fromJson(result, JsonObject.class); + + if (resultObj != null && resultObj.has("Result")) { + JsonElement resultVal = resultObj.get("Result"); + JsonObject resultObj2 = resultVal.getAsJsonObject(); + + if (resultObj2.has("Success")) { + boolean successVal = resultObj2.get("Success").getAsBoolean(); + + if (successVal) { + if (resultObj.has("SessionId")) { + sessionId = resultObj.get("SessionId").getAsString(); + logger.debug("Have new SessionId: {} ", sessionId); } } } + } - logger.debug("siemensHvac:doAuth:decodeResponse:()"); - - if (sessionId == null) { - throw new SiemensHvacException( - "Session request auth was unsuccessful in _doAuth(), please verify login parameters"); - } + logger.debug("siemensHvac:doAuth:decodeResponse:()"); + if (sessionId == null) { + throw new SiemensHvacException( + "Session request auth was unsuccessful in _doAuth(), please verify login parameters"); } } + } } - - logger.trace("siemensHvac:doAuth:connect()"); - - } catch (Exception ex) { - logger.debug("siemensHvac:doAuth:error() {}", ex.getLocalizedMessage()); - throw new SiemensHvacException("Error during authentification", ex); } + + logger.trace("siemensHvac:doAuth:connect()"); } } @Override - public @Nullable String doBasicRequest(String uri) throws Exception { + public @Nullable String doBasicRequest(String uri) throws SiemensHvacException { return doBasicRequest(uri, null); } - public @Nullable String doBasicRequestAsync(String uri, @Nullable SiemensHvacCallback callback) throws Exception { + public @Nullable String doBasicRequestAsync(String uri, @Nullable SiemensHvacCallback callback) + throws SiemensHvacException { return doBasicRequest(uri, callback); } - public @Nullable String doBasicRequest(String uri, @Nullable SiemensHvacCallback callback) throws Exception { + public @Nullable String doBasicRequest(String uri, @Nullable SiemensHvacCallback callback) + throws SiemensHvacException { if (sessionIdHttp == null) { doAuth(true); } @@ -427,7 +426,11 @@ private void doAuth(boolean http) throws SiemensHvacException { cookie.setPath("/"); cookie.setVersion(0); - c.add(new URI(baseUri), cookie); + try { + c.add(new URI(baseUri), cookie); + } catch (URISyntaxException ex) { + throw new SiemensHvacException(String.format("URI is not correctly formated: %s", baseUri), ex); + } logger.debug("Execute request: {}", uri); final Request request = httpClient.newRequest(baseUri + mUri); @@ -438,9 +441,7 @@ private void doAuth(boolean http) throws SiemensHvacException { int statusCode = response.getStatus(); if (statusCode == HttpStatus.OK_200) { - String result = response.getContentAsString(); - - return result; + return response.getContentAsString(); } } @@ -474,7 +475,7 @@ private void doAuth(boolean http) throws SiemensHvacException { return null; } - } catch (Exception e) { + } catch (SiemensHvacException e) { logger.warn("siemensHvac:DoRequest:Exception by executing jsonRequest: {} ; {} ", req, e.getLocalizedMessage()); } @@ -484,7 +485,7 @@ private void doAuth(boolean http) throws SiemensHvacException { @Override public void displayRequestStats() { - logger.debug("DisplayRequestStats : "); + logger.debug("DisplayRequestStats: "); logger.debug(" currentRuning : {}", getCurrentHandlerRegistryCount()); logger.debug(" errors : {}", getHandlerInErrorRegistryCount()); } @@ -500,7 +501,7 @@ public void waitAllPendingRequest() { allRequestDone = false; int currentRequestCount = getCurrentHandlerRegistryCount(); - logger.debug("WaitAllPendingRequest:waitAllRequestDone {} : {}", idx, currentRequestCount); + logger.debug("WaitAllPendingRequest:waitAllRequestDone {}: {}", idx, currentRequestCount); if (currentRequestCount == 0) { allRequestDone = true; @@ -545,7 +546,7 @@ public void checkStaleRequest() { } } - logger.debug("check stale request::end : {}", staleRequest); + logger.debug("check stale request::end: {}", staleRequest); } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java index b91d4c7851d8e..770eb43ada1b5 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacRequestListener.java @@ -30,6 +30,7 @@ import org.eclipse.jetty.client.api.Response.SuccessListener; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.util.BufferingResponseListener; +import org.openhab.binding.siemenshvac.internal.type.SiemensHvacException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -239,7 +240,7 @@ public void onComplete(@Nullable Result result) { hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorBridge, mayRetry); return; } - } catch (Exception ex) { + } catch (SiemensHvacException ex) { logger.debug("An error occurred", ex); } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index 29b7155569dd3..16e1dc70281e4 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -48,16 +48,7 @@ public static String sanetizeId(String label) { result = result.replaceAll("\\p{M}", ""); } - result = result.replace(' ', '_'); - result = result.replace(':', '_'); - result = result.replace('.', '_'); - result = result.replace('\'', '_'); - result = result.replace('(', '_'); - result = result.replace(')', '_'); - result = result.replace('&', '_'); - result = result.replace('/', '_'); - result = result.replace('°', '_'); - result = result.replace('+', '_'); + result = result.replaceAll("[^a-zA-Z0-9_]", "_").toLowerCase(); return result; } @@ -71,10 +62,6 @@ public static ThingTypeUID generateThingTypeUID(SiemensHvacMetadataDevice device return new ThingTypeUID(SiemensHvacBindingConstants.BINDING_ID, type); } - public static ThingTypeUID generateThingTypeUID(String name) { - return new ThingTypeUID(SiemensHvacBindingConstants.BINDING_ID, name); - } - /** * get a more user friendly description from English short descriptor * @@ -129,7 +116,6 @@ private static String normalizeDescriptor(String descriptor) { result = result.replace('/', '-'); result = result.replace(' ', '-'); result = result.replace("+", "-"); - result = result.replace("--", "-"); result = result.replace("standard-tsp-hc", "time-switch-program-standard"); result = result.replace("standard-tsp-4", "time-switch-program-standard"); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties index 441b15f957ee4..cb441337cab86 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties @@ -1,23 +1,26 @@ -# add-on - -addon.siemenshvac.name = SiemensHvac Binding -addon.siemenshvac.description = This is the binding for SiemensHvac. - -# thing types - -thing-type.siemenshvac.ozw672.label = OZW672 IP Gateway -thing-type.siemenshvac.ozw672.description = This is a OZW672 IP interface - -# thing types config - -thing-type.config.siemenshvac.ozw672.baseUrl.label = Base URL -thing-type.config.siemenshvac.ozw672.baseUrl.description = The URL of the Siemens Hvac IP gateway. Must be in format http://hostname/ or https://hostname/. Don't forget the trailing '/' -thing-type.config.siemenshvac.ozw672.userName.label = User Name -thing-type.config.siemenshvac.ozw672.userName.description = User name of the Siemens Hvac gateway -thing-type.config.siemenshvac.ozw672.userPassword.label = User Password -thing-type.config.siemenshvac.ozw672.userPassword.description = User password of the Siemens Hvac gateway - -offline.waiting-bridge-initialization = Waiting bridge initialization, reading metadata in background - -thing-handler.msg.baseUrlMandatory = baseUrl is mandatory on configuration ! -thing-handler.msg.errorGatewayInit = Error occurs during gateway initialization: %s +# add-on + +addon.siemenshvac.name = SiemensHvac Binding +addon.siemenshvac.description = This is the binding for SiemensHvac. + +# thing types + +thing-type.siemenshvac.ozw672.label = OZW672 IP Gateway +thing-type.siemenshvac.ozw672.description = This is a OZW672 IP interface + +# thing types config + +thing-type.config.siemenshvac.ozw672.baseUrl.label = Base URL +thing-type.config.siemenshvac.ozw672.baseUrl.description = The URL of the Siemens Hvac IP gateway. Must be in format http://hostname/ or https://hostname/. Don't forget the trailing '/' +thing-type.config.siemenshvac.ozw672.userName.label = User Name +thing-type.config.siemenshvac.ozw672.userName.description = User name of the Siemens Hvac gateway +thing-type.config.siemenshvac.ozw672.userPassword.label = User Password +thing-type.config.siemenshvac.ozw672.userPassword.description = User password of the Siemens Hvac gateway + +offline.waiting-bridge-initialization = Waiting bridge initialization, reading metadata in background + +offline.baseurl-mandatory = baseUrl is mandatory on configuration ! +offline.error-gateway-init = Error occurs during gateway initialization: %s + +offline.config-not-init = Config not initialize during reading metadata, aborting. +offline.user-not-find = Cannot find user during reading metadata, aborting. \ No newline at end of file diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr.properties b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr.properties deleted file mode 100644 index d11e42a5fe750..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac_fr.properties +++ /dev/null @@ -1,23 +0,0 @@ -# add-on - -addon.siemenshvac.name = SiemensHvac Binding -addon.siemenshvac.description = Binding pour SiemensHvac. - -# thing types - -thing-type.siemenshvac.ozw672.label = Passerelle IP OZW672 -thing-type.siemenshvac.ozw672.description = Interface IP OZW672 - -# thing types config - -thing-type.config.siemenshvac.ozw672.baseUrl.label = URL de base -thing-type.config.siemenshvac.ozw672.baseUrl.description = L' URL de la passerelle IP Siemens Hvac. Doit être de la forme http://hostname/ or https://hostname/. Ne pas oublier le dernier slash'/' -thing-type.config.siemenshvac.ozw672.userName.label = Nom de l'utilisateur -thing-type.config.siemenshvac.ozw672.userName.description = Nom de l'utilisateur de la passerelle Siemens Hvac -thing-type.config.siemenshvac.ozw672.userPassword.label = Mot de passe de l'utilisateur -thing-type.config.siemenshvac.ozw672.userPassword.description = Mot de passe de l'utilisateur de la passerelle Siemens Hvac - -offline.waiting-bridge-initialization = Attente de l''initialisation de la passerelle, lecture des méta-données en tâche de fond - -thing-handler.msg.baseUrlMandatory = l'URL de base est obligatoire dans la configuration ! -thing-handler.msg.errorGatewayInit = Erreur pendant l'initialisation de la passerelle: %s diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml index a72622b15003f..fa7b9dd78eacf 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml @@ -42,7 +42,7 @@ Number:Temperature - + Setpoint Heat Pump Temperature From bcdfa37f0d6a1a2e17a19faf8be4ae02a202310d Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 14 May 2024 12:34:16 +0200 Subject: [PATCH 169/214] fix unit test Signed-off-by: Laurent ARNAL --- .../binding/siemenshvac/internal/type/UidUtilsTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java b/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java index 6f1457f6641af..75eb50e8e3b1c 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java +++ b/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java @@ -25,8 +25,8 @@ public class UidUtilsTest { @Test public void testSanetizeId() throws Exception { - assertEquals(UidUtils.sanetizeId("Début heure été"), "Debut_heure_ete"); - assertEquals(UidUtils.sanetizeId("App.Ambiance 1"), "App_Ambiance_1"); - assertEquals(UidUtils.sanetizeId("Appareil d'ambiance P"), "Appareil_d_ambiance_P"); + assertEquals(UidUtils.sanetizeId("Début heure été"), "debut_heure_ete"); + assertEquals(UidUtils.sanetizeId("App.Ambiance 1"), "app_ambiance_1"); + assertEquals(UidUtils.sanetizeId("Appareil d'ambiance P"), "appareil_d_ambiance_p"); } } From 5c1d1a6743ce01719b3799fc1190d580be5b22ed Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 14 May 2024 12:48:29 +0200 Subject: [PATCH 170/214] fix code violations Signed-off-by: Laurent ARNAL --- .../src/main/resources/OH-INF/i18n/siemenshvac.properties | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties index cb441337cab86..361df43f75380 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties @@ -17,10 +17,12 @@ thing-type.config.siemenshvac.ozw672.userName.description = User name of the Sie thing-type.config.siemenshvac.ozw672.userPassword.label = User Password thing-type.config.siemenshvac.ozw672.userPassword.description = User password of the Siemens Hvac gateway +# offline message + offline.waiting-bridge-initialization = Waiting bridge initialization, reading metadata in background offline.baseurl-mandatory = baseUrl is mandatory on configuration ! offline.error-gateway-init = Error occurs during gateway initialization: %s offline.config-not-init = Config not initialize during reading metadata, aborting. -offline.user-not-find = Cannot find user during reading metadata, aborting. \ No newline at end of file +offline.user-not-find = Cannot find user during reading metadata, aborting. From 907373f19df3f75ec99c7df9d980330c5e38e446 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 14 May 2024 13:08:37 +0200 Subject: [PATCH 171/214] review upnp discovery Signed-off-by: Laurent ARNAL --- .../internal/discovery/SiemenesHvacDiscoveryParticipant.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java index f67e5c40770a2..a3c4554d63010 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java @@ -41,7 +41,7 @@ * @author Laurent Arnal - Initial contribution */ @NonNullByDefault -@Component(service = UpnpDiscoveryParticipant.class, configurationPid = "discovery.siemenshvac") +@Component(configurationPid = "discovery.siemenshvac", immediate = true) public class SiemenesHvacDiscoveryParticipant implements UpnpDiscoveryParticipant { @Activate From b0957b72c51d02b70e20d8ccc51b8c45ee96d874 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 14 May 2024 13:24:56 +0200 Subject: [PATCH 172/214] fix autodiscovery Signed-off-by: Laurent ARNAL --- .../internal/constants/SiemensHvacBindingConstants.java | 2 -- .../discovery/SiemenesHvacDiscoveryParticipant.java | 2 +- .../discovery/SiemensHvacDeviceDiscoveryService.java | 8 -------- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java index bbe43e7cf4aed..a4df4f20ab628 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java @@ -34,8 +34,6 @@ public class SiemensHvacBindingConstants { public static final String IP_ADDRESS = "ipAddress"; public static final String BASE_URL = "baseUrl"; - public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge"); - public static final String PROPERTY_VENDOR_NAME = "Siemens"; public static final String CONFIG_DESCRIPTION_URI_THING_PREFIX = "thing-type"; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java index a3c4554d63010..0657d6bf04ada 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java @@ -54,7 +54,7 @@ public void modified(@Nullable Map configProperties) { @Override public Set getSupportedThingTypeUIDs() { - return Set.of(SiemensHvacBindingConstants.THING_TYPE_BRIDGE); + return Set.of(SiemensHvacBindingConstants.THING_TYPE_OZW672); } @Override diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java index 8471511d8faf5..59f7e5aa1d4e3 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java @@ -158,14 +158,6 @@ public void setThingHandler(@Nullable ThingHandler handler) { return siemensHvacBridgeHandler; } - @Override - public void activate() { - final SiemensHvacBridgeBaseThingHandler handler = siemensHvacBridgeHandler; - if (handler != null) { - handler.registerDiscoveryListener(this); - } - } - @Override public void deactivate() { } From 088cf37965bed26dac8984b823d3703df601ad46 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Tue, 14 May 2024 14:46:34 +0200 Subject: [PATCH 173/214] change json cache file location Signed-off-by: Laurent ARNAL --- .../metadata/SiemensHvacMetadataRegistryImpl.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java index 121cbfa8bbc10..e42399d59072b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -89,7 +89,7 @@ public class SiemensHvacMetadataRegistryImpl implements SiemensHvacMetadataRegis private @Nullable SiemensHvacMetadata root = null; private @Nullable ArrayList devices = null; - private static final String JSON_DIR = OpenHAB.getUserDataFolder() + File.separatorChar + "jsondb"; + private static final String JSON_DIR = OpenHAB.getUserDataFolder() + File.separatorChar + "siemenshvac"; private @Nullable SiemensHvacThingTypeProvider thingTypeProvider; private @Nullable SiemensHvacChannelTypeProvider channelTypeProvider; @@ -297,6 +297,13 @@ public void readMeta() throws SiemensHvacException { logger.trace("siemensHvac:Initialization():Begin_0001"); + File folder = new File(JSON_DIR); + + if (!folder.exists()) { + logger.debug("Creating directory {}", folder); + folder.mkdirs(); + } + logger.trace("siemensHvac:Initialization():ReadCache"); loadMetaDataFromCache(); From defe62a187c2b8a6eeb09fa4f576cd991302aec2 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Fri, 31 May 2024 10:23:52 +0200 Subject: [PATCH 174/214] first pass on May 15 review from Jacob Signed-off-by: Laurent ARNAL --- .../SiemensHvacBindingConstants.java | 8 +- .../internal/converter/TypeConverter.java | 4 +- .../converter/type/AbstractTypeConverter.java | 9 +- .../converter/type/CalendarTypeConverter.java | 5 +- .../converter/type/CheckboxTypeConverter.java | 5 +- .../converter/type/DateTimeTypeConverter.java | 5 +- .../converter/type/EnumTypeConverter.java | 5 +- .../converter/type/NumericTypeConverter.java | 5 +- .../converter/type/RadioTypeConverter.java | 4 +- .../type/SchedulerTypeConverter.java | 5 +- .../converter/type/StringTypeConverter.java | 5 +- .../type/TimeOfDayTypeConverter.java | 5 +- .../SiemensHvacBridgeBaseThingHandler.java | 25 ++- .../handler/SiemensHvacHandlerImpl.java | 14 +- .../SiemensHvacOZW672BridgeThingHandler.java | 23 -- .../metadata/SiemensHvacMetadataRegistry.java | 7 + .../SiemensHvacMetadataRegistryImpl.java | 197 +++++++++--------- .../network/SiemensHvacConnector.java | 2 +- .../network/SiemensHvacConnectorImpl.java | 2 +- .../OH-INF/i18n/siemenshvac.properties | 4 +- 20 files changed, 199 insertions(+), 140 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java index a4df4f20ab628..04c85a5a5ff82 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java @@ -51,10 +51,10 @@ public class SiemensHvacBindingConstants { public static final String CATEGORY_THING_HVAC = "HVAC"; - public static final String CATEGORY_CHANNEL_WIDGETS_NUMBER = "Number"; - public static final String CATEGORY_CHANNEL_WIDGETS_SWITCH = "Switch"; - public static final String CATEGORY_CHANNEL_WIDGETS_TEMP = "Temperature"; - public static final String CATEGORY_CHANNEL_WIDGETS_TIME = "Time"; + public static final String CATEGORY_CHANNEL_NUMBER = "Number"; + public static final String CATEGORY_CHANNEL_SWITCH = "Switch"; + public static final String CATEGORY_CHANNEL_TEMP = "Temperature"; + public static final String CATEGORY_CHANNEL_TIME = "Time"; public static final String CATEGORY_CHANNEL_CONTROL_HEATING = "Heating"; } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java index 94feda6b00fab..cc9cb9237217e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.siemenshvac.internal.converter; +import java.util.Locale; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.thing.type.ChannelType; @@ -37,7 +39,7 @@ public interface TypeConverter { /** * Converts a siemensHvac value to an openHAB type. */ - State convertFromBinding(JsonObject dp, ChannelType tp) throws ConverterException; + State convertFromBinding(JsonObject dp, ChannelType tp, Locale locale) throws ConverterException; /** * get underlying channel type to construct channel type UID diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java index 4174ae407ee72..64664f6763941 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; +import java.util.Locale; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; @@ -53,7 +55,7 @@ public abstract class AbstractTypeConverter implements TypeConverter { } @Override - public State convertFromBinding(JsonObject dp, ChannelType tp) throws ConverterException { + public State convertFromBinding(JsonObject dp, ChannelType tp, Locale locale) throws ConverterException { String type = null; JsonElement value = null; @@ -82,7 +84,7 @@ public State convertFromBinding(JsonObject dp, ChannelType tp) throws ConverterE return UnDefType.NULL; } - return fromBinding(value, type, tp); + return fromBinding(value, type, tp, locale); } /** @@ -111,7 +113,8 @@ public State convertFromBinding(JsonObject dp, ChannelType tp) throws ConverterE /** * Converts the datapoint value to an openHAB type. */ - protected abstract State fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException; + protected abstract State fromBinding(JsonElement value, String type, ChannelType tp, Locale locale) + throws ConverterException; /** * get underlying channel type to construct channel type UID diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java index 951328969dd94..6c7b9e613a511 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; +import java.util.Locale; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; @@ -52,7 +54,8 @@ protected boolean fromBindingValidation(JsonElement value, String type) { } @Override - protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { + protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp, Locale locale) + throws ConverterException { throw new ConverterException("NIY"); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java index cef45a71482eb..b5c68fcb27ec6 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; +import java.util.Locale; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; @@ -45,7 +47,8 @@ protected boolean fromBindingValidation(JsonElement value, String type) { } @Override - protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { + protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp, Locale locale) + throws ConverterException { throw new ConverterException("NIY"); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java index ec71a5694bb31..e8afedc74f4d4 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java @@ -66,7 +66,8 @@ protected boolean fromBindingValidation(JsonElement value, String type) { } @Override - protected DateTimeType fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { + protected DateTimeType fromBinding(JsonElement value, String type, ChannelType tp, Locale locale) + throws ConverterException { if ("----".equals(value.getAsString())) { return new DateTimeType(ZonedDateTime.now(this.timeZoneProvider.getTimeZone())); } else { @@ -86,7 +87,7 @@ protected DateTimeType fromBinding(JsonElement value, String type, ChannelType t } LocalDateTime parsedDate = LocalDateTime.parse(value.getAsString(), - formatterBuilder.toFormatter(Locale.FRENCH)); + formatterBuilder.toFormatter(locale)); ZonedDateTime zdt = parsedDate.atZone(this.timeZoneProvider.getTimeZone()); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java index df3a8c31a4142..3b2220986ef4c 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; +import java.util.Locale; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; @@ -51,7 +53,8 @@ protected boolean fromBindingValidation(JsonElement value, String type) { } @Override - protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { + protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp, Locale locale) + throws ConverterException { return new DecimalType(value.getAsInt()); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java index df96d4fc28200..19fecb8c49014 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; +import java.util.Locale; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; @@ -55,7 +57,8 @@ protected boolean fromBindingValidation(JsonElement value, String type) { } @Override - protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { + protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp, Locale locale) + throws ConverterException { if ("----".equals(value.getAsString())) { return new DecimalType(0); } else { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java index e02c5dc31626d..de63753857653 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/RadioTypeConverter.java @@ -13,6 +13,7 @@ package org.openhab.binding.siemenshvac.internal.converter.type; import java.util.List; +import java.util.Locale; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -76,7 +77,8 @@ protected boolean fromBindingValidation(JsonElement value, String type) { } @Override - protected State fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { + protected State fromBinding(JsonElement value, String type, ChannelType tp, Locale locale) + throws ConverterException { State updateVal = UnDefType.UNDEF; String valueSt = value.getAsString(); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SchedulerTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SchedulerTypeConverter.java index f49812e7b0845..7963fb92de424 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SchedulerTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SchedulerTypeConverter.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; +import java.util.Locale; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; @@ -52,7 +54,8 @@ protected boolean fromBindingValidation(JsonElement value, String type) { } @Override - protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { + protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp, Locale locale) + throws ConverterException { throw new ConverterException("NIY"); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/StringTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/StringTypeConverter.java index 84a762ec6aff9..ad73d1e81d73b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/StringTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/StringTypeConverter.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; +import java.util.Locale; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; @@ -57,7 +59,8 @@ protected boolean fromBindingValidation(JsonElement value, String type) { } @Override - protected StringType fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { + protected StringType fromBinding(JsonElement value, String type, ChannelType tp, Locale locale) + throws ConverterException { return new StringType(value.getAsString()); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java index aeed3f63f108c..65ea0a47d0328 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.siemenshvac.internal.converter.type; +import java.util.Locale; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; @@ -53,7 +55,8 @@ protected boolean fromBindingValidation(JsonElement value, String type) { } @Override - protected State fromBinding(JsonElement value, String type, ChannelType tp) throws ConverterException { + protected State fromBinding(JsonElement value, String type, ChannelType tp, Locale locale) + throws ConverterException { if ("----".equals(value.getAsString())) { return UnDefType.UNDEF; } else { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java index 2f8011b9034b9..8a5929d1e2940 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -12,6 +12,11 @@ */ package org.openhab.binding.siemenshvac.internal.handler; +import java.net.URL; +import java.net.URLConnection; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.concurrent.TimeUnit; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -96,6 +101,24 @@ public void initialize() { scheduler.schedule(this::initializeCode, 1, TimeUnit.SECONDS); } + protected String getBuildDate() { + try { + ClassLoader cl = getClass().getClassLoader(); + if (cl != null) { + URL res = cl.getResource(getClass().getCanonicalName().replace('.', '/') + ".class"); + if (res != null) { + URLConnection cnx = res.openConnection(); + Date dt = new Date(cnx.getLastModified()); + DateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); + return df.format(dt); + } + } + + } catch (Exception ex) { + } + return "unknown"; + } + public static String getStackTrace(final Throwable throwable) { StringBuffer sb = new StringBuffer(); @@ -124,7 +147,7 @@ private void initializeCode() { updateStatus(ThingStatus.ONLINE); } catch (SiemensHvacException ex) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - String.format("@text/offline.error-gateway-init", ex.getMessage())); + "@text/offline.error-gateway-init[\"" + ex.getMessage() + "\"]"); } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index d659fc29e7ec0..39a03426e68a9 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -13,6 +13,7 @@ package org.openhab.binding.siemenshvac.internal.handler; import java.math.BigDecimal; +import java.util.Locale; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; @@ -221,6 +222,12 @@ private void readChannel(Channel channel) { public void decodeReadDp(@Nullable JsonObject response, @Nullable String uid, @Nullable String dp, ChannelType tp, @Nullable String type) { + + SiemensHvacMetadataRegistry lcMetaDataRegistry = metaDataRegistry; + if (lcMetaDataRegistry == null) { + return; + } + if (response != null && response.has("Data")) { JsonObject subResult = (JsonObject) response.get("Data"); @@ -236,7 +243,12 @@ public void decodeReadDp(@Nullable JsonObject response, @Nullable String uid, @N if (typer != null) { TypeConverter converter = ConverterFactory.getConverter(typer); - State state = converter.convertFromBinding(subResult, tp); + Locale local = lcMetaDataRegistry.getUserLocale(); + if (local == null) { + local = Locale.getDefault(); + } + + State state = converter.convertFromBinding(subResult, tp, local); updateState(updateKey, state); } } catch (ConverterTypeException ex) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java index e22e7d7897cfe..8bc682a4549db 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZW672BridgeThingHandler.java @@ -12,12 +12,7 @@ */ package org.openhab.binding.siemenshvac.internal.handler; -import java.net.URL; -import java.net.URLConnection; -import java.text.DateFormat; -import java.text.SimpleDateFormat; import java.util.Collection; -import java.util.Date; import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -57,24 +52,6 @@ public void initialize() { super.initialize(); } - private String getBuildDate() { - try { - ClassLoader cl = getClass().getClassLoader(); - if (cl != null) { - URL res = cl.getResource(getClass().getCanonicalName().replace('.', '/') + ".class"); - if (res != null) { - URLConnection cnx = res.openConnection(); - Date dt = new Date(cnx.getLastModified()); - DateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); - return df.format(dt); - } - } - - } catch (Exception ex) { - } - return "unknown"; - } - @Override public void dispose() { super.dispose(); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistry.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistry.java index 51ac9f87ea783..0d3a2c3f20dd9 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistry.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistry.java @@ -13,6 +13,7 @@ package org.openhab.binding.siemenshvac.internal.metadata; import java.util.ArrayList; +import java.util.Locale; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -50,4 +51,10 @@ public interface SiemensHvacMetadataRegistry { SiemensHvacConnector getSiemensHvacConnector(); void invalidate(); + + @Nullable + SiemensHvacMetadataUser getUser(); + + @Nullable + Locale getUserLocale(); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java index e42399d59072b..369a5ca563238 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.Hashtable; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -96,6 +97,8 @@ public class SiemensHvacMetadataRegistryImpl implements SiemensHvacMetadataRegis private @Nullable SiemensHvacChannelGroupTypeProvider channelGroupTypeProvider; private @Nullable SiemensHvacConfigDescriptionProvider configDescriptionProvider; private @Nullable SiemensHvacConnector hvacConnector; + private @Nullable SiemensHvacMetadataUser user; + private @Nullable Locale userLocale; private final HashMap userList; @@ -284,17 +287,20 @@ public void readMeta() throws SiemensHvacException { throw new SiemensHvacException("@offline.config-not-init"); } - SiemensHvacMetadataUser user = null; + SiemensHvacMetadataUser lcUser = null; String userName = config.userName; if (userList.containsKey(userName)) { - user = userList.get(userName); + lcUser = userList.get(userName); } - if (user == null) { + if (lcUser == null) { throw new SiemensHvacException("@offline.user-not-find"); } + this.user = lcUser; + this.userLocale = Locale.forLanguageTag(lcUser.getLanguage()); + logger.trace("siemensHvac:Initialization():Begin_0001"); File folder = new File(JSON_DIR); @@ -323,12 +329,12 @@ public void readMeta() throws SiemensHvacException { logger.trace("siemensHvac:Initialization():BeginReadMenu"); root = new SiemensHvacMetadataMenu(); - changeLanguage(user, 1); + changeLanguage(lcUser, 1); readMetaData(root, -1, false); lcHvacConnector.waitNoNewRequest(); lcHvacConnector.waitAllPendingRequest(); - changeLanguage(user, user.getLanguageId()); + changeLanguage(lcUser, lcUser.getLanguageId()); readMetaData(root, -1, true); lcHvacConnector.waitNoNewRequest(); lcHvacConnector.waitAllPendingRequest(); @@ -372,6 +378,16 @@ public void readMeta() throws SiemensHvacException { logger.trace("siemensHvac:InitDptMap():end"); } + @Override + public @Nullable SiemensHvacMetadataUser getUser() { + return user; + } + + @Override + public @Nullable Locale getUserLocale() { + return userLocale; + } + private void generateThingsType(SiemensHvacMetadataDevice device) { SiemensHvacThingTypeProvider lcThingTypeProvider = thingTypeProvider; logger.debug("Generate thing types for device: {} / {}", device.getName(), device.getSerialNr()); @@ -642,17 +658,17 @@ public static String getCategory(SiemensHvacMetadataDataPoint dp) { if (dptUnit == null) { return ""; } else if (dptUnit.contains("°C")) { - return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_TEMP; + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_TEMP; } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_DATE_TIME)) { - return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_TIME; + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_TIME; } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_TIMEOFDAY)) { - return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_TIME; + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_TIME; } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_ENUM)) { - return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_SWITCH; + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_SWITCH; } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_RADIO)) { - return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_SWITCH; + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_SWITCH; } else if (dpType.contains(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC)) { - return SiemensHvacBindingConstants.CATEGORY_CHANNEL_WIDGETS_NUMBER; + return SiemensHvacBindingConstants.CATEGORY_CHANNEL_NUMBER; } else { return SiemensHvacBindingConstants.CATEGORY_CHANNEL_CONTROL_HEATING; } @@ -678,109 +694,104 @@ public static String getStatePattern(SiemensHvacMetadataDataPoint dpt) { } public void readUserInfo() throws SiemensHvacException { - try { - SiemensHvacConnector lcHvacConnector = hvacConnector; - String request = "main.app?section=settings&subsection=user"; - - if (lcHvacConnector != null) { - String response = lcHvacConnector.doBasicRequest(request); - - if (response != null) { - String st = response; - st = st.replace("\n", ""); - - Pattern pattern1 = Pattern.compile("table class=\\\"user_table\\\".*?>(.*?)<\\/table>"); - Matcher matcher1 = pattern1.matcher(st); - - if (matcher1.find()) { - String userTable = matcher1.group(1); - - Pattern pattern2 = Pattern.compile("(.*?)<\\/tr>"); - Matcher matcher2 = pattern2.matcher(userTable); - - int idx = 0; - while (matcher2.find()) { - String line = matcher2.group(1); - - if (idx > 0) { - Pattern pattern3 = Pattern.compile("(.*?)<\\/td>"); - Matcher matcher3 = pattern3.matcher(line); + SiemensHvacConnector lcHvacConnector = hvacConnector; + String request = "main.app?section=settings&subsection=user"; - int idxCell = 0; - String userName = ""; - String userEdit = ""; - String userId = ""; - while (matcher3.find()) { - String cell = matcher3.group(2); - String header = matcher3.group(1); + if (lcHvacConnector != null) { + String response = lcHvacConnector.doBasicRequest(request); - if (idxCell == 0) { - userName = cell; - } else if (idxCell == 5) { - userEdit = header; - } - idxCell++; + if (response != null) { + String st = response; + st = st.replace("\n", ""); + + Pattern pattern1 = Pattern.compile("table class=\\\"user_table\\\".*?>(.*?)<\\/table>"); + Matcher matcher1 = pattern1.matcher(st); + + if (matcher1.find()) { + String userTable = matcher1.group(1); + + Pattern pattern2 = Pattern.compile("(.*?)<\\/tr>"); + Matcher matcher2 = pattern2.matcher(userTable); + + int idx = 0; + while (matcher2.find()) { + String line = matcher2.group(1); + + if (idx > 0) { + Pattern pattern3 = Pattern.compile("(.*?)<\\/td>"); + Matcher matcher3 = pattern3.matcher(line); + + int idxCell = 0; + String userName = ""; + String userEdit = ""; + String userId = ""; + while (matcher3.find()) { + String cell = matcher3.group(2); + String header = matcher3.group(1); + + if (idxCell == 0) { + userName = cell; + } else if (idxCell == 5) { + userEdit = header; } + idxCell++; + } - if ("".equals(userName)) { - continue; - } + if ("".equals(userName)) { + continue; + } - Pattern pattern4 = Pattern.compile("userid=(.+?)"); - Matcher matcher4 = pattern4.matcher(userEdit); + Pattern pattern4 = Pattern.compile("userid=(.+?)"); + Matcher matcher4 = pattern4.matcher(userEdit); - SiemensHvacMetadataUser user = new SiemensHvacMetadataUser(); - user.setName(userName); + SiemensHvacMetadataUser user = new SiemensHvacMetadataUser(); + user.setName(userName); - if (matcher4.find()) { - userId = matcher4.group(1); - user.setId(Integer.parseInt(userId)); - } else { - userId = null; - user.setId(-1); - } + if (matcher4.find()) { + userId = matcher4.group(1); + user.setId(Integer.parseInt(userId)); + } else { + userId = null; + user.setId(-1); + } - request = "main.app?section=settings&subsection=user&action=modify"; - if (userId != null) { - request = request + "&userid=" + userId; - } - response = lcHvacConnector.doBasicRequest(request); + request = "main.app?section=settings&subsection=user&action=modify"; + if (userId != null) { + request = request + "&userid=" + userId; + } + response = lcHvacConnector.doBasicRequest(request); - Pattern pattern5 = Pattern.compile( - "", Pattern.MULTILINE); - Matcher matcher5 = pattern5.matcher(response); + Pattern pattern5 = Pattern.compile("", + Pattern.MULTILINE); + Matcher matcher5 = pattern5.matcher(response); - if (matcher5.find()) { - String optionsList = matcher5.group(1); + if (matcher5.find()) { + String optionsList = matcher5.group(1); - Pattern pattern6 = java.util.regex.Pattern.compile( - "", Pattern.MULTILINE); - Matcher matcher6 = pattern6.matcher(optionsList); + Pattern pattern6 = java.util.regex.Pattern + .compile("", Pattern.MULTILINE); + Matcher matcher6 = pattern6.matcher(optionsList); - while (matcher6.find()) { - String id = matcher6.group(1); - String opt = matcher6.group(2); - String lang = matcher6.group(3); + while (matcher6.find()) { + String id = matcher6.group(1); + String opt = matcher6.group(2); + String lang = matcher6.group(3); - if (opt.indexOf("selected") >= 0) { - user.setLanguage(lang); - user.setLanguageId(Integer.parseInt(id)); - } + if (opt.indexOf("selected") >= 0) { + user.setLanguage(lang); + user.setLanguageId(Integer.parseInt(id)); } } - - userList.put(userName, user); } - idx++; - + userList.put(userName, user); } + + idx++; + } } } - } catch (Exception e) { - throw new SiemensHvacException("Error during reading user info", e); - // Reset sessionId so we redone _auth on error } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java index 489fc276764b1..97707ecbcd103 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java @@ -29,7 +29,7 @@ public interface SiemensHvacConnector { @Nullable - String doBasicRequest(String uri) throws Exception; + String doBasicRequest(String uri) throws SiemensHvacException; @Nullable JsonObject doRequest(String req); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index e09308ad75081..1712e6aaa2fd4 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -429,7 +429,7 @@ private void doAuth(boolean http) throws SiemensHvacException { try { c.add(new URI(baseUri), cookie); } catch (URISyntaxException ex) { - throw new SiemensHvacException(String.format("URI is not correctly formated: %s", baseUri), ex); + throw new SiemensHvacException(String.format("URI is not correctly formatted: %s", baseUri), ex); } logger.debug("Execute request: {}", uri); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties index 361df43f75380..826d44d79b5df 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties @@ -21,8 +21,8 @@ thing-type.config.siemenshvac.ozw672.userPassword.description = User password of offline.waiting-bridge-initialization = Waiting bridge initialization, reading metadata in background -offline.baseurl-mandatory = baseUrl is mandatory on configuration ! -offline.error-gateway-init = Error occurs during gateway initialization: %s +offline.baseurl-mandatory = baseUrl is mandatory on configuration. +offline.error-gateway-init = Error occurred during gateway initialization: {0} offline.config-not-init = Config not initialize during reading metadata, aborting. offline.user-not-find = Cannot find user during reading metadata, aborting. From 06ce157b251d83ed2145555cb00354a98ac0fc41 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Fri, 31 May 2024 10:34:51 +0200 Subject: [PATCH 175/214] fix some imprecision on gateway model handle by the addon Signed-off-by: Laurent ARNAL --- bundles/org.openhab.binding.siemenshvac/README.md | 4 ++-- .../internal/discovery/SiemenesHvacDiscoveryParticipant.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/README.md b/bundles/org.openhab.binding.siemenshvac/README.md index 425c5970869df..7e7900f975c13 100644 --- a/bundles/org.openhab.binding.siemenshvac/README.md +++ b/bundles/org.openhab.binding.siemenshvac/README.md @@ -29,8 +29,8 @@ Support many different things as the thing type is handle by autodiscovery. Mainly, it will first discover the gateway. Currently test was done with the OZW672.x series. -No test was conduct using the OZW772.x series, but it should work as well. -You can request support to the community forum is you encounter troubles with an untested device. +No test was conduct using the OZW772.x series, the code will currently not handing initialization of OZW772 gateway. +You can request support to the community forum is you have the gateway model and want it to be supported. After, it will discover thing inside your PAC, mainly main controller of type RVS... Only test in real condition with RVS41.813/327 but should work with all other type as the access interface is standard. diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java index 0657d6bf04ada..a7fc70db22cfd 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java @@ -93,7 +93,7 @@ public Set getSupportedThingTypeUIDs() { if (modelDetails != null && serialNumber != null && !serialNumber.isBlank()) { String modelName = modelDetails.getModelName(); if (modelName != null) { - if (modelName.startsWith("Web Server OZW672.01")) { + if (modelName.startsWith("Web Server OZW672")) { return new ThingUID(SiemensHvacBindingConstants.THING_TYPE_OZW672, serialNumber.toLowerCase()); } } From 7424deecdd511834c0d1bcfe83907362c272d3c4 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 2 Jun 2024 11:47:46 +0200 Subject: [PATCH 176/214] Add support for UoM fix locale initialization handling review dimension initialization for NumberType Fix TimeOfDay converter Signed-off-by: Laurent ARNAL --- .../internal/converter/ConverterFactory.java | 2 +- .../internal/converter/TypeConverter.java | 5 +- .../converter/type/AbstractTypeConverter.java | 18 ++- .../converter/type/CalendarTypeConverter.java | 9 +- .../converter/type/CheckboxTypeConverter.java | 9 +- .../converter/type/DateTimeTypeConverter.java | 9 +- .../converter/type/EnumTypeConverter.java | 9 +- .../converter/type/NumericTypeConverter.java | 115 +++++++++++++++++- .../converter/type/RadioTypeConverter.java | 17 +-- .../type/SchedulerTypeConverter.java | 9 +- .../converter/type/StringTypeConverter.java | 9 +- .../type/TimeOfDayTypeConverter.java | 60 +++++++-- .../SiemensHvacMetadataRegistryImpl.java | 45 +++++-- .../siemenshvac/internal/type/UidUtils.java | 2 +- .../main/resources/OH-INF/thing/ozw672.xml | 1 + 15 files changed, 252 insertions(+), 67 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java index 6500f39bf8d75..757c8eed4cd89 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/ConverterFactory.java @@ -44,7 +44,7 @@ public static void registerConverter(TimeZoneProvider timeZoneProvider) { registerConverter(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC, new NumericTypeConverter()); registerConverter(SiemensHvacBindingConstants.DPT_TYPE_RADIO, new RadioTypeConverter()); registerConverter(SiemensHvacBindingConstants.DPT_TYPE_STRING, new StringTypeConverter()); - registerConverter(SiemensHvacBindingConstants.DPT_TYPE_TIMEOFDAY, new TimeOfDayTypeConverter()); + registerConverter(SiemensHvacBindingConstants.DPT_TYPE_TIMEOFDAY, new TimeOfDayTypeConverter(timeZoneProvider)); registerConverter(SiemensHvacBindingConstants.DPT_TYPE_CHECKBOX, new CheckboxTypeConverter()); registerConverter(SiemensHvacBindingConstants.DPT_TYPE_SCHEDULER, new SchedulerTypeConverter()); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java index cc9cb9237217e..fbaf5efe0c43b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/TypeConverter.java @@ -16,6 +16,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint; import org.openhab.core.thing.type.ChannelType; import org.openhab.core.types.State; import org.openhab.core.types.Type; @@ -45,13 +46,13 @@ public interface TypeConverter { * get underlying channel type to construct channel type UID * */ - String getChannelType(boolean writeAccess); + String getChannelType(SiemensHvacMetadataDataPoint dpt); /** * get underlying item type on openhab side for this SiemensHvac type * */ - String getItemType(boolean writeAccess); + String getItemType(SiemensHvacMetadataDataPoint dpt); /** * tell if this type have different subvariant or not diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java index 64664f6763941..69b4485e14d08 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/AbstractTypeConverter.java @@ -19,6 +19,7 @@ import org.openhab.binding.siemenshvac.internal.converter.ConverterException; import org.openhab.binding.siemenshvac.internal.converter.ConverterTypeException; import org.openhab.binding.siemenshvac.internal.converter.TypeConverter; +import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint; import org.openhab.core.thing.type.ChannelType; import org.openhab.core.types.Command; import org.openhab.core.types.State; @@ -57,6 +58,7 @@ public abstract class AbstractTypeConverter implements TypeConverter { @Override public State convertFromBinding(JsonObject dp, ChannelType tp, Locale locale) throws ConverterException { String type = null; + String unit = ""; JsonElement value = null; if (dp.has("Type")) { @@ -69,6 +71,10 @@ public State convertFromBinding(JsonObject dp, ChannelType tp, Locale locale) th value = dp.get("EnumValue"); } + if (dp.has("Unit")) { + unit = dp.get("Unit").getAsString().trim(); + } + if (value == null) { return UnDefType.NULL; } @@ -78,13 +84,13 @@ public State convertFromBinding(JsonObject dp, ChannelType tp, Locale locale) th return UnDefType.NULL; } - if (!fromBindingValidation(value, type)) { + if (!fromBindingValidation(value, unit, type)) { logger.debug("Can't convert {} value '{}' with {} for '{}'", type, value, this.getClass().getSimpleName(), dp); return UnDefType.NULL; } - return fromBinding(value, type, tp, locale); + return fromBinding(value, unit, type, tp, locale); } /** @@ -108,12 +114,12 @@ public State convertFromBinding(JsonObject dp, ChannelType tp, Locale locale) th /** * Returns true, if the conversion from the binding to openHAB is possible. */ - protected abstract boolean fromBindingValidation(JsonElement value, String type); + protected abstract boolean fromBindingValidation(JsonElement value, String unit, String type); /** * Converts the datapoint value to an openHAB type. */ - protected abstract State fromBinding(JsonElement value, String type, ChannelType tp, Locale locale) + protected abstract State fromBinding(JsonElement value, String unit, String type, ChannelType tp, Locale locale) throws ConverterException; /** @@ -121,14 +127,14 @@ protected abstract State fromBinding(JsonElement value, String type, ChannelType * */ @Override - public abstract String getChannelType(boolean writeAccess); + public abstract String getChannelType(SiemensHvacMetadataDataPoint dpt); /** * get underlying item type on openhab side for this SiemensHvac type * */ @Override - public abstract String getItemType(boolean writeAccess); + public abstract String getItemType(SiemensHvacMetadataDataPoint dpt); /** * tell if this type have different subvariant or not diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java index 6c7b9e613a511..b6576f74680a4 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CalendarTypeConverter.java @@ -17,6 +17,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; @@ -49,23 +50,23 @@ protected boolean toBindingValidation(Type type) { } @Override - protected boolean fromBindingValidation(JsonElement value, String type) { + protected boolean fromBindingValidation(JsonElement value, String unit, String type) { return true; } @Override - protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp, Locale locale) + protected DecimalType fromBinding(JsonElement value, String unit, String type, ChannelType tp, Locale locale) throws ConverterException { throw new ConverterException("NIY"); } @Override - public String getChannelType(boolean writeAccess) { + public String getChannelType(SiemensHvacMetadataDataPoint dpt) { return "datetime"; } @Override - public String getItemType(boolean writeAccess) { + public String getItemType(SiemensHvacMetadataDataPoint dpt) { return CoreItemFactory.DATETIME; } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java index b5c68fcb27ec6..f6537b407b57b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/CheckboxTypeConverter.java @@ -17,6 +17,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.DecimalType; import org.openhab.core.thing.type.ChannelType; @@ -42,23 +43,23 @@ protected boolean toBindingValidation(Type type) { } @Override - protected boolean fromBindingValidation(JsonElement value, String type) { + protected boolean fromBindingValidation(JsonElement value, String unit, String type) { return true; } @Override - protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp, Locale locale) + protected DecimalType fromBinding(JsonElement value, String unit, String type, ChannelType tp, Locale locale) throws ConverterException { throw new ConverterException("NIY"); } @Override - public String getChannelType(boolean writeAccess) { + public String getChannelType(SiemensHvacMetadataDataPoint dpt) { return "contact"; } @Override - public String getItemType(boolean writeAccess) { + public String getItemType(SiemensHvacMetadataDataPoint dpt) { return CoreItemFactory.CONTACT; } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java index e8afedc74f4d4..f426ad5abf84e 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/DateTimeTypeConverter.java @@ -22,6 +22,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint; import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.DateTimeType; @@ -61,12 +62,12 @@ protected boolean toBindingValidation(Type type) { } @Override - protected boolean fromBindingValidation(JsonElement value, String type) { + protected boolean fromBindingValidation(JsonElement value, String unit, String type) { return true; } @Override - protected DateTimeType fromBinding(JsonElement value, String type, ChannelType tp, Locale locale) + protected DateTimeType fromBinding(JsonElement value, String unit, String type, ChannelType tp, Locale locale) throws ConverterException { if ("----".equals(value.getAsString())) { return new DateTimeType(ZonedDateTime.now(this.timeZoneProvider.getTimeZone())); @@ -104,12 +105,12 @@ protected DateTimeType fromBinding(JsonElement value, String type, ChannelType t } @Override - public String getChannelType(boolean writeAccess) { + public String getChannelType(SiemensHvacMetadataDataPoint dpt) { return "datetime"; } @Override - public String getItemType(boolean writeAccess) { + public String getItemType(SiemensHvacMetadataDataPoint dpt) { return CoreItemFactory.DATETIME; } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java index 3b2220986ef4c..b23bfb1cad8c5 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/EnumTypeConverter.java @@ -17,6 +17,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.DecimalType; import org.openhab.core.thing.type.ChannelType; @@ -48,23 +49,23 @@ protected boolean toBindingValidation(Type type) { } @Override - protected boolean fromBindingValidation(JsonElement value, String type) { + protected boolean fromBindingValidation(JsonElement value, String unit, String type) { return true; } @Override - protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp, Locale locale) + protected DecimalType fromBinding(JsonElement value, String unit, String type, ChannelType tp, Locale locale) throws ConverterException { return new DecimalType(value.getAsInt()); } @Override - public String getChannelType(boolean writeAccess) { + public String getChannelType(SiemensHvacMetadataDataPoint dpt) { return "number"; } @Override - public String getItemType(boolean writeAccess) { + public String getItemType(SiemensHvacMetadataDataPoint dpt) { return CoreItemFactory.NUMBER; } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java index 19fecb8c49014..28f551e783251 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java @@ -14,13 +14,24 @@ import java.util.Locale; +import javax.measure.Unit; +import javax.measure.quantity.Dimensionless; +import javax.measure.quantity.ElectricPotential; +import javax.measure.quantity.Temperature; +import javax.measure.quantity.Time; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.ImperialUnits; +import org.openhab.core.library.unit.SIUnits; +import org.openhab.core.library.unit.Units; import org.openhab.core.thing.type.ChannelType; +import org.openhab.core.types.State; import org.openhab.core.types.Type; import com.google.gson.JsonElement; @@ -52,28 +63,120 @@ protected boolean toBindingValidation(Type type) { } @Override - protected boolean fromBindingValidation(JsonElement value, String type) { + protected boolean fromBindingValidation(JsonElement value, String unit, String type) { return true; } @Override - protected DecimalType fromBinding(JsonElement value, String type, ChannelType tp, Locale locale) + protected State fromBinding(JsonElement value, String unit, String type, ChannelType tp, Locale locale) throws ConverterException { if ("----".equals(value.getAsString())) { return new DecimalType(0); } else { - return new DecimalType(value.getAsDouble()); + double dValue = value.getAsDouble(); + + String itemType = tp.getItemType(); + + if (itemType != null) { + + if (itemType.equals("Number:Temperature")) { + Unit targetUnit = null; + + if (unit.equals("°C")) { + targetUnit = SIUnits.CELSIUS; + } else if (unit.equals("°C*min")) { + targetUnit = SIUnits.CELSIUS; + } else if (unit.equals("°F")) { + targetUnit = ImperialUnits.FAHRENHEIT; + } else if (unit.equals("°F*min")) { + targetUnit = ImperialUnits.FAHRENHEIT; + } + + if (targetUnit != null) { + return new QuantityType<>(dValue, targetUnit); + } + } else if (itemType.equals("Number:ElectricPotential")) { + Unit targetUnit = null; + + if (unit.equals("V")) { + targetUnit = Units.VOLT; + } + + if (targetUnit != null) { + return new QuantityType<>(dValue, targetUnit); + } + } else if (itemType.equals("Number:Time")) { + Unit + From 76d5caaeb657e39b9ab9343c76c6e519034c4614 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Sun, 2 Jun 2024 12:01:09 +0200 Subject: [PATCH 177/214] review style checker warnings Signed-off-by: Laurent ARNAL --- .../converter/type/NumericTypeConverter.java | 33 +++++++++---------- .../handler/SiemensHvacHandlerImpl.java | 1 - .../SiemensHvacMetadataRegistryImpl.java | 2 +- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java index 28f551e783251..192dfa882a04f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java @@ -78,60 +78,59 @@ protected State fromBinding(JsonElement value, String unit, String type, Channel String itemType = tp.getItemType(); if (itemType != null) { - - if (itemType.equals("Number:Temperature")) { + if ("Number:Temperature".equals(itemType)) { Unit targetUnit = null; - if (unit.equals("°C")) { + if ("°C".equals(unit)) { targetUnit = SIUnits.CELSIUS; - } else if (unit.equals("°C*min")) { + } else if ("°C*min".equals(unit)) { targetUnit = SIUnits.CELSIUS; - } else if (unit.equals("°F")) { + } else if ("°F".equals(unit)) { targetUnit = ImperialUnits.FAHRENHEIT; - } else if (unit.equals("°F*min")) { + } else if ("°F*min"unit.equals(unit)) { targetUnit = ImperialUnits.FAHRENHEIT; } if (targetUnit != null) { return new QuantityType<>(dValue, targetUnit); } - } else if (itemType.equals("Number:ElectricPotential")) { + } else if ("Number:ElectricPotential".equals(itemType)) { Unit targetUnit = null; - if (unit.equals("V")) { + if ("V".equals(unit)) { targetUnit = Units.VOLT; } if (targetUnit != null) { return new QuantityType<>(dValue, targetUnit); } - } else if (itemType.equals("Number:Time")) { + } else if ("Number:Time".equals(itemType)) { Unit diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties index 85990a6733458..3f972eaace4f5 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties @@ -5,17 +5,17 @@ addon.siemenshvac.description = This is the binding for SiemensHvac. # thing types -thing-type.siemenshvac.ozw672.label = OZW672 IP Gateway -thing-type.siemenshvac.ozw672.description = This is a OZW672 IP interface +thing-type.siemenshvac.ozw.label = OZW IP Gateway +thing-type.siemenshvac.ozw.description = This is a OZW IP interface # thing types config -thing-type.config.siemenshvac.ozw672.baseUrl.label = Base URL -thing-type.config.siemenshvac.ozw672.baseUrl.description = The URL of the Siemens Hvac IP gateway. Must be in format http://hostname/ or https://hostname/. Don't forget the trailing '/' -thing-type.config.siemenshvac.ozw672.userName.label = User Name -thing-type.config.siemenshvac.ozw672.userName.description = User name of the Siemens Hvac gateway -thing-type.config.siemenshvac.ozw672.userPassword.label = User Password -thing-type.config.siemenshvac.ozw672.userPassword.description = User password of the Siemens Hvac gateway +thing-type.config.siemenshvac.ozw.baseUrl.label = Base URL +thing-type.config.siemenshvac.ozw.baseUrl.description = The URL of the Siemens Hvac IP gateway. Must be in format http://hostname/ or https://hostname/. Don't forget the trailing '/' +thing-type.config.siemenshvac.ozw.userName.label = User Name +thing-type.config.siemenshvac.ozw.userName.description = User name of the Siemens Hvac gateway +thing-type.config.siemenshvac.ozw.userPassword.label = User Password +thing-type.config.siemenshvac.ozw.userPassword.description = User password of the Siemens Hvac gateway # offline message diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw.xml similarity index 74% rename from bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml rename to bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw.xml index e29dc80e0a6e9..252d3e514d303 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw672.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw.xml @@ -32,6 +32,34 @@
+ + + + This is a OZW772 IP interface + + + + + url + The URL of the Siemens Hvac IP gateway. Must be in format http://hostname/ or https://hostname/. Don't + forget the trailing '/' + true + + + User name of the Siemens Hvac gateway + false + + Administrator + + + password + User password of the Siemens Hvac gateway + false + + password + + + Number:Temperature From 6bb116561eb40f6041cf65b843e497f2cebd2c9c Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Mon, 3 Jun 2024 09:45:14 +0200 Subject: [PATCH 181/214] =?UTF-8?q?spotless:=C2=B2apply?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Laurent ARNAL --- .../src/main/resources/OH-INF/addon/addon.xml | 8 +-- .../src/main/resources/OH-INF/thing/ozw.xml | 52 +++++++++---------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml index 5d0dc263ed32b..2db2c99ce8e4d 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml @@ -17,10 +17,10 @@ modelName Web Server OZW672 - - modelName - Web Server OZW772 - + + modelName + Web Server OZW772 + diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw.xml index 252d3e514d303..0dbe12208f284 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw.xml @@ -32,34 +32,34 @@ - + - - This is a OZW772 IP interface + + This is a OZW772 IP interface - - - - url - The URL of the Siemens Hvac IP gateway. Must be in format http://hostname/ or https://hostname/. Don't - forget the trailing '/' - true - - - User name of the Siemens Hvac gateway - false - - Administrator - - - password - User password of the Siemens Hvac gateway - false - - password - - - + + + + url + The URL of the Siemens Hvac IP gateway. Must be in format http://hostname/ or https://hostname/. Don't + forget the trailing '/' + true + + + User name of the Siemens Hvac gateway + false + + Administrator + + + password + User password of the Siemens Hvac gateway + false + + password + + + Number:Temperature From 5dc4d08e1ae639a917ea0a946b29a2851e89371d Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 13 Jun 2024 10:13:56 +0200 Subject: [PATCH 182/214] progress on last jlaur review Signed-off-by: Laurent ARNAL --- .../org.openhab.binding.siemenshvac/NOTICE | 7 +++++ .../org.openhab.binding.siemenshvac/README.md | 2 +- .../type/TimeOfDayTypeConverter.java | 3 ++- .../SiemensHvacBridgeBaseThingHandler.java | 11 ++++---- .../network/SiemensHvacConnectorImpl.java | 2 +- .../RuntimeTypeAdapterFactory.java | 24 +++-------------- .../siemenshvac/internal/type/UidUtils.java | 2 +- .../OH-INF/i18n/siemenshvac.properties | 27 ++++++++++++++++--- 8 files changed, 46 insertions(+), 32 deletions(-) rename bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/{metadata => thirdparty}/RuntimeTypeAdapterFactory.java (95%) diff --git a/bundles/org.openhab.binding.siemenshvac/NOTICE b/bundles/org.openhab.binding.siemenshvac/NOTICE index 38d625e349232..451e1f04ab2bf 100644 --- a/bundles/org.openhab.binding.siemenshvac/NOTICE +++ b/bundles/org.openhab.binding.siemenshvac/NOTICE @@ -11,3 +11,10 @@ https://www.eclipse.org/legal/epl-2.0/. == Source Code https://github.com/openhab/openhab-addons + +== Third-party Content + +RuntimeTypeAdapterFactory +* License: Apache License, Version 2.0 +* Project: https://github.com/google/gson +* Source: https://github.com/google/gson/blob/main/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java diff --git a/bundles/org.openhab.binding.siemenshvac/README.md b/bundles/org.openhab.binding.siemenshvac/README.md index fa6bde89e2eb7..4f2dd93fdf9ca 100644 --- a/bundles/org.openhab.binding.siemenshvac/README.md +++ b/bundles/org.openhab.binding.siemenshvac/README.md @@ -29,7 +29,7 @@ Support many different things as the thing type is handle by autodiscovery. Mainly, it will first discover the gateway. Currently test was done with the OZW672.x series. -No test was conducted using the OZW772.x series, the code will currently not handle initialization of OZW772 gateway. +No test was conducted using the OZW772.x series, the code will currently not handle initialization of an OZW772 gateway. You can request support in the community forum, if you have the gateway model and want it to be supported. After, it will discover thing inside your PAC, mainly main controller of type RVS... diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java index dc6996ebf2cf2..67e5ccaf24eaa 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/TimeOfDayTypeConverter.java @@ -81,7 +81,8 @@ protected State fromBinding(JsonElement value, String unit, String type, Channel .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0) .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0) .parseDefaulting(ChronoField.DAY_OF_MONTH, 1).parseDefaulting(ChronoField.MONTH_OF_YEAR, 1) - .parseDefaulting(ChronoField.YEAR, 2024); + .parseDefaulting(ChronoField.YEAR, + ZonedDateTime.now(this.timeZoneProvider.getTimeZone()).getYear()); LocalDateTime parsedDate = LocalDateTime.parse(value.getAsString(), formatterBuilder.toFormatter(locale)); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java index 8a5929d1e2940..73b5be69f1578 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java @@ -16,7 +16,7 @@ import java.net.URLConnection; import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.Date; +import java.time.LocalDate; import java.util.concurrent.TimeUnit; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -75,15 +75,14 @@ public void dispose() { @Override public void initialize() { - updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "@text/offline.waiting-bridge-initialization"); - SiemensHvacBridgeConfig lcConfig = getConfigAs(SiemensHvacBridgeConfig.class); String baseUrl = null; baseUrl = lcConfig.baseUrl; if (baseUrl.isEmpty()) { - logger.debug("@text/offline.error-gateway-init"); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, + "@text/offline.error-gateway-init"); return; } @@ -97,6 +96,8 @@ public void initialize() { config = lcConfig; + updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "@text/offline.waiting-bridge-initialization"); + // Will read metadata in background to not block initialize for a long period ! scheduler.schedule(this::initializeCode, 1, TimeUnit.SECONDS); } @@ -108,7 +109,7 @@ protected String getBuildDate() { URL res = cl.getResource(getClass().getCanonicalName().replace('.', '/') + ".class"); if (res != null) { URLConnection cnx = res.openConnection(); - Date dt = new Date(cnx.getLastModified()); + LocalDate dt = LocalDate.ofEpochDay(cnx.getLastModified()); DateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); return df.format(dt); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index f1e6a0f444f82..867be2d11e6a6 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -35,10 +35,10 @@ import org.eclipse.jetty.util.ssl.SslContextFactory; import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeBaseThingHandler; import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeConfig; -import org.openhab.binding.siemenshvac.internal.metadata.RuntimeTypeAdapterFactory; import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadata; import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint; import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataMenu; +import org.openhab.binding.siemenshvac.internal.thirdparty.RuntimeTypeAdapterFactory; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacException; import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.types.Type; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/RuntimeTypeAdapterFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/thirdparty/RuntimeTypeAdapterFactory.java similarity index 95% rename from bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/RuntimeTypeAdapterFactory.java rename to bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/thirdparty/RuntimeTypeAdapterFactory.java index f9deedf8cbbda..96d3e1fb75860 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/RuntimeTypeAdapterFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/thirdparty/RuntimeTypeAdapterFactory.java @@ -1,15 +1,3 @@ -/** - * Copyright (c) 2010-2024 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 - */ /* * Copyright (C) 2011 Google Inc. * @@ -33,7 +21,7 @@ * com.google.gson.internal.{Streams,TypeAdapters,LazilyParsedNumber} * to avoid using the internal package. */ -package org.openhab.binding.siemenshvac.internal.metadata; +package org.openhab.binding.siemenshvac.internal.thirdparty; import java.io.EOFException; import java.io.IOException; @@ -155,9 +143,9 @@ * *
  *    {@code
- *   shapeAdapter.registerSubtype(Rectangle.class, "Rectangle");
- *   shapeAdapter.registerSubtype(Circle.class, "Circle");
- *   shapeAdapter.registerSubtype(Diamond.class, "Diamond");
+ * shapeAdapter.registerSubtype(Rectangle.class, "Rectangle");
+ * shapeAdapter.registerSubtype(Circle.class, "Circle");
+ * shapeAdapter.registerSubtype(Diamond.class, "Diamond");
  * }
  * 
* @@ -180,10 +168,6 @@ * } *
*/ -/** - * - * @author Laurent Arnal - Initial contribution - */ @NonNullByDefault public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory { private final Class baseType; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java index cf14ac8d45e98..c7c994c42e2cc 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/type/UidUtils.java @@ -48,7 +48,7 @@ public static String sanetizeId(String label) { result = result.replaceAll("\\p{M}", ""); } - result = result.replaceAll("[^a-zA-Z0-9_]", "_").toLowerCase(); + result = result.replaceAll("[^a-zA-Z0-9_]", "-").toLowerCase(); return result; } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties index 3f972eaace4f5..753f2b8f7bce2 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties @@ -3,6 +3,29 @@ addon.siemenshvac.name = SiemensHvac Binding addon.siemenshvac.description = This is the binding for SiemensHvac. +# thing types + +thing-type.siemenshvac.ozw672.label = OZW672 IP Gateway +thing-type.siemenshvac.ozw672.description = This is a OZW672 IP interface +thing-type.siemenshvac.ozw772.label = OZW772 IP Gateway +thing-type.siemenshvac.ozw772.description = This is a OZW772 IP interface + +# thing types config + +thing-type.config.siemenshvac.ozw672.baseUrl.label = Base URL +thing-type.config.siemenshvac.ozw672.baseUrl.description = The URL of the Siemens Hvac IP gateway. Must be in format http://hostname/ or https://hostname/. Don't forget the trailing '/' +thing-type.config.siemenshvac.ozw672.userName.label = User Name +thing-type.config.siemenshvac.ozw672.userName.description = User name of the Siemens Hvac gateway +thing-type.config.siemenshvac.ozw672.userPassword.label = User Password +thing-type.config.siemenshvac.ozw672.userPassword.description = User password of the Siemens Hvac gateway +thing-type.config.siemenshvac.ozw772.baseUrl.label = Base URL +thing-type.config.siemenshvac.ozw772.baseUrl.description = The URL of the Siemens Hvac IP gateway. Must be in format http://hostname/ or https://hostname/. Don't forget the trailing '/' +thing-type.config.siemenshvac.ozw772.userName.label = User Name +thing-type.config.siemenshvac.ozw772.userName.description = User name of the Siemens Hvac gateway +thing-type.config.siemenshvac.ozw772.userPassword.label = User Password +thing-type.config.siemenshvac.ozw772.userPassword.description = User password of the Siemens Hvac gateway + + # thing types thing-type.siemenshvac.ozw.label = OZW IP Gateway @@ -19,10 +42,8 @@ thing-type.config.siemenshvac.ozw.userPassword.description = User password of th # offline message -offline.waiting-bridge-initialization = Waiting bridge initialization, reading metadata in background - offline.baseurl-mandatory = baseUrl is mandatory on configuration. offline.error-gateway-init = Error occurred during gateway initialization: {0} - offline.config-not-init = Config not initialize during reading metadata, aborting. offline.user-not-find = Cannot find user during reading metadata, aborting. +offline.waiting-bridge-initialization = Waiting bridge initialization, reading metadata in background From c6a437f03f2ce4bdecd479a2db718add78b4eed4 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 13 Jun 2024 19:03:28 +0200 Subject: [PATCH 183/214] review binding addon.xml Signed-off-by: Laurent ARNAL --- .../src/main/resources/OH-INF/addon/addon.xml | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml index 2db2c99ce8e4d..c8b4ac4be694a 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/addon/addon.xml @@ -1,25 +1,18 @@ - - - + binding SiemensHvac Binding This is the binding for SiemensHvac. local - upnp modelName - Web Server OZW672 - - - modelName - Web Server OZW772 + Web Server OZW.* From 6c6d2a029a9a6cad64fd19ac5848566a085f9d2b Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 13 Jun 2024 19:43:19 +0200 Subject: [PATCH 184/214] fix unit test Signed-off-by: Laurent ARNAL --- .../binding/siemenshvac/internal/type/UidUtilsTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java b/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java index 75eb50e8e3b1c..b94428d4fe071 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java +++ b/bundles/org.openhab.binding.siemenshvac/src/test/java/org/openhab/binding/siemenshvac/internal/type/UidUtilsTest.java @@ -25,8 +25,8 @@ public class UidUtilsTest { @Test public void testSanetizeId() throws Exception { - assertEquals(UidUtils.sanetizeId("Début heure été"), "debut_heure_ete"); - assertEquals(UidUtils.sanetizeId("App.Ambiance 1"), "app_ambiance_1"); - assertEquals(UidUtils.sanetizeId("Appareil d'ambiance P"), "appareil_d_ambiance_p"); + assertEquals(UidUtils.sanetizeId("Début heure été"), "debut-heure-ete"); + assertEquals(UidUtils.sanetizeId("App.Ambiance 1"), "app-ambiance-1"); + assertEquals(UidUtils.sanetizeId("Appareil d'ambiance P"), "appareil-d-ambiance-p"); } } From aa63fa13a24165d173abbdff886352514c3a0104 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 13 Jun 2024 19:55:22 +0200 Subject: [PATCH 185/214] fix checkstyle errors Signed-off-by: Laurent ARNAL --- .../thirdparty/RuntimeTypeAdapterFactory.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/thirdparty/RuntimeTypeAdapterFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/thirdparty/RuntimeTypeAdapterFactory.java index 96d3e1fb75860..4f647a68f716f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/thirdparty/RuntimeTypeAdapterFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/thirdparty/RuntimeTypeAdapterFactory.java @@ -1,3 +1,15 @@ +/** + * Copyright (c) 2010-2024 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 + */ /* * Copyright (C) 2011 Google Inc. * @@ -168,6 +180,9 @@ * } *
*/ +/** + * @author Jesse Wilson (swankjesse) - Initial contribution + */ @NonNullByDefault public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory { private final Class baseType; From 4fe620c033063e10fa7056382e76e904cf87ba24 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 13 Jun 2024 22:19:56 +0200 Subject: [PATCH 186/214] progress on last jlaur review Signed-off-by: Laurent ARNAL --- .../SiemensHvacBindingConstants.java | 5 +- .../SiemenesHvacDiscoveryParticipant.java | 6 +- .../SiemensHvacDeviceDiscoveryService.java | 2 +- .../factory/SiemensHvacHandlerFactory.java | 6 +- .../SiemensHvacMetadataRegistryImpl.java | 3 +- .../network/SiemensHvacConnectorImpl.java | 2 +- .../OH-INF/i18n/siemenshvac.properties | 23 ---- .../src/main/resources/OH-INF/thing/ozw.xml | 101 +----------------- 8 files changed, 13 insertions(+), 135 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java index 9ce12e5d562ba..f92d5e8e71436 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/constants/SiemensHvacBindingConstants.java @@ -31,10 +31,9 @@ public class SiemensHvacBindingConstants { public static final String CONFIG_DESCRIPTION_URI_CHANNEL = "channel-type:siemenshvac:config"; // List of all Thing Type UIDs - public static final ThingTypeUID THING_TYPE_OZW672 = new ThingTypeUID(BINDING_ID, "ozw672"); - public static final ThingTypeUID THING_TYPE_OZW772 = new ThingTypeUID(BINDING_ID, "ozw772"); + public static final ThingTypeUID THING_TYPE_OZW = new ThingTypeUID(BINDING_ID, "ozw"); - public static final Set SUPPORTED_THING_TYPES = Set.of(SiemensHvacBindingConstants.THING_TYPE_OZW672); + public static final Set SUPPORTED_THING_TYPES = Set.of(SiemensHvacBindingConstants.THING_TYPE_OZW); public static final String IP_ADDRESS = "ipAddress"; public static final String BASE_URL = "baseUrl"; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java index 0b165b51ba2e5..b14d2dc1f3451 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemenesHvacDiscoveryParticipant.java @@ -70,7 +70,7 @@ public Set getSupportedThingTypeUIDs() { if (uid.getAsString().contains("ozw672")) { label = "OZW672 IP Gateway"; - } else if (uid.getAsString().contains("ozw672")) { + } else if (uid.getAsString().contains("ozw772")) { label = "OZW772 IP Gateway"; } @@ -100,9 +100,9 @@ public Set getSupportedThingTypeUIDs() { String modelName = modelDetails.getModelName(); if (modelName != null) { if (modelName.startsWith("Web Server OZW672")) { - return new ThingUID(SiemensHvacBindingConstants.THING_TYPE_OZW672, serialNumber.toLowerCase()); + return new ThingUID(SiemensHvacBindingConstants.THING_TYPE_OZW, "ozw672"); } else if (modelName.startsWith("Web Server OZW772")) { - return new ThingUID(SiemensHvacBindingConstants.THING_TYPE_OZW772, serialNumber.toLowerCase()); + return new ThingUID(SiemensHvacBindingConstants.THING_TYPE_OZW, "ozw772"); } } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java index bf765f868f5ce..334822c5591bd 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java @@ -115,7 +115,7 @@ public void startScan() { String typeSn = UidUtils.sanetizeId(type); ThingTypeUID thingTypeUID = new ThingTypeUID(SiemensHvacBindingConstants.BINDING_ID, typeSn); - ThingUID thingUID = getThingUID(thingTypeUID, serialNr); + ThingUID thingUID = getThingUID(thingTypeUID, typeSn); if (lcSiemensHvacBridgeHandler != null) { ThingUID bridgeUID = lcSiemensHvacBridgeHandler.getThing().getUID(); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java index 2da3c09c87c8b..3e003a0a5f984 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java @@ -72,8 +72,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { @Override public @Nullable Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration, @Nullable ThingUID thingUID, @Nullable ThingUID bridgeUID) { - if (SiemensHvacBindingConstants.THING_TYPE_OZW672.equals(thingTypeUID) - || SiemensHvacBindingConstants.THING_TYPE_OZW772.equals(thingTypeUID)) { + if (SiemensHvacBindingConstants.THING_TYPE_OZW.equals(thingTypeUID)) { ThingUID iPBridgeUID = getIPBridgeThingUID(thingTypeUID, thingUID, configuration); return super.createThing(thingTypeUID, configuration, iPBridgeUID, null); } else if (SiemensHvacBindingConstants.BINDING_ID.equals(thingTypeUID.getBindingId())) { @@ -85,8 +84,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { @Override protected @Nullable ThingHandler createHandler(Thing thing) { - if (thing.getThingTypeUID().equals(SiemensHvacBindingConstants.THING_TYPE_OZW672) - || thing.getThingTypeUID().equals(SiemensHvacBindingConstants.THING_TYPE_OZW772)) { + if (thing.getThingTypeUID().equals(SiemensHvacBindingConstants.THING_TYPE_OZW)) { return new SiemensHvacOZWBridgeThingHandler((Bridge) thing, networkAddressService, httpClientFactory, metaDataRegistry); } else if (SiemensHvacBindingConstants.BINDING_ID.equals(thing.getThingTypeUID().getBindingId())) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java index 46af1d19465a6..95880ccbff964 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -620,8 +620,7 @@ private ThingType createThingType(SiemensHvacMetadataDevice device, List supportedBridgeTypeUids = new ArrayList<>(); - supportedBridgeTypeUids.add(SiemensHvacBindingConstants.THING_TYPE_OZW672.toString()); - supportedBridgeTypeUids.add(SiemensHvacBindingConstants.THING_TYPE_OZW772.toString()); + supportedBridgeTypeUids.add(SiemensHvacBindingConstants.THING_TYPE_OZW.toString()); ThingTypeUID thingTypeUID = UidUtils.generateThingTypeUID(device); Map properties = new HashMap<>(); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 867be2d11e6a6..61022fc9f5d90 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -406,7 +406,7 @@ private void doAuth(boolean http) throws SiemensHvacException { SiemensHvacBridgeConfig config = this.config; if (config == null) { - throw new SiemensHvacException("Missing SiemensHvacOZW672 Bridge configuration"); + throw new SiemensHvacException("Missing SiemensHvac OZW Bridge configuration"); } String baseUri = config.baseUrl; diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties index 753f2b8f7bce2..713a67a8147f9 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/i18n/siemenshvac.properties @@ -3,29 +3,6 @@ addon.siemenshvac.name = SiemensHvac Binding addon.siemenshvac.description = This is the binding for SiemensHvac. -# thing types - -thing-type.siemenshvac.ozw672.label = OZW672 IP Gateway -thing-type.siemenshvac.ozw672.description = This is a OZW672 IP interface -thing-type.siemenshvac.ozw772.label = OZW772 IP Gateway -thing-type.siemenshvac.ozw772.description = This is a OZW772 IP interface - -# thing types config - -thing-type.config.siemenshvac.ozw672.baseUrl.label = Base URL -thing-type.config.siemenshvac.ozw672.baseUrl.description = The URL of the Siemens Hvac IP gateway. Must be in format http://hostname/ or https://hostname/. Don't forget the trailing '/' -thing-type.config.siemenshvac.ozw672.userName.label = User Name -thing-type.config.siemenshvac.ozw672.userName.description = User name of the Siemens Hvac gateway -thing-type.config.siemenshvac.ozw672.userPassword.label = User Password -thing-type.config.siemenshvac.ozw672.userPassword.description = User password of the Siemens Hvac gateway -thing-type.config.siemenshvac.ozw772.baseUrl.label = Base URL -thing-type.config.siemenshvac.ozw772.baseUrl.description = The URL of the Siemens Hvac IP gateway. Must be in format http://hostname/ or https://hostname/. Don't forget the trailing '/' -thing-type.config.siemenshvac.ozw772.userName.label = User Name -thing-type.config.siemenshvac.ozw772.userName.description = User name of the Siemens Hvac gateway -thing-type.config.siemenshvac.ozw772.userPassword.label = User Password -thing-type.config.siemenshvac.ozw772.userPassword.description = User password of the Siemens Hvac gateway - - # thing types thing-type.siemenshvac.ozw.label = OZW IP Gateway diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw.xml b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw.xml index 0dbe12208f284..7b43f25f1aa82 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw.xml +++ b/bundles/org.openhab.binding.siemenshvac/src/main/resources/OH-INF/thing/ozw.xml @@ -5,9 +5,9 @@ xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd"> - - - This is a OZW672 IP interface + + + This is a OZW IP interface @@ -32,99 +32,4 @@ - - - - This is a OZW772 IP interface - - - - - url - The URL of the Siemens Hvac IP gateway. Must be in format http://hostname/ or https://hostname/. Don't - forget the trailing '/' - true - - - User name of the Siemens Hvac gateway - false - - Administrator - - - password - User password of the Siemens Hvac gateway - false - - password - - - - - - Number:Temperature - - Temperature - - - - - Number:Temperature - - Setpoint Heat Pump - Temperature - - - - - Number:Temperature - - Room temperature Comfort setpoint HC - Temperature - - - - - Number:Temperature - - Room temp reduced setpoint heat circuit - Temperature - - - - - Number - - Operating mode heat circuit - - - - - - - - - - - - Number - - Status heating circuit - Temperature - - - - - - - - - - - - - - - - From 2242d69d849b5b2e66cc7dbf515e21240c4ad059 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 13 Jun 2024 22:27:12 +0200 Subject: [PATCH 187/214] remove specific device bridge Signed-off-by: Laurent ARNAL --- .../SiemensHvacDeviceDiscoveryService.java | 10 +-- .../factory/SiemensHvacHandlerFactory.java | 4 +- ...ava => SiemensHvacBridgeThingHandler.java} | 18 +++++- .../handler/SiemensHvacHandlerImpl.java | 6 +- .../SiemensHvacOZWBridgeThingHandler.java | 64 ------------------- .../network/SiemensHvacConnector.java | 4 +- .../network/SiemensHvacConnectorImpl.java | 10 +-- 7 files changed, 31 insertions(+), 85 deletions(-) rename bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/{SiemensHvacBridgeBaseThingHandler.java => SiemensHvacBridgeThingHandler.java} (91%) delete mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZWBridgeThingHandler.java diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java index 334822c5591bd..2d7f97be2588b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/discovery/SiemensHvacDeviceDiscoveryService.java @@ -19,7 +19,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; -import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeBaseThingHandler; +import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeThingHandler; import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDevice; import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataRegistry; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacException; @@ -49,7 +49,7 @@ public class SiemensHvacDeviceDiscoveryService extends AbstractDiscoveryService private final Logger logger = LoggerFactory.getLogger(SiemensHvacDeviceDiscoveryService.class); private @Nullable SiemensHvacMetadataRegistry metadataRegistry; - private @Nullable SiemensHvacBridgeBaseThingHandler siemensHvacBridgeHandler; + private @Nullable SiemensHvacBridgeThingHandler siemensHvacBridgeHandler; private static final int SEARCH_TIME = 10; @@ -75,7 +75,7 @@ protected void stopBackgroundDiscovery() { } private @Nullable ThingUID getThingUID(ThingTypeUID thingTypeUID, String serial) { - final SiemensHvacBridgeBaseThingHandler lcSiemensHvacBridgeHandler = siemensHvacBridgeHandler; + final SiemensHvacBridgeThingHandler lcSiemensHvacBridgeHandler = siemensHvacBridgeHandler; if (lcSiemensHvacBridgeHandler != null) { ThingUID localBridgeUID = lcSiemensHvacBridgeHandler.getThing().getUID(); return new ThingUID(thingTypeUID, localBridgeUID, serial); @@ -86,7 +86,7 @@ protected void stopBackgroundDiscovery() { @Override public void startScan() { final SiemensHvacMetadataRegistry lcMetadataRegistry = metadataRegistry; - final SiemensHvacBridgeBaseThingHandler lcSiemensHvacBridgeHandler = siemensHvacBridgeHandler; + final SiemensHvacBridgeThingHandler lcSiemensHvacBridgeHandler = siemensHvacBridgeHandler; logger.debug("call startScan()"); if (lcMetadataRegistry != null) { @@ -145,7 +145,7 @@ protected synchronized void stopScan() { @Override public void setThingHandler(@Nullable ThingHandler handler) { - if (handler instanceof SiemensHvacBridgeBaseThingHandler siemensHvacBridgeHandler) { + if (handler instanceof SiemensHvacBridgeThingHandler siemensHvacBridgeHandler) { this.siemensHvacBridgeHandler = siemensHvacBridgeHandler; this.siemensHvacBridgeHandler.registerDiscoveryListener(this); } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java index 3e003a0a5f984..5acd8ed037af4 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/factory/SiemensHvacHandlerFactory.java @@ -16,8 +16,8 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants; import org.openhab.binding.siemenshvac.internal.converter.ConverterFactory; +import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeThingHandler; import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacHandlerImpl; -import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacOZWBridgeThingHandler; import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataRegistry; import org.openhab.core.config.core.Configuration; import org.openhab.core.i18n.TimeZoneProvider; @@ -85,7 +85,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { @Override protected @Nullable ThingHandler createHandler(Thing thing) { if (thing.getThingTypeUID().equals(SiemensHvacBindingConstants.THING_TYPE_OZW)) { - return new SiemensHvacOZWBridgeThingHandler((Bridge) thing, networkAddressService, httpClientFactory, + return new SiemensHvacBridgeThingHandler((Bridge) thing, networkAddressService, httpClientFactory, metaDataRegistry); } else if (SiemensHvacBindingConstants.BINDING_ID.equals(thing.getThingTypeUID().getBindingId())) { SiemensHvacHandlerImpl handler = new SiemensHvacHandlerImpl(thing, diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeThingHandler.java similarity index 91% rename from bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java rename to bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeThingHandler.java index 73b5be69f1578..b09617e17be72 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeBaseThingHandler.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacBridgeThingHandler.java @@ -17,6 +17,8 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.time.LocalDate; +import java.util.Collection; +import java.util.Set; import java.util.concurrent.TimeUnit; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -32,6 +34,7 @@ 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.ThingHandlerService; import org.openhab.core.types.Command; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,15 +46,15 @@ * @author Laurent Arnal - Initial contribution and API */ @NonNullByDefault -public abstract class SiemensHvacBridgeBaseThingHandler extends BaseBridgeHandler { +public class SiemensHvacBridgeThingHandler extends BaseBridgeHandler { - private final Logger logger = LoggerFactory.getLogger(SiemensHvacBridgeBaseThingHandler.class); + private final Logger logger = LoggerFactory.getLogger(SiemensHvacBridgeThingHandler.class); private @Nullable SiemensHvacDeviceDiscoveryService discoveryService; private final @Nullable HttpClientFactory httpClientFactory; private final SiemensHvacMetadataRegistry metaDataRegistry; private @Nullable SiemensHvacBridgeConfig config; - public SiemensHvacBridgeBaseThingHandler(Bridge bridge, @Nullable NetworkAddressService networkAddressService, + public SiemensHvacBridgeThingHandler(Bridge bridge, @Nullable NetworkAddressService networkAddressService, @Nullable HttpClientFactory httpClientFactory, SiemensHvacMetadataRegistry metaDataRegistry) { super(bridge); SiemensHvacConnector lcConnector = null; @@ -78,6 +81,10 @@ public void initialize() { SiemensHvacBridgeConfig lcConfig = getConfigAs(SiemensHvacBridgeConfig.class); String baseUrl = null; + if (logger.isDebugEnabled()) { + logger.debug("Initialize() bridge: {}", getBuildDate()); + } + baseUrl = lcConfig.baseUrl; if (baseUrl.isEmpty()) { @@ -185,4 +192,9 @@ public boolean unregisterDiscoveryListener() { public @Nullable HttpClientFactory getHttpClientFactory() { return this.httpClientFactory; } + + @Override + public Collection> getServices() { + return Set.of(SiemensHvacDeviceDiscoveryService.class); + } } diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java index 654c1e28f26d3..e31b20caf9fed 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacHandlerImpl.java @@ -149,8 +149,7 @@ private void pollingCode() { double errorRate = (double) errorCount / requestCount * 100.00; if (errorRate > 50) { - SiemensHvacBridgeBaseThingHandler bridgeHandler = (SiemensHvacBridgeBaseThingHandler) lcBridge - .getHandler(); + SiemensHvacBridgeThingHandler bridgeHandler = (SiemensHvacBridgeThingHandler) lcBridge.getHandler(); if (lcHvacConnector.getErrorSource() == ErrorSource.ErrorBridge) { if (bridgeHandler != null) { @@ -164,8 +163,7 @@ private void pollingCode() { } else { updateStatus(ThingStatus.ONLINE); - SiemensHvacBridgeBaseThingHandler bridgeHandler = (SiemensHvacBridgeBaseThingHandler) lcBridge - .getHandler(); + SiemensHvacBridgeThingHandler bridgeHandler = (SiemensHvacBridgeThingHandler) lcBridge.getHandler(); // Automatically recover from communication errors if errorRate is ok. if (bridgeHandler != null) { diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZWBridgeThingHandler.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZWBridgeThingHandler.java deleted file mode 100644 index 09faf41a8b45e..0000000000000 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/handler/SiemensHvacOZWBridgeThingHandler.java +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright (c) 2010-2024 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.siemenshvac.internal.handler; - -import java.util.Collection; -import java.util.Set; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.siemenshvac.internal.discovery.SiemensHvacDeviceDiscoveryService; -import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataRegistry; -import org.openhab.core.io.net.http.HttpClientFactory; -import org.openhab.core.net.NetworkAddressService; -import org.openhab.core.thing.Bridge; -import org.openhab.core.thing.binding.ThingHandlerService; -import org.osgi.service.component.annotations.Activate; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The {@link SiemensHvacOZWBridgeThingHandler} is responsible for handling communication to Siemens Gateway using - * HTTP API interface. - * - * @author Laurent ARNAL - Initial contribution - */ -@NonNullByDefault -public class SiemensHvacOZWBridgeThingHandler extends SiemensHvacBridgeBaseThingHandler { - - private final Logger logger = LoggerFactory.getLogger(SiemensHvacOZWBridgeThingHandler.class); - - @Activate - public SiemensHvacOZWBridgeThingHandler(Bridge bridge, @Nullable NetworkAddressService networkAddressService, - @Nullable HttpClientFactory httpClientFactory, SiemensHvacMetadataRegistry metaDataRegistry) { - super(bridge, networkAddressService, httpClientFactory, metaDataRegistry); - } - - @Override - public void initialize() { - if (logger.isDebugEnabled()) { - logger.debug("Initialize() bridge: {}", getBuildDate()); - } - super.initialize(); - } - - @Override - public void dispose() { - super.dispose(); - } - - @Override - public Collection> getServices() { - return Set.of(SiemensHvacDeviceDiscoveryService.class); - } -} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java index 97707ecbcd103..3b5583cf83ee3 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnector.java @@ -15,8 +15,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.api.Request; -import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeBaseThingHandler; import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeConfig; +import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeThingHandler; import org.openhab.binding.siemenshvac.internal.type.SiemensHvacException; import com.google.gson.Gson; @@ -46,7 +46,7 @@ public interface SiemensHvacConnector { void onError(Request request, SiemensHvacRequestHandler reqListener, SiemensHvacRequestListener.ErrorSource errorSource, boolean mayRetry) throws SiemensHvacException; - void setSiemensHvacBridgeBaseThingHandler(SiemensHvacBridgeBaseThingHandler hvacBridgeBaseThingHandler); + void setSiemensHvacBridgeBaseThingHandler(SiemensHvacBridgeThingHandler hvacBridgeBaseThingHandler); @Nullable SiemensHvacBridgeConfig getBridgeConfiguration(); diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java index 61022fc9f5d90..12c2c341e8412 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/network/SiemensHvacConnectorImpl.java @@ -33,8 +33,8 @@ import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.util.ssl.SslContextFactory; -import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeBaseThingHandler; import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeConfig; +import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeThingHandler; import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadata; import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint; import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataMenu; @@ -86,7 +86,7 @@ public class SiemensHvacConnectorImpl implements SiemensHvacConnector { private int timeout = 10; private SiemensHvacRequestListener.ErrorSource errorSource = SiemensHvacRequestListener.ErrorSource.ErrorBridge; - private @Nullable SiemensHvacBridgeBaseThingHandler hvacBridgeBaseThingHandler; + private @Nullable SiemensHvacBridgeThingHandler hvacBridgeBaseThingHandler; @Activate public SiemensHvacConnectorImpl(@Reference HttpClientFactory httpClientFactory) { @@ -127,11 +127,11 @@ public SiemensHvacConnectorImpl(@Reference HttpClientFactory httpClientFactory) @Override public void setSiemensHvacBridgeBaseThingHandler( - @Nullable SiemensHvacBridgeBaseThingHandler hvacBridgeBaseThingHandler) { + @Nullable SiemensHvacBridgeThingHandler hvacBridgeBaseThingHandler) { this.hvacBridgeBaseThingHandler = hvacBridgeBaseThingHandler; } - public void unsetSiemensHvacBridgeBaseThingHandler(SiemensHvacBridgeBaseThingHandler hvacBridgeBaseThingHandler) { + public void unsetSiemensHvacBridgeBaseThingHandler(SiemensHvacBridgeThingHandler hvacBridgeBaseThingHandler) { this.hvacBridgeBaseThingHandler = null; } @@ -282,7 +282,7 @@ private void registerHandlerError(SiemensHvacRequestHandler handler) { } private void initConfig() throws SiemensHvacException { - SiemensHvacBridgeBaseThingHandler lcHvacBridgeBaseThingHandler = hvacBridgeBaseThingHandler; + SiemensHvacBridgeThingHandler lcHvacBridgeBaseThingHandler = hvacBridgeBaseThingHandler; if (lcHvacBridgeBaseThingHandler != null) { config = lcHvacBridgeBaseThingHandler.getBridgeConfiguration(); From bae833f0d6aa5b43b644e5e8f3a802a1a3e969cf Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 13 Jun 2024 22:44:59 +0200 Subject: [PATCH 188/214] remove unless samples Signed-off-by: Laurent ARNAL --- bundles/org.openhab.binding.siemenshvac/README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/README.md b/bundles/org.openhab.binding.siemenshvac/README.md index 4f2dd93fdf9ca..a82ada3c1306a 100644 --- a/bundles/org.openhab.binding.siemenshvac/README.md +++ b/bundles/org.openhab.binding.siemenshvac/README.md @@ -87,10 +87,6 @@ Bridge siemenshvac:ozw672:local "Ozw672" [ baseUrl="https://192.168.254.42/", us { Thing RVS41_813_327 local "RVS41.813/327" [ ] { - Channels: - Type Setpoint:temperature "Temperature" [ id="1726" ] - Type Regime:cc1 "CC1" [ id="1725" ] - } ``` From 04a45e8bbf203d630af626f83ab25b66ea4835a0 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Thu, 13 Jun 2024 22:44:59 +0200 Subject: [PATCH 189/214] remove unless samples Signed-off-by: Laurent ARNAL --- bundles/org.openhab.binding.siemenshvac/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/README.md b/bundles/org.openhab.binding.siemenshvac/README.md index a82ada3c1306a..f8ac4cf33643d 100644 --- a/bundles/org.openhab.binding.siemenshvac/README.md +++ b/bundles/org.openhab.binding.siemenshvac/README.md @@ -63,8 +63,8 @@ Each channel is strongly typed, so for example, for heating mode, openHAB will p Channel | Description | Type | Unit | Security Access Level | ReadOnly | Advanced -----------------------|----------------------------------------------------------|---------------|----------|-------------------------|-----------|------------------- -controlBoilerApproval | Set Boiler Approval (`AUTO`, `OFF`, `ON`) | String | | | R/W | true -controlProgram | Set Program (`OFF`, `NORMAL`, `WARMWATER`, `MANUAL`) | String | | | R/W | true +control-boiler-approval | Set Boiler Approval (`AUTO`, `OFF`, `ON`) | String | | | R/W | true +control-program | Set Program (`OFF`, `NORMAL`, `WARMWATER`, `MANUAL`) | String | | | R/W | true Channel Type ID | Item Type | Description -----------------------|------------------------------------------|---------------------------------------------- From ccc662eafe5edfe95f2a7b56e50315705794786c Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Fri, 14 Jun 2024 11:49:26 +0200 Subject: [PATCH 190/214] try to implements TemperatureChangeRate (WIP) Signed-off-by: Laurent ARNAL --- .../converter/type/NumericTypeConverter.java | 21 +++++-- .../internal/converter/type/SiemensUnit.java | 61 +++++++++++++++++++ .../SiemensHvacMetadataRegistryImpl.java | 3 +- 3 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SiemensUnit.java diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java index 9ab78f6dda1c2..0612d28b5e9e3 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java @@ -23,6 +23,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.siemenshvac.internal.converter.ConverterException; +import org.openhab.binding.siemenshvac.internal.converter.type.SiemensUnit.SIEUnits.TemperatureChangeRate; import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.DecimalType; @@ -43,6 +44,7 @@ */ @NonNullByDefault public class NumericTypeConverter extends AbstractTypeConverter { + @Override protected boolean toBindingValidation(Type type) { return true; @@ -83,12 +85,20 @@ protected State fromBinding(JsonElement value, String unit, String type, Channel if ("°C".equals(unit)) { targetUnit = SIUnits.CELSIUS; - } else if ("°C*min".equals(unit)) { - targetUnit = SIUnits.CELSIUS; } else if ("°F".equals(unit)) { targetUnit = ImperialUnits.FAHRENHEIT; + } + + if (targetUnit != null) { + return new QuantityType<>(dValue, targetUnit); + } + } else if ("Number:TemperatureChangeRate".equals(itemType)) { + Unit targetUnit = null; + + if ("°C*min".equals(unit)) { + targetUnit = SiemensUnit.SIEUnits.CELSIUS_PER_MINUTE; } else if ("°F*min".equals(unit)) { - targetUnit = ImperialUnits.FAHRENHEIT; + targetUnit = SiemensUnit.SIEUnits.FAHRENHEIT_PER_MINUTE; } if (targetUnit != null) { @@ -149,6 +159,7 @@ public String getChannelType(SiemensHvacMetadataDataPoint dpt) { @Override public String getItemType(SiemensHvacMetadataDataPoint dpt) { String unit = dpt.getDptUnit(); + Unit unit2 = SiemensUnit.SIEUnits.CELSIUS_PER_MINUTE; if (unit == null) { return CoreItemFactory.NUMBER; @@ -161,8 +172,10 @@ public String getItemType(SiemensHvacMetadataDataPoint dpt) { switch (unit) { case "°F": case "°C": - case "°C*min": return CoreItemFactory.NUMBER + ":Temperature"; + case "°F*min": + case "°C*min": + return CoreItemFactory.NUMBER + ":TemperatureChangeRate"; case "V": return CoreItemFactory.NUMBER + ":ElectricPotential"; case "%": diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SiemensUnit.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SiemensUnit.java new file mode 100644 index 0000000000000..c2f9ca19c565a --- /dev/null +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/SiemensUnit.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2010-2024 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.siemenshvac.internal.converter.type; + +import javax.measure.Quantity; +import javax.measure.Unit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.library.unit.ImperialUnits; +import org.openhab.core.library.unit.SIUnits; +import org.openhab.core.library.unit.Units; + +import tech.units.indriya.format.SimpleUnitFormat; +import tech.units.indriya.unit.ProductUnit; + +/** + * Specific SiemensHvac Unit + * + * @author Laurent Arnal - Initial contribution + */ +@NonNullByDefault +public enum SiemensUnit { + + CELSIUS_PER_MINUTE(0x2700, "org.siemens.unit.celcius_per_minute", SIEUnits.CELSIUS_PER_MINUTE), + FAHRENHEIT_PER_MINUTE(0x2701, "org.siemens.unit.fahrenheit_per_minute", SIEUnits.FAHRENHEIT_PER_MINUTE); + + private SiemensUnit(long key, String type, Unit unit) { + } + + public static class SIEUnits { + public static final Unit CELSIUS_PER_MINUTE = addUnit( + new ProductUnit<>(SIUnits.CELSIUS.multiply(Units.MINUTE))); + + public static final Unit FAHRENHEIT_PER_MINUTE = addUnit( + new ProductUnit<>(ImperialUnits.FAHRENHEIT.multiply(Units.MINUTE))); + + private static > U addUnit(U unit) { + return unit; + } + + public interface TemperatureChangeRate extends Quantity { + } + + // public QuantityType temperatureChangeRate; + + static { + SimpleUnitFormat.getInstance().label(CELSIUS_PER_MINUTE, "°C*min"); + SimpleUnitFormat.getInstance().label(FAHRENHEIT_PER_MINUTE, "°F*min"); + } + } +} diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java index 95880ccbff964..3c25e50a38d7f 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -718,7 +718,8 @@ public static String getStatePattern(SiemensHvacMetadataDataPoint dpt) { } if (unit != null && !unit.isEmpty()) { - return String.format("%s %s", "%." + digits + "f", "%unit%"); + // return String.format("%s %s", "%." + digits + "f", "%unit%"); + return String.format("%s %s", "%." + digits + "f", unit); } else { return String.format("%s", "%." + digits + "f"); } From 364b5b0f44842936a8fd73acc8326ea620c9a4ac Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Fri, 14 Jun 2024 11:49:26 +0200 Subject: [PATCH 191/214] try to implements TemperatureChangeRate (WIP) Signed-off-by: Laurent ARNAL --- .../internal/metadata/SiemensHvacMetadataRegistryImpl.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java index 3c25e50a38d7f..ce5dca721d64a 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -718,8 +718,6 @@ public static String getStatePattern(SiemensHvacMetadataDataPoint dpt) { } if (unit != null && !unit.isEmpty()) { - // return String.format("%s %s", "%." + digits + "f", "%unit%"); - return String.format("%s %s", "%." + digits + "f", unit); } else { return String.format("%s", "%." + digits + "f"); } From a14c6697cfd9ef5b51fb6f91a6bd78560136ba83 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Fri, 14 Jun 2024 11:49:26 +0200 Subject: [PATCH 192/214] try to implements TemperatureChangeRate (WIP) Signed-off-by: Laurent ARNAL --- .../internal/metadata/SiemensHvacMetadataRegistryImpl.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java index ce5dca721d64a..ccf835527562b 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -718,6 +718,8 @@ public static String getStatePattern(SiemensHvacMetadataDataPoint dpt) { } if (unit != null && !unit.isEmpty()) { + return String.format("%s %s", "%." + digits + "f", "%unit%"); + } else { return String.format("%s", "%." + digits + "f"); } From 57269e13b6e1a202b5f9f128db05817e718a6035 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Fri, 14 Jun 2024 16:54:02 +0200 Subject: [PATCH 193/214] spotless:apply Signed-off-by: Laurent ARNAL --- .../internal/metadata/SiemensHvacMetadataRegistryImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java index ccf835527562b..95880ccbff964 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/metadata/SiemensHvacMetadataRegistryImpl.java @@ -719,7 +719,6 @@ public static String getStatePattern(SiemensHvacMetadataDataPoint dpt) { if (unit != null && !unit.isEmpty()) { return String.format("%s %s", "%." + digits + "f", "%unit%"); - } else { return String.format("%s", "%." + digits + "f"); } From 1ec0ff9765be3a8fe034799b6888419305b58e29 Mon Sep 17 00:00:00 2001 From: Laurent ARNAL Date: Fri, 14 Jun 2024 19:02:50 +0200 Subject: [PATCH 194/214] fix translation on units Signed-off-by: Laurent ARNAL --- .../converter/type/NumericTypeConverter.java | 86 +++++++++++++++++-- 1 file changed, 78 insertions(+), 8 deletions(-) diff --git a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java index 0612d28b5e9e3..06cdc686e1bc0 100644 --- a/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java +++ b/bundles/org.openhab.binding.siemenshvac/src/main/java/org/openhab/binding/siemenshvac/internal/converter/type/NumericTypeConverter.java @@ -97,6 +97,10 @@ protected State fromBinding(JsonElement value, String unit, String type, Channel if ("°C*min".equals(unit)) { targetUnit = SiemensUnit.SIEUnits.CELSIUS_PER_MINUTE; + } else if ("°Cmin".equals(unit)) { + targetUnit = SiemensUnit.SIEUnits.CELSIUS_PER_MINUTE; + } else if ("°Cdak".equals(unit)) { + targetUnit = SiemensUnit.SIEUnits.CELSIUS_PER_MINUTE; } else if ("°F*min".equals(unit)) { targetUnit = SiemensUnit.SIEUnits.FAHRENHEIT_PER_MINUTE; } @@ -117,14 +121,50 @@ protected State fromBinding(JsonElement value, String unit, String type, Channel } else if ("Number:Time".equals(itemType)) { Unit