From ddff0f4e5b7fe988701f61db686e07b13fea03ad Mon Sep 17 00:00:00 2001 From: Guenther Schreiner Date: Fri, 8 Nov 2019 19:00:30 +0100 Subject: [PATCH 1/9] ***NOTE***: Temporary eliminated due to Jenkins errors: BlueGiga Bluetooth Adapter, Modbus Binding ***NOTE***: Temporary eliminated due to Jenkins errors: org.openhab.voice.googletts [velux] OH2 binding based on OH1 binding (v1.14 code) with compliance for PaperUI and discovery, updated README w/ new examples. [velux] README updated, minor changes in configuration (property inverted automaticly assigned), naming of things optimized, discovery fixed. [velux] All classes adopted to @NonNullByDefault. [velux] Annotations fixed. [velux] Many changes necessary to fully support @NonNullByDefault. [velux] Adaptions according to initial review by @Hilbrand (20190910:0030). [velux] Logging adapted according to new Jenkis rules. [velux] Parallel activities mode integrated. Sanities according to analysis report added. Logging harmonized. pom.xml with re-enabled outside-of-this-scope modules. [velux] Deleted obsolete localization (de_DE), added _nl, _dk. Bug fix for somfy shutters. More strict config patterns. Virtual shutter fully supported. [velux] Localization in all config files added. Obsolete code from openHAB1 eliminated (virtual shutter). [velux] Update: README, bugfix: config parameters, added: processing of GW_ACTIVATION_LOG_UPDATED_NTF. [velux] class/method visibility adapted according to advise by tool UCDetector. Optimized bridge queries (speed-up). [velux] Adaptions according to latest review by @Hilbrand (thanks!) on 2019/10/16. [velux] Adapted bundles/pom.xml [velux] Adapted whitespaces [velux] Non-null Annotations fixed. Co-Authored-By: Hilbrand Bouwkamp Signed-off-by: Guenther Schreiner --- CODEOWNERS | 1 + bom/openhab-addons/pom.xml | 5 + bundles/org.openhab.binding.velux/.classpath | 32 + bundles/org.openhab.binding.velux/.gitignore | 27 + bundles/org.openhab.binding.velux/.project | 23 + bundles/org.openhab.binding.velux/NOTICE | 13 + bundles/org.openhab.binding.velux/README.md | 563 +++++++++++++++ .../doc/conf/items/velux.items | 73 ++ .../doc/conf/rules/velux.rules | 44 ++ .../doc/conf/sitemaps/velux.sitemap | 66 ++ .../doc/conf/things/velux.things | 56 ++ .../lib/gson-2.2.4.jar | Bin 0 -> 190418 bytes bundles/org.openhab.binding.velux/pom.xml | 16 + .../src/main/feature/feature.xml | 9 + .../binding/velux/VeluxBindingConstants.java | 144 ++++ .../binding/velux/VeluxBindingProperties.java | 56 ++ .../binding/velux/bridge/VeluxBridge.java | 284 ++++++++ .../velux/bridge/VeluxBridgeActuators.java | 175 +++++ .../bridge/VeluxBridgeDetectProducts.java | 92 +++ .../VeluxBridgeDeviceCheckLostNodes.java | 55 ++ .../velux/bridge/VeluxBridgeDeviceStatus.java | 108 +++ .../velux/bridge/VeluxBridgeGetFirmware.java | 104 +++ .../bridge/VeluxBridgeGetHouseStatus.java | 62 ++ .../bridge/VeluxBridgeGetLimitation.java | 61 ++ .../velux/bridge/VeluxBridgeInstance.java | 69 ++ .../velux/bridge/VeluxBridgeLANConfig.java | 114 +++ .../velux/bridge/VeluxBridgeProvider.java | 59 ++ .../bridge/VeluxBridgeRunProductCommand.java | 64 ++ .../velux/bridge/VeluxBridgeRunScene.java | 78 ++ .../velux/bridge/VeluxBridgeScenes.java | 130 ++++ .../VeluxBridgeSetHouseStatusMonitor.java | 63 ++ .../bridge/VeluxBridgeSetSceneVelocity.java | 61 ++ .../velux/bridge/VeluxBridgeWLANConfig.java | 108 +++ .../velux/bridge/common/BridgeAPI.java | 101 +++ .../common/BridgeCommunicationProtocol.java | 52 ++ .../velux/bridge/common/GetDeviceStatus.java | 41 ++ .../velux/bridge/common/GetFirmware.java | 43 ++ .../velux/bridge/common/GetHouseStatus.java | 32 + .../velux/bridge/common/GetLANConfig.java | 43 ++ .../velux/bridge/common/GetProduct.java | 51 ++ .../bridge/common/GetProductLimitation.java | 51 ++ .../velux/bridge/common/GetProducts.java | 44 ++ .../velux/bridge/common/GetScenes.java | 43 ++ .../velux/bridge/common/GetWLANConfig.java | 42 ++ .../binding/velux/bridge/common/Login.java | 53 ++ .../binding/velux/bridge/common/Logout.java | 31 + .../bridge/common/RunProductCommand.java | 44 ++ .../bridge/common/RunProductDiscovery.java | 32 + .../common/RunProductIdentification.java | 45 ++ .../velux/bridge/common/RunProductSearch.java | 32 + .../binding/velux/bridge/common/RunScene.java | 55 ++ .../bridge/common/SetHouseStatusMonitor.java | 43 ++ .../velux/bridge/common/SetSceneVelocity.java | 37 + .../velux/bridge/common/package-info.java | 18 + .../velux/bridge/json/JCgetDeviceStatus.java | 168 +++++ .../velux/bridge/json/JCgetFirmware.java | 163 +++++ .../velux/bridge/json/JCgetLANConfig.java | 187 +++++ .../velux/bridge/json/JCgetProducts.java | 194 +++++ .../velux/bridge/json/JCgetScenes.java | 225 ++++++ .../velux/bridge/json/JCgetWLANConfig.java | 181 +++++ .../binding/velux/bridge/json/JClogin.java | 173 +++++ .../binding/velux/bridge/json/JClogout.java | 135 ++++ .../bridge/json/JCrunProductDiscovery.java | 157 ++++ .../json/JCrunProductIdentification.java | 173 +++++ .../velux/bridge/json/JCrunProductSearch.java | 147 ++++ .../binding/velux/bridge/json/JCrunScene.java | 154 ++++ .../velux/bridge/json/JCsetSceneVelocity.java | 166 +++++ .../velux/bridge/json/JsonBridgeAPI.java | 203 ++++++ .../json/JsonBridgeCommunicationProtocol.java | 93 +++ .../velux/bridge/json/JsonVeluxBridge.java | 320 ++++++++ .../velux/bridge/json/package-info.java | 18 + .../binding/velux/bridge/package-info.java | 19 + .../velux/bridge/slip/SCgetDeviceStatus.java | 152 ++++ .../velux/bridge/slip/SCgetFirmware.java | 165 +++++ .../velux/bridge/slip/SCgetHouseStatus.java | 184 +++++ .../velux/bridge/slip/SCgetLANConfig.java | 153 ++++ .../velux/bridge/slip/SCgetLimitation.java | 234 ++++++ .../velux/bridge/slip/SCgetProduct.java | 268 +++++++ .../velux/bridge/slip/SCgetProducts.java | 276 +++++++ .../velux/bridge/slip/SCgetScenes.java | 175 +++++ .../velux/bridge/slip/SCgetWLANConfig.java | 124 ++++ .../binding/velux/bridge/slip/SClogin.java | 164 +++++ .../binding/velux/bridge/slip/SClogout.java | 102 +++ .../bridge/slip/SCrunProductCommand.java | 313 ++++++++ .../bridge/slip/SCrunProductDiscovery.java | 169 +++++ .../slip/SCrunProductIdentification.java | 204 ++++++ .../velux/bridge/slip/SCrunProductSearch.java | 113 +++ .../binding/velux/bridge/slip/SCrunScene.java | 291 ++++++++ .../bridge/slip/SCsetHouseStatusMonitor.java | 145 ++++ .../velux/bridge/slip/SCsetSceneVelocity.java | 175 +++++ .../velux/bridge/slip/SlipBridgeAPI.java | 207 ++++++ .../slip/SlipBridgeCommunicationProtocol.java | 85 +++ .../velux/bridge/slip/SlipVeluxBridge.java | 346 +++++++++ .../velux/bridge/slip/io/Connection.java | 241 +++++++ .../velux/bridge/slip/io/SSLconnection.java | 245 +++++++ .../velux/bridge/slip/io/package-info.java | 18 + .../velux/bridge/slip/package-info.java | 18 + .../bridge/slip/util/KLF200Response.java | 164 +++++ .../velux/bridge/slip/util/Packet.java | 277 +++++++ .../velux/bridge/slip/util/SlipEncoding.java | 170 +++++ .../velux/bridge/slip/util/SlipRFC1055.java | 128 ++++ .../velux/bridge/slip/util/package-info.java | 18 + .../discovery/VeluxDiscoveryService.java | 236 ++++++ .../binding/velux/discovery/package-info.java | 18 + .../binding/velux/handler/BridgeChannels.java | 79 ++ .../handler/ChannelActuatorLimitation.java | 239 ++++++ .../handler/ChannelActuatorPosition.java | 244 +++++++ .../velux/handler/ChannelBridgeCheck.java | 112 +++ .../handler/ChannelBridgeDoDetection.java | 78 ++ .../velux/handler/ChannelBridgeFirmware.java | 72 ++ .../velux/handler/ChannelBridgeLANconfig.java | 98 +++ .../velux/handler/ChannelBridgeProducts.java | 73 ++ .../velux/handler/ChannelBridgeScenes.java | 73 ++ .../velux/handler/ChannelBridgeStatus.java | 72 ++ .../handler/ChannelBridgeWLANconfig.java | 88 +++ .../velux/handler/ChannelSceneAction.java | 121 ++++ .../velux/handler/ChannelSceneSilentmode.java | 101 +++ .../handler/ChannelVShutterPosition.java | 166 +++++ .../handler/ExtendedBaseBridgeHandler.java | 65 ++ .../handler/ExtendedBaseThingHandler.java | 68 ++ .../binding/velux/handler/StateUtils.java | 87 +++ .../binding/velux/handler/ThingProperty.java | 155 ++++ .../velux/handler/VeluxBindingHandler.java | 201 ++++++ .../velux/handler/VeluxBridgeHandler.java | 682 ++++++++++++++++++ .../velux/handler/VeluxChannelHandler.java | 71 ++ .../binding/velux/handler/VeluxHandler.java | 126 ++++ .../binding/velux/handler/package-info.java | 18 + .../binding/velux/internal/VeluxBinding.java | 123 ++++ .../velux/internal/VeluxBindingConfig.java | 90 +++ .../velux/internal/VeluxHandlerFactory.java | 136 ++++ .../binding/velux/internal/VeluxItemType.java | 477 ++++++++++++ .../velux/internal/VeluxRSBindingConfig.java | 213 ++++++ .../config/VeluxBridgeConfiguration.java | 75 ++ .../config/VeluxThingConfiguration.java | 140 ++++ .../velux/internal/config/package-info.java | 18 + .../binding/velux/internal/package-info.java | 18 + .../velux/internal/utils/Localization.java | 56 ++ .../velux/internal/utils/LoggerFulltrace.java | 88 +++ .../internal/utils/ManifestInformation.java | 58 ++ .../velux/internal/utils/package-info.java | 18 + .../openhab/binding/velux/package-info.java | 18 + .../velux/things/VeluxExistingProducts.java | 205 ++++++ .../velux/things/VeluxExistingScenes.java | 118 +++ .../binding/velux/things/VeluxGwFirmware.java | 53 ++ .../binding/velux/things/VeluxGwLAN.java | 77 ++ .../binding/velux/things/VeluxGwState.java | 175 +++++ .../binding/velux/things/VeluxGwWLAN.java | 59 ++ .../binding/velux/things/VeluxKLFAPI.java | 356 +++++++++ .../binding/velux/things/VeluxProduct.java | 332 +++++++++ .../velux/things/VeluxProductName.java | 50 ++ .../velux/things/VeluxProductPosition.java | 165 +++++ .../velux/things/VeluxProductReference.java | 78 ++ .../velux/things/VeluxProductSerialNo.java | 138 ++++ .../velux/things/VeluxProductState.java | 90 +++ .../velux/things/VeluxProductType.java | 127 ++++ .../velux/things/VeluxProductVelocity.java | 96 +++ .../binding/velux/things/VeluxScene.java | 139 ++++ .../binding/velux/things/package-info.java | 18 + .../resources/ESH-INF/binding/binding.xml | 11 + .../main/resources/ESH-INF/config/config.xml | 196 +++++ .../resources/ESH-INF/i18n/velux.properties | 165 +++++ .../ESH-INF/i18n/velux_de.properties | 325 +++++++++ .../ESH-INF/i18n/velux_dk.properties | 160 ++++ .../ESH-INF/i18n/velux_nl.properties | 160 ++++ .../main/resources/ESH-INF/thing/actuator.xml | 29 + .../main/resources/ESH-INF/thing/binding.xml | 24 + .../main/resources/ESH-INF/thing/bridge.xml | 43 ++ .../main/resources/ESH-INF/thing/channels.xml | 181 +++++ .../resources/ESH-INF/thing/rollershutter.xml | 28 + .../main/resources/ESH-INF/thing/scene.xml | 28 + .../main/resources/ESH-INF/thing/vshutter.xml | 25 + .../main/resources/ESH-INF/thing/window.xml | 28 + bundles/pom.xml | 5 +- 173 files changed, 20501 insertions(+), 2 deletions(-) create mode 100644 bundles/org.openhab.binding.velux/.classpath create mode 100644 bundles/org.openhab.binding.velux/.gitignore create mode 100644 bundles/org.openhab.binding.velux/.project create mode 100644 bundles/org.openhab.binding.velux/NOTICE create mode 100644 bundles/org.openhab.binding.velux/README.md create mode 100644 bundles/org.openhab.binding.velux/doc/conf/items/velux.items create mode 100644 bundles/org.openhab.binding.velux/doc/conf/rules/velux.rules create mode 100644 bundles/org.openhab.binding.velux/doc/conf/sitemaps/velux.sitemap create mode 100644 bundles/org.openhab.binding.velux/doc/conf/things/velux.things create mode 100644 bundles/org.openhab.binding.velux/lib/gson-2.2.4.jar create mode 100644 bundles/org.openhab.binding.velux/pom.xml create mode 100644 bundles/org.openhab.binding.velux/src/main/feature/feature.xml create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/VeluxBindingConstants.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/VeluxBindingProperties.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridge.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeActuators.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeDetectProducts.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeDeviceCheckLostNodes.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeDeviceStatus.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeGetFirmware.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeGetHouseStatus.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeGetLimitation.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeInstance.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeLANConfig.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeProvider.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeRunProductCommand.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeRunScene.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeScenes.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeSetHouseStatusMonitor.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeSetSceneVelocity.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeWLANConfig.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/BridgeAPI.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/BridgeCommunicationProtocol.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetDeviceStatus.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetFirmware.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetHouseStatus.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetLANConfig.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetProduct.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetProductLimitation.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetProducts.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetScenes.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetWLANConfig.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/Login.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/Logout.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductCommand.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductDiscovery.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductIdentification.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductSearch.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunScene.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/SetHouseStatusMonitor.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/SetSceneVelocity.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/package-info.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetDeviceStatus.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetFirmware.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetLANConfig.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetProducts.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetScenes.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetWLANConfig.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JClogin.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JClogout.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunProductDiscovery.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunProductIdentification.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunProductSearch.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunScene.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCsetSceneVelocity.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JsonBridgeAPI.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JsonBridgeCommunicationProtocol.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JsonVeluxBridge.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/package-info.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/package-info.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetDeviceStatus.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetFirmware.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetHouseStatus.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetLANConfig.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetLimitation.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetProduct.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetProducts.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetScenes.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetWLANConfig.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SClogin.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SClogout.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductCommand.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductDiscovery.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductIdentification.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductSearch.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunScene.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCsetHouseStatusMonitor.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCsetSceneVelocity.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SlipBridgeAPI.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SlipBridgeCommunicationProtocol.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SlipVeluxBridge.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/io/Connection.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/io/SSLconnection.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/io/package-info.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/package-info.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/KLF200Response.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/Packet.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/SlipEncoding.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/SlipRFC1055.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/package-info.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/discovery/VeluxDiscoveryService.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/discovery/package-info.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/BridgeChannels.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelActuatorLimitation.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelActuatorPosition.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeCheck.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeDoDetection.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeFirmware.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeLANconfig.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeProducts.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeScenes.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeStatus.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeWLANconfig.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelSceneAction.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelSceneSilentmode.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelVShutterPosition.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ExtendedBaseBridgeHandler.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ExtendedBaseThingHandler.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/StateUtils.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ThingProperty.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxBindingHandler.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxBridgeHandler.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxChannelHandler.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxHandler.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/package-info.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConfig.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxHandlerFactory.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxItemType.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxRSBindingConfig.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxBridgeConfiguration.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxThingConfiguration.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/package-info.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/package-info.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/Localization.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/LoggerFulltrace.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/ManifestInformation.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/package-info.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/package-info.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxExistingProducts.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxExistingScenes.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwFirmware.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwLAN.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwState.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwWLAN.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxKLFAPI.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProduct.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductName.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductPosition.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductReference.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductSerialNo.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductState.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductType.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductVelocity.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxScene.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/package-info.java create mode 100644 bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/binding/binding.xml create mode 100644 bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/config/config.xml create mode 100644 bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux.properties create mode 100644 bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_de.properties create mode 100644 bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_dk.properties create mode 100644 bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_nl.properties create mode 100644 bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/actuator.xml create mode 100644 bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/binding.xml create mode 100644 bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/bridge.xml create mode 100644 bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/channels.xml create mode 100644 bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/rollershutter.xml create mode 100644 bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/scene.xml create mode 100644 bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/vshutter.xml create mode 100644 bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/window.xml diff --git a/CODEOWNERS b/CODEOWNERS index d2a825574f51c..eba4444a1bf0c 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -163,6 +163,7 @@ /bundles/org.openhab.binding.valloxmv/ @bjoernbrings /bundles/org.openhab.binding.vektiva/ @octa22 /bundles/org.openhab.binding.velbus/ @cedricboon +/bundles/org.openhab.binding.velux/ @gs4711 /bundles/org.openhab.binding.vitotronic/ @steand /bundles/org.openhab.binding.volvooncall/ @clinique /bundles/org.openhab.binding.weatherunderground/ @lolodomo diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index e1c8d1af1a677..ea78bce9c954d 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -810,6 +810,11 @@ org.openhab.binding.velbus ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.velux + ${project.version} + org.openhab.addons.bundles org.openhab.binding.vitotronic diff --git a/bundles/org.openhab.binding.velux/.classpath b/bundles/org.openhab.binding.velux/.classpath new file mode 100644 index 0000000000000..615608997a6c5 --- /dev/null +++ b/bundles/org.openhab.binding.velux/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.velux/.gitignore b/bundles/org.openhab.binding.velux/.gitignore new file mode 100644 index 0000000000000..052e6417f38dc --- /dev/null +++ b/bundles/org.openhab.binding.velux/.gitignore @@ -0,0 +1,27 @@ +.antlr* +.idea +.DS_Store +*.iml +npm-debug.log +.build.log + +.metadata/ +bin/ +target/ +src-gen/ +xtend-gen/ + +*/plugin.xml_gen +**/.settings/org.eclipse.* + +bundles/**/src/main/history +features/**/src/main/history +features/**/src/main/feature + +.vscode +.factorypath + +doc/**/*.html +doc/package-list +doc/script.js +doc/stylesheet.css diff --git a/bundles/org.openhab.binding.velux/.project b/bundles/org.openhab.binding.velux/.project new file mode 100644 index 0000000000000..e49cf15609981 --- /dev/null +++ b/bundles/org.openhab.binding.velux/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.velux + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/bundles/org.openhab.binding.velux/NOTICE b/bundles/org.openhab.binding.velux/NOTICE new file mode 100644 index 0000000000000..4c20ef446c1e4 --- /dev/null +++ b/bundles/org.openhab.binding.velux/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/openhab2-addons diff --git a/bundles/org.openhab.binding.velux/README.md b/bundles/org.openhab.binding.velux/README.md new file mode 100644 index 0000000000000..18be768dff6bf --- /dev/null +++ b/bundles/org.openhab.binding.velux/README.md @@ -0,0 +1,563 @@ + +# Velux Binding + +This binding integrates the Velux devices with help of a gateway, the Velux Bridge KLF200, which is able to control 200 actuators. +The Velux Binding interacts via the Velux Bridge with any [io-homecontrol](http://www.io-homecontrol.com/)-based +devices like window openers, shutters and others. + +Based on the VELUX API this binding integrates Velux and other io-homecontrol devices directly into the openHAB, avoiding the necessity of any cloud-based mediation infrastructures. The complete home-automation will work even without any Internet connectivity. + +For details about the features, see the following websites: + +- [Velux](http://www.velux.com) +- [Velux API](http://www.velux.com/api/klf200) + +## Overview + +As the API is widely open, there are several use cases which are supported by the Bridge: +From the complete configuration of a set of io-homecontrol devices including registration, naming, grouping, crypto key setup and exchange, the definition of intended settings, so called scenes, up to the control of single devices, i.e. ```open window of bathroom up to 45%```. + +The following areas are covered: + +| Topic | Details | +|-------------------------|------------------------------------------------------------------------------------------------------------| +| General bridge commands | SW version(\*), Gateway state(\*), Learn state, Clock, Reboot, FactoryReset, Network Setup(+) | +| Configuration Services | Node Discovery, Node Removal, Controller Copy, Crypto Key Generation, Crypto Key Exchange, Actuator config | +| Information Services | House Monitoring Service(\*), Node information(+), Group information | +| Activation Logging | | +| Command Handling | Command activation(\*), Command interruption(\*), Status Request(\*), Actuator Identification(\*), Limitations | +| Scene Handling | Scene definition, Scene execution(\*), Scene deletion, Scene renaming, Scene Overview(\*) | +| Physical I/O Handling | I/O Port setup | + +Items marked with (\*) are fully implemented. Items marked with (+) have only partial support. + +## Binding Configuration + +The Velux KLF200 bridge has to be configured with some parameters, at least with the IP address of the bridge. + +| Property | Default | Required | Description | +|----------------------|------------------|:--------:|--------------------------------------------------------------| +| ipAddress | | Yes | Hostname or address for accessing the Velux Bridge. | +| protocol | slip | No | Underlying communication protocol (http/https/slip). | +| tcpPort | 51200 | No | TCP port (80 or 51200) for accessing the Velux Bridge. | +| password | velux123 | No | Password for authentication against the Velux Bridge.(\*\*) | +| timeoutMsecs | 1000 | No | Initial Connection timeout in milliseconds. | +| retries | 5 | No | Number of retries during I/O. | +| refreshMsecs | 10000 | No | Refresh interval in milliseconds. | +| isBulkRetrievalEnabled | yes | No | Load all scenes and actuators in one step. | +| isSequentialEnforced | no | No | Enforce Sequential Actuator Control even for long operations.| +| isProtocolTraceEnabled | no | No | Show any protocol interaction (loglevel INFO). | + +(\*\*) Note: This password is the API password that is printed on the back of the unit. Normally it differs from the password of the web frontend. + +Advise: if you see a significant number of messages per day like + +``` + communicate(): socket I/O failed continuously (x times). +``` + +please increase the parameters retries or/and timeoutMsecs. + +For your convenience you'll see a log entry for the recognized configuration within the log file i.e. + +``` +2018-07-23 20:40:24.746 [INFO ] [.b.velux.internal.VeluxBinding] - veluxConfig[ipAddress=192.168.42.1,tcpPort=80,password=********,timeoutMsecs=2000,retries=10] +``` + +The Velux Things (beside the mentioned bridge) are Velux Window, Velux Rollershutter, and a generic Velux Actuator and Velux Scene. The 1st three Things have to be configured with an identification by their serial number. + +| Property | Default | Required | Description | +|----------------|------------------------|:--------:|-----------------------------------------------------------| +| serial | | Yes | Serial number of the io-homecontrol device. | +| name | | No | (Optional) name of the io-homecontrol device. | +| inverted | false | No | Inverts any device values. | + +The fourth Thing, the Velux Scene, has to be configured with an identification by their scenename. + +| Property | Default | Required | Description | +|----------------|------------------------|:--------:|-----------------------------------------------------------| +| sceneName | | Yes | Name of the io-homecontrol configuration. | + +The fifth Thing, the Velux Virtual Shutter, has to be configured with pairs of level combined with the appropriate scenenames. + +| Property | Default | Required | Description | +|----------------|------------------------|:--------:|-----------------------------------------------------------| +| sceneLevels | | Yes | ,,,,.... | +| currentLevel | 0 | No | Inverts any device values. | + + +## Discovery + +Unfortunately there is no way to discover the Velux bridge itself within the local network. But after configuring the Velux Bridge, it is possible to discover all scenes and actuators like windows and rollershutters by the binding. + +## Item Configuration + +The Items of a Velux Bridge consists in general of a pair of mastertype and subtype definition. +In the appropriate items file, i.e. velux.items, this looks like + +``` +{ velux="thing=;channel=" } +``` + +Optionally the subtype is enhanced with parameters like the appropriate name of the scene. + +``` +{ velux="thing=;channel=#" } +``` + +| Mastertype | Description | +|---------------|----------------------------------------------------------------------------------| +| binding | Provides informations for easier configuration of this binding. | +| bridge | The Velux KLF200 represents a gateway to all Velux devices. | +| scene | Named ordered set of product states which can be activated for execution. | +| actuator | Generic IO-home controlled device which can be maintained by parameter settings. | +| window | IO-home controlled device of type window. | +| rollershutter | IO-home controlled device of type rollershutter. | +| vshutter | IO-home controlled device of type rollershutter. | + + +### Subtype + + +| Subtype | Item Type | Description | Mastertype | Parameter | +|--------------|---------------|-----------------------------------------------------------------|------------|-----------| +| information | String | Describes the current state of the binding | binding | N/A | +| status | String | Current Bridge State (\*\*\*) | bridge | N/A | +| reload | Switch | Reload information from bridge into binding | bridge | N/A | +| timestamp | Number | Timestamp of last successful device interaction | bridge | N/A | +| doDetection | Switch | Start of the product detection mode | bridge | N/A | +| firmware | String | Software version of the Bridge | bridge | N/A | +| ipAddress | String | IP address of the Bridge | bridge | N/A | +| subnetMask | String | IP subnetmask of the Bridge | bridge | N/A | +| defaultGW | String | IP address of the Default Gateway of the Bridge | bridge | N/A | +| DHCP | Switch | Flag whether automatic IP configuration is enabled | bridge | N/A | +| WLANSSID | String | Name of the wireless network | bridge | N/A | +| WLANPassword | String | WLAN Authentication Password | bridge | N/A | +| products | String | List of all recognized products | bridge | N/A | +| scenes | String | List of all defined scenes | bridge | N/A | +| check | String | Result of the check of current item configuration | bridge | N/A | +| shutter | Rollershutter | Virtual rollershutter as combination of different scenes | bridge | required | +| position | Rollershutter | Position of the IO-Homecontrol'ed device (\*\*\*\*) (\*\*\*\*\*)| actuator | required | +| state | Switch | Position of the IO-Homecontrol'ed device (\*\*\*\*) (\*\*\*\*\*)| actuator | required | +| serial | Rollershutter | IO-Homecontrol'ed device (\*\*\*\*) (\*\*\*\*\*) | actuator | required | +| silentMode | Switch | NOT YET IMPLEMENTED. | actuator | required | +| action | Switch | Activates a set of predefined product settings | scene | required | +| silentMode | Switch | Modification of the silent mode of the defined product settings | scene | required | + +Notes: +(\*\*\*) The existence of this item triggers the continuous realtime status updates of any Velux item like shutters even if they are manually controlled by other controllers. + +(\*\*\*\*) To enable a complete invertion of all parameter values (i.e. for Velux windows), add a trailing star to the eight-byte serial number. For an example, +see below at item `Velux DG Window Bathroom`. + +(\*\*\*\*\*) Somfy devices does not provides a valid serial number to the Velux KLF200 gateway: The bridge reports a registration of the serial number 00:00:00:00:00:00:00:00. Therefore the binding implements a fallback to allow an item specification with a actuator name instead of actuator serial number whenever such an invalid serial number occurs. For an example, see below at item `Velux OG Somfy Shutter`. + + +### Subtype Parameters + +In case of the scene-related subtypes, action and silentMode, the specification of the related scene as parameters is necessary; + +``` +{ velux="thing=scene;channel=#" } +``` + +The subtype shutter requires an even pair of parameters, each defining the shutter level and the related scene: + +``` +{ velux="thing=brigde;channel=shutter#,,," } +``` + +### Virtual shutter + +As the bridge does not support a real rollershutter interaction, this binding provides a virtual rollershutter consisting of different scenes which set a specific shutter level. Therefore the item definition contains multiple pairs of rollershutter levels each followed by a scene name, which leads to this setting. + + +## Full Example for firmware version One + + + +### Items + +``` +// Group for simulating push buttons + +Group:Switch:OR(ON, OFF) gV "PushButton" + +// Velux Scenes + +Switch V_W_S_OPEN "Velux DG Rolladen West open" (gV) { velux="thing=scene;channel=action#V_Shutter_West_000" } +Switch V_W_S_SUNNY "Velux DG Rolladen West sunny" (gV) { velux="thing=scene;channel=action#V_Shutter_West_090" } +Switch V_W_S_CLOSED "Velux DG Rolladen West closed" (gV) { velux="thing=scene;channel=action#V_Shutter_West_100" } + +Switch V_O_S_OPEN "Velux DG Rolladen Ost open" (gV) { velux="thing=scene;channel=action#V_Shutter_Ost_000" } +Switch V_O_S_SUNNY "Velux DG Rolladen Ost sunny" (gV) { velux="thing=scene;channel=action#V_Shutter_Ost_090" } +Switch V_O_S_CLOSED "Velux DG Rolladen Ost closed" (gV) { velux="thing=scene;channel=action#V_Shutter_Ost_100" } + +Switch V_M_S_OPEN "Velux DG Rolladen Mitte open" (gV) { velux="thing=scene;channel=action#V_Shutter_Mitte_000" } +Switch V_M_S_SUNNY "Velux DG Rolladen Mitte sunny" (gV) { velux="thing=scene;channel=action#V_Shutter_Mitte_090" } +Switch V_M_S_CLOSED "Velux DG Rolladen Mitte closed" (gV) { velux="thing=scene;channel=action#V_Shutter_Mitte_100" } + +Switch V_M_W_OPEN "Velux DG Window open" (gV) { velux="thing=scene;channel=action#V_Window_Mitte_000" } +Switch V_M_W_UNLOCKED "Velux DG Window unlocked" (gV) { velux="thing=scene;channel=action#V_Window_Mitte_010" } +Switch V_M_W_CLOSED "Velux DG Window closed" (gV) { velux="thing=scene;channel=action#V_Window_Mitte_100" } + +Switch V_OPEN "Velux DG open" (gV) { velux="thing=scene;channel=action#V_Shutters_000" } +Switch V_SUNNY "Velux DG sunny" (gV) { velux="thing=scene;channel=action#V_Shutters_090" } +Switch V_CLOSED "Velux DG closed" (gV) { velux="thing=scene;channel=action#V_Shutters_100" } + +// Velux Bridge parameters + +Switch V_RELOAD "Reload info from bridge" { velux="thing=bridge;channel=reload" } +String V_STATUS "Status [%s]" { velux="thing=bridge;channel=status" } +String V_TIMESTAMP "Timestamp [%.1f]" { velux="thing=bridge;channel=timestamp" } +String V_CHECK "Velux Config Check [%s]" { velux="thing=bridge;channel=check" } +String V_FIRMWARE "Firmware [%s]" { velux="thing=bridge;channel=firmware" } +String V_CONF_LAN_IP "KLF LAN IP [%s]" { velux="thing=bridge;channel=ipAddress" } +String V_CONF_LAN_SUBNET "KLF LAN Subnet [%s]" { velux="thing=bridge;channel=subnetMask" } +String V_CONF_LAN_GW "KLF LAN Gateway [%s]" { velux="thing=bridge;channel=defaultGW" } +Switch V_CONF_LAN_DHCP "KLF LAN DHCP [%s]" { velux="thing=bridge;channel=DHCP" } +String V_CONF_WLAN_SSID "KLF WLAN SSID [%s]" { velux="thing=bridge;channel=WLANSSID" } +String V_CONF_WLAN_PW "KLF WLAN Password [%s]"{ velux="thing=bridge;channel=WLANPassword" } + + +// Velux Shutters + +Rollershutter V_W_S "Velux DG Rolladen West [%d]" { velux="thing=bridge;channel=shutter#0,V_Shutter_West_000,90,V_Shutter_West_090, 100,V_Shutter_West_100"} +Rollershutter V_O_S "Velux DG Rolladen Ost [%d]" { velux="thing=bridge;channel=shutter#0,V_Shutter_Ost_000,90,V_Shutter_Ost_090,10 0,V_Shutter_Ost_100"} +Rollershutter V_M_S "Velux DG Rolladen Mitte [%d]" { velux="thing=bridge;channel=shutter#0,V_Shutter_Mitte_000,90,V_Shutter_Mitte_09 +0,100,V_Shutter_Mitte_100"} +Rollershutter V_M_W "Velux DG Window Mitte [%d]" { velux="thing=bridge;channel=shutter#0,V_Window_Mitte_000,10,V_Window_Mitte_010, +100,V_Window_Mitte_100"} +``` + +### Sitemap + +``` +sitemap velux label="Velux Environment" +{ + Frame label="Velux Shutter and Window" { + Switch item=V_W_S + Switch item=V_O_S + Switch item=V_M_S + Switch item=V_M_W + } + Frame label="Velux Bridge" { + Switch item=V_RELOAD + Text item=V_STATUS + Text item=V_TIMESTAMP + Text item=V_CHECK + Text item=V_FIRMWARE + Text item=V_CONF_LAN_IP + Text item=V_CONF_LAN_SUBNET + Text item=V_CONF_LAN_GW + Switch item=V_CONF_LAN_DHCP + Text item=V_CONF_WLAN_SSID + Text item=V_CONF_WLAN_PW + } +} +``` + +### Rules + +``` +/** + * This rule simulates the push button behaviour. + */ +rule "PushButton of group gV" + when + Item gV changed + then + // waiting a second. + Thread::sleep(1000) + // Foreach-Switch-is-ON + gV.allMembers.filter( s | s.state == ON).forEach[i| + // switching OFF + i.sendCommand(OFF) + ] + end +``` + +## Full Example for firmware version Two (text-based configuration) + + +### Things + +``` +// +// Definition of Velux bridge velux:klf200:home +// + +Bridge velux:klf200:home [ ipAddress="192.168.1.1", tcpPort=51200, password="secret" ] { + +// Velux scenes + + Thing scene windowClosed [ sceneName="V_Window_Mitte_000" ] + Thing scene windowUnlocked [ sceneName="V_Window_Mitte_005" ] + Thing scene windowOpened [ sceneName="V_Window_Mitte_100" ] + +// Velux IO-homecontrol devices + + Thing actuator V_M_W [ serial="43:12:3E:26:0C:1B:00:10" ] + Thing actuator V_M_S [ serial="43:12:14:5A:12:1C:05:5F" ] + Thing actuator V_W_S [ serial="43:12:40:5A:0C:2A:05:64" ] + Thing actuator V_O_S [ serial="43:12:40:5A:0C:23:0A:6E",inverted=true ] + +} + +``` + +### Items + +``` +// Group for simulating push buttons + +Group:Switch:OR(ON, OFF) gV "PushButton" + +// Velux Bridge channels + +String V_BRIDGE_STATUS "Velux Bridge Status [%s]" { channel="velux:klf200:home:status" } +Switch V_BRIDGE_RELOAD "Velux Bridge Reload" (gV) { channel="velux:klf200:home:reload" } +String V_BRIDGE_TIMESTAMP "Velux Bridge Timestamp [%d]" { channel="velux:klf200:home:timestamp" } + +String V_BRIDGE_FIRMWARE "Velux Bridge Firmware version [%s]" { channel="velux:klf200:home:firmware" } +String V_BRIDGE_IPADDRESS "Velux Bridge LAN IP Address" { channel="velux:klf200:home:ipAddress" } +String V_BRIDGE_SUBNETMASK "Velux Bridge LAN IP Subnet Mask" { channel="velux:klf200:home:subnetMask" } +String V_BRIDGE_DEFAULTGW "Velux Bridge LAN Default Gateway" { channel="velux:klf200:home:defaultGW" } +String V_BRIDGE_DHCP "Velux Bridge LAN DHCP Enabled" { channel="velux:klf200:home:DHCP" } +String V_BRIDGE_WLANSSID "Velux Bridge WLAN SSID" { channel="velux:klf200:home:WLANSSID" } +String V_BRIDGE_WLANPASSWD "Velux Bridge WLAN Password" { channel="velux:klf200:home:WLANPassword" } + +Switch V_BRIDGE_DETECTION "Velux Bridge Detection mode" (gV) { channel="velux:klf200:home:doDetection" } +String V_BRIDGE_CHECK "Velux Bridge Check" { channel="velux:klf200:home:check" } +String V_BRIDGE_SCENES "Velux Bridge Scenes" { channel="velux:klf200:home:scenes" } +String V_BRIDGE_PRODUCTS "Velux Bridge Products" { channel="velux:klf200:home:products" } + +// Velux Scene channels + +Switch V_M_W_OPEN "Velux Window open" (gV) { channel="velux:scene:home:windowOpened:action" } +Switch V_M_W_UNLOCKED "Velux Window a little open" (gV) { channel="velux:scene:home:windowUnlocked:action" } +Switch V_M_W_CLOSED "Velux Window closed" (gV) { channel="velux:scene:home:windowClosed:action" } + +// Velux Bridge channel + +Rollershutter RS2 "Velux Rolladen 2 [%d]" { channel="velux:klf200:home:shutter#0,V_Shutter_Ost_000,100,V_Shutter_Ost_100", channel="knx:device:bridge:control:VeluxFenster" } + +// Velux Actuator channels + +Rollershutter V_M_W "DG Fenster Bad [%d]" { channel="velux:klf200:home:V_M_W" } +Rollershutter V_M_S "DG Bad [%d]" { channel="velux:klf200:home:V_M_S" } +Rollershutter V_W_S "DG West [%d]" { channel="velux:klf200:home:V_W_S" } +Rollershutter V_O_S "DG Ost [%d]" { channel="velux:klf200:home:V_O_S" } + +``` + +### Sitemap + +``` +sitemap velux label="Velux Environment" +{ + Frame label="Velux Shutter and Window" { + + Switch item=V_M_W_OPEN + Switch item=V_M_W_UNLOCKED + Switch item=V_M_W_CLOSED + Slider item=V_M_W + } + + Frame label="Velux Bridge Status" { + Text item=V_BRIDGE_STATUS + Text item=V_BRIDGE_TIMESTAMP + Switch item=V_BRIDGE_RELOAD + } + + Frame label="Velux Bridge Status" { + Switch item=V_BRIDGE_DETECTION + Text item=V_BRIDGE_CHECK + Text item=V_BRIDGE_SCENES + Text item=V_BRIDGE_PRODUCTS + } + + Frame label="Velux Bridge Configuration" { + Text item=V_BRIDGE_FIRMWARE + Text item=V_BRIDGE_IPADDRESS + Text item=V_BRIDGE_SUBNETMASK + Text item=V_BRIDGE_DEFAULTGW + Switch item=V_BRIDGE_DHCP + Text item=V_BRIDGE_WLANSSID + Text item=V_BRIDGE_WLANPASSWD + } + +} +``` + +## More automation samples + +At this point some interesting automation rules are included to demonstrate the power of this gateway to the io-homecontrol world. + + +### Closing windows after a period of time + +Especially in the colder months, it is advisable to close the window after adequate ventilation. Therefore, automatic closing after one minute is good to save on heating costs. +However, to allow the case of intentional prolonged opening, an automatic closure is made only with the window fully open. + +``` +/* + * Start of imports + */ + +import org.openhab.core.library.types.* + +/* + * Start of rules + */ + +rule "V_WINDOW_changed" +when + Item V_WINDOW changed +then + logInfo("rules.V_WINDOW", "V_WINDOW_changes() called.") + // + // Get the sensor value + // + val Number windowState = V_WINDOW.state as DecimalType + logWarn("rules.V_WINDOW", "Window state is "+windowState+".") + if (windowState < 80) { + if (windowState == 0) { + logWarn("rules.V_WINDOW", "V-WINDOW changed to fully open.") + + var int interval = 1 + + createTimer(now.plusMinutes(interval)) [| + logWarn("rules.V_WINDOW:event", "event-V_WINDOW(): setting V-WINDOW to 100.") + sendCommand(V_WINDOW,100) + V_WINDOW.postUpdate(100) + logWarn("rules.V_WINDOW:event", "event-V_WINDOW done.") + ] + } else { + logWarn("rules.V_WINDOW", "V-WINDOW changed to partially open.") + } + } + // + // Check type of item + // + logDebug("rules.V_WINDOW", "V_WINDOW_changes finished.") +end + +/* + * end-of-rules/V_WINDOW.rules + */ +``` + +## Debugging + +For those who are interested in more detailed insight of the processing of this binding, a deeper look can be achieved by increased loglevel. + +With Karaf you can use the following command sequence: + +``` +log:set TRACE org.openhab.binding.velux +log:tail +``` + +This, of course, is possible on command line with the commands: + +``` +% openhab-cli console log:set TRACE org.openhab.binding.velux +% openhab-cli console log:tail org.openhab.binding.velux +``` + +On the other hand, if you prefer a textual configuration, you can append the logging definition with: + +``` + + + +``` + +During startup of normal operations, there should be only some few messages within the logfile, like: + +``` +[INFO ] [i.dashboard.internal.DashboardService] - Started Dashboard at http://192.168.45.150:8080 +[INFO ] [i.dashboard.internal.DashboardService] - Started Dashboard at https://192.168.45.150:8443 +[INFO ] [rthome.model.lsp.internal.ModelServer] - Started Language Server Protocol (LSP) service on port 5007 +[INFO ] [e.core.internal.i18n.I18nProviderImpl] - Location set to '49.035932145739444,8.455450258310762'. +[INFO ] [smarthome.event.ExtensionEvent ] - Extension 'package-expert' has been installed. +[INFO ] [marthome.ui.paper.internal.PaperUIApp] - Started Paper UI at /paperui +[INFO ] [.ui.habmin.internal.servlet.HABminApp] - Started HABmin servlet at /habmin +[INFO ] [ding.velux.handler.VeluxBridgeHandler] - Initializing Velux veluxBridge handler for 'velux:klf200:7dff2548'. +[INFO ] [ome.event.ThingStatusInfoChangedEvent] - 'velux:klf200:7dff2548' changed from UNINITIALIZED to INITIALIZING +[INFO ] [ome.event.ThingStatusInfoChangedEvent] - 'velux:klf200:7dff2548' changed from INITIALIZING to UNKNOWN +[INFO ] [nal.VeluxValidatedBridgeConfiguration] - veluxConfig[protocol=slip,ipAddress=192.168.45.9,tcpPort=51200,password=********,timeoutMsecs=1000,retries=5,refreshMsecs=15000,isBulkRetrievalEnabled=true] +[INFO ] [ng.velux.bridge.slip.io.SSLconnection] - Starting velux bridge connection. +[INFO ] [hab.binding.velux.bridge.slip.SClogin] - velux bridge connection successfully established (login succeeded). +[INFO ] [ding.velux.handler.VeluxBridgeHandler] - Found velux scenes: + Scene "V_Shutter_West_100" (index 5) with non-silent mode and 0 actions + Scene "V_Shutter_West_000" (index 4) with non-silent mode and 0 actions + Scene "V_Shutter_Ost_090" (index 10) with non-silent mode and 0 actions + Scene "V_Window_Mitte_005" (index 3) with non-silent mode and 0 actions + Scene "V_Window_Mitte_000" (index 1) with non-silent mode and 0 actions + Scene "V_Window_Mitte_100" (index 2) with non-silent mode and 0 actions + Scene "V_Shutter_West_090" (index 7) with non-silent mode and 0 actions + Scene "V_Window_Mitte_010" (index 0) with non-silent mode and 0 actions + Scene "V_Shutter_Ost_000" (index 8) with non-silent mode and 0 actions + Scene "V_Shutter_Ost_100" (index 9) with non-silent mode and 0 actions . +[INFO ] [ding.velux.handler.VeluxBridgeHandler] - Found velux actuators: + Product "M_Rollershutter" / ROLLER_SHUTTER (bridgeIndex=4,serial=43:12:14:5A:12:1C:05:5F,position=0010) + Product "O_Rollershutter" / ROLLER_SHUTTER (bridgeIndex=3,serial=43:12:40:5A:0C:23:0A:6E,position=0000) + Product "M_Window" / WINDOW_OPENER (bridgeIndex=0,serial=43:12:3E:26:0C:1B:00:10,position=C800) + Product "W-Rollershutter" / ROLLER_SHUTTER (bridgeIndex=1,serial=43:12:40:5A:0C:2A:05:64,position=0000) . +[INFO ] [ding.velux.handler.VeluxBridgeHandler] - velux Bridge is online with 10 scenes and 4 actuators, now. +[INFO ] [ome.event.ThingStatusInfoChangedEvent] - 'velux:klf200:7dff2548' changed from UNKNOWN to ONLINE +[INFO ] [thome.event.ItemChannelLinkAddedEvent] - Link 'velux_klf200_7dff2548_status-velux:klf200:7dff2548:status' has been added. +[INFO ] [smarthome.event.ItemStateChangedEvent] - velux_klf200_7dff2548_status changed from NULL to GW_S_GWM/GW_SS_IDLE +[INFO ] [thome.event.ItemChannelLinkAddedEvent] - Link 'velux_klf200_7dff2548_timestamp-velux:klf200:7dff2548:timestamp' has been added. +[WARN ] [ding.velux.handler.VeluxBridgeHandler] - Exception occurred during activated refresh scheduler: null +[INFO ] [smarthome.event.ItemStateChangedEvent] - velux_klf200_7dff2548_timestamp changed from NULL to 1565646559977 +[INFO ] [thome.event.ItemChannelLinkAddedEvent] - Link 'velux_klf200_7dff2548_products-velux:klf200:7dff2548:products' has been added. +[INFO ] [smarthome.event.ItemStateChangedEvent] - velux_klf200_7dff2548_products changed from NULL to 4_members:_Product_"M_Rollershutter"_/_ROLLER_SHUTTER_(bridgeIndex=4,serial=56:32:14:5A:12:1C:05:5F,position=0010),Product_"O_Rollershutter"_/_ROLLER_SHUTTER_(bridgeIndex=3,serial=53:09:40:5A:0C:23:0A:6E,position=0000),Product_"M_Window"_/_WINDOW_OPENER_(bridgeIndex=0,serial=56:23:3E:26:0C:1B:00:10,position=C800),Product_"W-Rollershutter"_/_ROLLER_SHUTTER_(bridgeIndex=1,serial=53:09:40:5A:0C:2A:05:64,position=0000) +[INFO ] [thome.event.ItemChannelLinkAddedEvent] - Link 'velux_klf200_7dff2548_scenes-velux:klf200:7dff2548:scenes' has been added. +[INFO ] [smarthome.event.ItemStateChangedEvent] - velux_klf200_7dff2548_scenes changed from NULL to 10_members:_Scene_"V_Shutter_West_100"_(index_5)_with_non-silent_mode_and_0_actions,Scene_"V_Shutter_West_000"_(index_4)_with_non-silent_mode_and_0_actions,Scene_"V_Shutter_Ost_090"_(index_10)_with_non-silent_mode_and_0_actions,Scene_"V_Window_Mitte_005"_(index_3)_with_non-silent_mode_and_0_actions,Scene_"V_Window_Mitte_000"_(index_1)_with_non-silent_mode_and_0_actions,Scene_"V_Window_Mitte_100"_(index_2)_with_non-silent_mode_and_0_actions,Scene_"V_Shutter_West_090"_(index_7)_with_non-silent_mode_and_0_actions,Scene_"V_Window_Mitte_010"_(index_0)_with_non-silent_mode_and_0_actions,Scene_"V_Shutter_Ost_000"_(index_8)_with_non-silent_mode_and_0_actions,Scene_"V_Shutter_Ost_100"_(index_9)_with_non-silent_mode_and_0_actions +[INFO ] [smarthome.event.ItemStateChangedEvent] - velux_klf200_7dff2548_timestamp changed from 1565646559977 to 1565646593645 +[INFO ] [ig.discovery.internal.PersistentInbox] - Added new thing 'velux:scene:7dff2548:V_Shutter_West_100' to inbox. +... +``` + +## Supported/Tested Firmware Revisions + +The Velux Bridge in API version one (firmware version 0.1.1.*) allows activating a set of predefined actions, so called scenes. Therefore beside the bridge, only one main thing exists, the scene element. The next-generation firmware version two is not backward compatible, and does not provide a public web frontend, but version two does provide full access to any IO-Home compatible devices not limited to Velux and includes many different features. + +| Firmware revision | Release date | Description | +|:-----------------:|:------------:|-------------------------------------------------------------------------| +| 0.1.1.0.41.0 | 2016-06-01 | Default factory shipping revision. | +| 0.1.1.0.42.0 | 2017-07-01 | Public Web Frontend w/ JSON-API. | +| 0.1.1.0.44.0 | 2017-12-14 | Public Web Frontend w/ JSON-API. | +| 2.0.0.71 | 2018-09-27 | Public SLIP-API w/ private-only WLAN-based Web Frontend w/ JSON-API. | + +Notes: + +- Velux bridges cannot be returned to version one of the firmware after being upgraded to version two. +- Firmware updates are currently provided at [Velux download area](https://updates2.velux.com/). + + +## Is it possible to run the both communication methods in parallel? + +For environments with the firmware version 0.1.* on the gateway, the interaction with the bridge is limited to the HTTP/JSON based communication, of course. On the other hand, after upgrading the gateway firmware to version 2, it is possible to run the binding either using HTTP/JSON if there is a permanent connectivity towards the WLAN interface of the KLF200 or using SLIP towards the LAN interface of the gateway. For example the Raspberry PI can directly be connected via WLAN to the Velux gateway and providing the other services via the LAN interface (but not vice versa). + + +## Known Limitations + +The communication based on HTTP/JSON is limited to one connection: If the binding is operational, you won't get access to the Web Frontend in parallel. + +The SLIP communication is limited to two connections in parallel, i.e. two different openHAB bindings - or - one openHAB binding and another platform connection. + +Both interfacing methods, HTTP/JSON and SLIP, can be run in parallel. Therefore, on the one hand you can use the Web Frontend for manual control and on the other hand a binding can do all automatic jobs. + + +## Unknown Velux devices + +All known Velux devices can be handled by this binding. However, there might be some new ones which will be reported within the logfiles. Therefore, error messages like the one below should be reported to the maintainers so that the new Velux device type can be incorporated." + +``` +[ERROR] [g.velux.things.VeluxProductReference] - PLEASE REPORT THIS TO MAINTAINER: VeluxProductReference(3) has found an unregistered ProductTypeId. +``` + diff --git a/bundles/org.openhab.binding.velux/doc/conf/items/velux.items b/bundles/org.openhab.binding.velux/doc/conf/items/velux.items new file mode 100644 index 0000000000000..3e010fb29a148 --- /dev/null +++ b/bundles/org.openhab.binding.velux/doc/conf/items/velux.items @@ -0,0 +1,73 @@ +/** + * + * Copyright (c) 2010-2019 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 + * + * 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 + */ + +/** + * OpenHAB item defintion for velux binding: + * Velux Bridge and Devices + * + * @author Guenther Schreiner - Initial contribution + */ + +// Group for simulating push buttons + +Group:Switch:OR(ON, OFF) gV "PushButton" + +// Velux Bridge channels + +String V_BRIDGE_STATUS "Velux Bridge Status [%s]" { channel="velux:klf200:home:status" } +Switch V_BRIDGE_RELOAD "Velux Bridge Reload" (gV) { channel="velux:klf200:home:reload" } +String V_BRIDGE_TIMESTAMP "Velux Bridge Timestamp [%d]" { channel="velux:klf200:home:timestamp" } + +String V_BRIDGE_FIRMWARE "Velux Bridge Firmware version [%s]" { channel="velux:klf200:home:firmware" } +String V_BRIDGE_IPADDRESS "Velux Bridge LAN IP Address" { channel="velux:klf200:home:ipAddress" } +String V_BRIDGE_SUBNETMASK "Velux Bridge LAN IP Subnet Mask" { channel="velux:klf200:home:subnetMask" } +String V_BRIDGE_DEFAULTGW "Velux Bridge LAN Default Gateway" { channel="velux:klf200:home:defaultGW" } +String V_BRIDGE_DHCP "Velux Bridge LAN DHCP Enabled" { channel="velux:klf200:home:DHCP" } +String V_BRIDGE_WLANSSID "Velux Bridge WLAN SSID" { channel="velux:klf200:home:WLANSSID" } +String V_BRIDGE_WLANPASSWD "Velux Bridge WLAN Password" { channel="velux:klf200:home:WLANPassword" } + +Switch V_BRIDGE_DETECTION "Velux Bridge Detection mode" (gV) { channel="velux:klf200:home:doDetection" } +String V_BRIDGE_CHECK "Velux Bridge Check" { channel="velux:klf200:home:check" } +String V_BRIDGE_SCENES "Velux Bridge Scenes" { channel="velux:klf200:home:scenes" } +String V_BRIDGE_PRODUCTS "Velux Bridge Products" { channel="velux:klf200:home:products" } + +// Velux Scene channels + +Switch V_DG_M_W_OPEN "Velux DG Window open" (gV) { channel="velux:scene:home:windowOpened:action" } +Switch V_DG_M_W_UNLOCKED "Velux DG Window a little open" (gV) { channel="velux:scene:home:windowUnlocked:action" } +Switch V_DG_M_W_CLOSED "Velux DG Window closed" (gV) { channel="velux:scene:home:windowClosed:action" } + +// Velux Bridge channel + +Rollershutter RS2 "Velux Rolladen 2 [%d]" { channel="velux:klf200:home:shutter#0,V_DG_Shutter_Ost_000,100,V_DG_Shutter_Ost_100", channel="knx:device:bridge:control:VeluxFenster" } + + +// Velux Actuator channels + +Rollershutter V_DG_M_W "DG Fenster Bad [%d]" { channel="velux:klf200:home:V_DG_M_W" } +Rollershutter V_DG_M_W2 "DG Fenster Bad [%d]" { channel="velux:klf200:home:V_DG_M_W2" } +Rollershutter V_DG_M_S "DG Bad [%d]" { channel="velux:klf200:home:V_DG_M_S" } +Rollershutter V_DG_W_S "DG West [%d]" { channel="velux:klf200:home:V_DG_W_S" } +Rollershutter V_DG_O_S "DG Ost [%d]" { channel="velux:klf200:home:V_DG_O_S" } + +// +// vim: syntax=Xtend vim: noai:ts=4:sw=4 +// +// end-of-items/velux.items +// diff --git a/bundles/org.openhab.binding.velux/doc/conf/rules/velux.rules b/bundles/org.openhab.binding.velux/doc/conf/rules/velux.rules new file mode 100644 index 0000000000000..739ed6b3930ee --- /dev/null +++ b/bundles/org.openhab.binding.velux/doc/conf/rules/velux.rules @@ -0,0 +1,44 @@ +/** + * + * Copyright (c) 2010-2019 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 + * + * 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 + */ + +/** + * OpenHAB rules for velux binding: + * This rule simulates the push button behaviour. + * + * @author Guenther Schreiner - Initial contribution + */ + +rule "PushButton of group gV" + when + Item gV changed + then + // waiting a second. + Thread::sleep(1000) + // Foreach-Switch-is-ON + gV.allMembers.filter( s | s.state == ON).forEach[i| + // switching OFF + i.sendCommand(OFF) + ] + end + +// +// vim: syntax=Xtend vim: noai:ts=4:sw=4 +// +// end-of-rules/velux.rules +// diff --git a/bundles/org.openhab.binding.velux/doc/conf/sitemaps/velux.sitemap b/bundles/org.openhab.binding.velux/doc/conf/sitemaps/velux.sitemap new file mode 100644 index 0000000000000..d25ea058bce6b --- /dev/null +++ b/bundles/org.openhab.binding.velux/doc/conf/sitemaps/velux.sitemap @@ -0,0 +1,66 @@ +/** + * + * Copyright (c) 2010-2019 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 + * + * 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 + */ + +/** + * OpenHAB sitemap definition for velux binding: + * displays most of the Velux items + * + * @author Guenther Schreiner - Initial contribution + */ + +sitemap velux label="Velux Environment" +{ + Frame label="Velux Shutter and Window" { + + Switch item=V_DG_M_W_OPEN + Switch item=V_DG_M_W_UNLOCKED + Switch item=V_DG_M_W_CLOSED + Slider item=V_DG_M_W + } + + Frame label="Velux Bridge Status" { + Text item=V_BRIDGE_STATUS + Text item=V_BRIDGE_TIMESTAMP + Switch item=V_BRIDGE_RELOAD + } + + Frame label="Velux Bridge Status" { + Switch item=V_BRIDGE_DETECTION + Text item=V_BRIDGE_CHECK + Text item=V_BRIDGE_SCENES + Text item=V_BRIDGE_PRODUCTS + } + + Frame label="Velux Bridge Configuration" { + Text item=V_BRIDGE_FIRMWARE + Text item=V_BRIDGE_IPADDRESS + Text item=V_BRIDGE_SUBNETMASK + Text item=V_BRIDGE_DEFAULTGW + Switch item=V_BRIDGE_DHCP + Text item=V_BRIDGE_WLANSSID + Text item=V_BRIDGE_WLANPASSWD + } + +} + +// +// vim: syntax=Xtend vim: noai:ts=4:sw=4 +// +// end-of-sitemap/velux.sitemap +// diff --git a/bundles/org.openhab.binding.velux/doc/conf/things/velux.things b/bundles/org.openhab.binding.velux/doc/conf/things/velux.things new file mode 100644 index 0000000000000..20ede95546b70 --- /dev/null +++ b/bundles/org.openhab.binding.velux/doc/conf/things/velux.things @@ -0,0 +1,56 @@ +/** + * + * Copyright (c) 2010-2019 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 + * + * 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 + */ + +/** + * OpenHAB thing definition for velux binding: + * define basic Velux bridge parameters + * + * @author Guenther Schreiner - Initial contribution + */ + +// +// Definition of Velux bridge velux:klf200:home +// + +Bridge velux:klf200:home [ ipAddress="192.168.45.9", tcpPort=51200 ] { + +// Velux scenes + + Thing scene windowClosed [ sceneName="V_DG_Window_Mitte_000" ] + Thing scene windowUnlocked [ sceneName="V_DG_Window_Mitte_005" ] + Thing scene windowOpened [ sceneName="V_DG_Window_Mitte_100" ] + Thing scene unknownScene [ sceneName="ThisIsADummySceneName" ] + +// Velux IO-homecontrol devices + + Thing window V_DG_M_W [ serial="56:23:3E:26:0C:1B:00:10" ] + Thing rollershutter V_DG_M_S [ serial="56:32:14:5A:12:1C:05:5F" ] + Thing rollershutter V_DG_W_S [ serial="53:09:40:5A:0C:2A:05:64" ] + Thing actuator V_DG_O_S [ serial="53:09:40:5A:0C:23:0A:6E" ] + +// Virtual rollershutter + + Thing vshutter V_WINDOW [ sceneLevels="0,V_DG_Window_Mitte_000#5,V_DG_Window_Mitte_005#100,V_DG_Window_Mitte_100" ] +} + +// +// vim: syntax=Xtend vim: noai:ts=4:sw=4 +// +// end-of-things/velux.things +// diff --git a/bundles/org.openhab.binding.velux/lib/gson-2.2.4.jar b/bundles/org.openhab.binding.velux/lib/gson-2.2.4.jar new file mode 100644 index 0000000000000000000000000000000000000000..9478253e8cb62cd840a21e1f323b68a507c98d66 GIT binary patch literal 190418 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

y)d@^y=s9`#39r;>ehppwbYq0U{|$2}4K7 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

-X*@Tz0V@JE<{JVc!OO7(MCcH*%up!yjaDX0y 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 M$1P8rF<>(NAA9kPA^-pY literal 0 HcmV?d00001 diff --git a/bundles/org.openhab.binding.velux/pom.xml b/bundles/org.openhab.binding.velux/pom.xml new file mode 100644 index 0000000000000..4a138563ced08 --- /dev/null +++ b/bundles/org.openhab.binding.velux/pom.xml @@ -0,0 +1,16 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.velux + + openHAB Add-ons :: Bundles :: Velux Binding + + diff --git a/bundles/org.openhab.binding.velux/src/main/feature/feature.xml b/bundles/org.openhab.binding.velux/src/main/feature/feature.xml new file mode 100644 index 0000000000000..8f81632822ccd --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/feature/feature.xml @@ -0,0 +1,9 @@ + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${project.version}/xml/features + + + openhab-runtime-base + mvn:org.openhab.addons.bundles/org.openhab.binding.velux/${project.version} + + diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/VeluxBindingConstants.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/VeluxBindingConstants.java new file mode 100644 index 0000000000000..938b5a8af1c5a --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/VeluxBindingConstants.java @@ -0,0 +1,144 @@ +/** + * Copyright (c) 2010-2019 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.velux; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * The {@link VeluxBindingConstants} class defines common constants, which are + * used across the whole binding. + *

+ * For an in-depth view of the available Item types with description of parameters, take a look onto + * {@link org.openhab.binding.velux.internal.VeluxItemType VeluxItemType}. + *

+ * This class contains the Thing identifications: + *
    + *
  • {@link #THING_VELUX_BRIDGE} for the bridge itself,
  • + *
  • {@link #THING_VELUX_SCENE} for the scenes summarizing a set of actuator states,
  • + *
  • {@link #THING_VELUX_ACTUATOR} for the general actuators identified on the bridge,
  • + *
  • {@link #THING_VELUX_ROLLERSHUTTER} for the rollershutters identified on the bridge,
  • + *
  • {@link #THING_VELUX_WINDOW} for the windows identified on the bridge.
  • + *
  • {@link #THING_VELUX_VSHUTTER} for the virtual shutters defined in the configuration
  • + *
+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBindingConstants { + + /** Basic binding identification. */ + public static final String BINDING_ID = "velux"; + + // Id of support bridge + /** + * The Thing identification of the binding. + */ + private static final String THING_VELUX_BINDING = "binding"; + /** + * The Thing identification of the Velux bridge. + */ + private static final String THING_VELUX_BRIDGE = "klf200"; + /** + * The Thing identification of a scene defined on the Velux bridge. + */ + private static final String THING_VELUX_SCENE = "scene"; + /** + * The Thing identification of a generic actuator defined on the Velux bridge. + */ + private static final String THING_VELUX_ACTUATOR = "actuator"; + /** + * The Thing identification of a rollershutter defined on the Velux bridge. + */ + private static final String THING_VELUX_ROLLERSHUTTER = "rollershutter"; + /** + * The Thing identification of a window defined on the Velux bridge. + */ + private static final String THING_VELUX_WINDOW = "window"; + /** + * The Thing identification of a virtual shutter defined on the Velux bridge. + */ + private static final String THING_VELUX_VSHUTTER = "vshutter"; + + // List of all Thing Type UIDs + public static final ThingTypeUID THING_TYPE_BINDING = new ThingTypeUID(BINDING_ID, THING_VELUX_BINDING); + + // List of all Bridge Type UIDs + public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, THING_VELUX_BRIDGE); + + // List of all Thing Type UIDs beyond the bridge(s) + public static final ThingTypeUID THING_TYPE_VELUX_SCENE = new ThingTypeUID(BINDING_ID, THING_VELUX_SCENE); + public static final ThingTypeUID THING_TYPE_VELUX_ACTUATOR = new ThingTypeUID(BINDING_ID, THING_VELUX_ACTUATOR); + public static final ThingTypeUID THING_TYPE_VELUX_ROLLERSHUTTER = new ThingTypeUID(BINDING_ID, + THING_VELUX_ROLLERSHUTTER); + public static final ThingTypeUID THING_TYPE_VELUX_WINDOW = new ThingTypeUID(BINDING_ID, THING_VELUX_WINDOW); + public static final ThingTypeUID THING_TYPE_VELUX_VSHUTTER = new ThingTypeUID(BINDING_ID, THING_VELUX_VSHUTTER); + + // Definitions of different set of Things + public static final Set SUPPORTED_THINGS_BINDING = new HashSet<>(Arrays.asList(THING_TYPE_BINDING)); + public static final Set SUPPORTED_THINGS_BRIDGE = new HashSet<>(Arrays.asList(THING_TYPE_BRIDGE)); + public static final Set SUPPORTED_THINGS_ITEMS = new HashSet<>( + Arrays.asList(THING_TYPE_VELUX_SCENE, THING_TYPE_VELUX_ACTUATOR, THING_TYPE_VELUX_ROLLERSHUTTER, + THING_TYPE_VELUX_WINDOW, THING_TYPE_VELUX_VSHUTTER)); + + // *** List of all Channel ids *** + + // List of all binding channel ids + + /** Channel identifier describing the current Binding State. */ + public static final String CHANNEL_BINDING_INFORMATION = "information"; + + // List of all bridge channel ids + + /** Channel identifier describing the current Bridge State. */ + public static final String CHANNEL_BRIDGE_STATUS = "status"; + public static final String CHANNEL_BRIDGE_RELOAD = "reload"; + public static final String CHANNEL_BRIDGE_TIMESTAMP = "timestamp"; + public static final String CHANNEL_BRIDGE_IOTIMESTAMP = "ioTimestamp"; + + public static final String CHANNEL_BRIDGE_FIRMWARE = "firmware"; + public static final String CHANNEL_BRIDGE_IPADDRESS = "ipAddress"; + public static final String CHANNEL_BRIDGE_SUBNETMASK = "subnetMask"; + public static final String CHANNEL_BRIDGE_DEFAULTGW = "defaultGW"; + public static final String CHANNEL_BRIDGE_DHCP = "DHCP"; + public static final String CHANNEL_BRIDGE_WLANSSID = "WLANSSID"; + public static final String CHANNEL_BRIDGE_WLANPASSWORD = "WLANPassword"; + + public static final String CHANNEL_BRIDGE_DO_DETECTION = "doDetection"; + public static final String CHANNEL_BRIDGE_CHECK = "check"; + public static final String CHANNEL_BRIDGE_PRODUCTS = "products"; + public static final String CHANNEL_BRIDGE_SCENES = "scenes"; + + // List of all scene channel ids + public static final String CHANNEL_SCENE_ACTION = "action"; + public static final String CHANNEL_SCENE_SILENTMODE = "silentMode"; + + // List of all actuator channel ids + public static final String CHANNEL_ACTUATOR_POSITION = "position"; + public static final String CHANNEL_ACTUATOR_STATE = "state"; + public static final String CHANNEL_ACTUATOR_SILENTMODE = "silentMode"; + public static final String CHANNEL_ACTUATOR_LIMITATION = "limitation"; + + // List of all virtual shutter channel ids + public static final String CHANNEL_VSHUTTER_POSITION = "vposition"; + + // Helper definitions + public static final String BINDING_VALUES_SEPARATOR = ","; + public static final String OUTPUT_VALUE_SEPARATOR = ","; + public static final String UNKNOWN = "unknown"; + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/VeluxBindingProperties.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/VeluxBindingProperties.java new file mode 100644 index 0000000000000..c146807355340 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/VeluxBindingProperties.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2010-2019 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.velux; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link VeluxBindingProperties} class defines common constants, which are + * used within the property definitions. + * + * This class contains the property identifications: + *
    + *
  • {@link #PROPERTY_BINDING_BUNDLEVERSION} for identification of the binding, + *
+ *
    + *
  • {@link #PROPERTY_SCENE_NAME} for defining the name of a scene, + *
+ *
    + *
  • {@link #PROPERTY_ACTUATOR_SERIALNUMBER} for defining the serial number of an actuator, a rollershutter and a + * window, + *
  • {@link #PROPERTY_ACTUATOR_NAME} for defining the name of an actuator, a rollershutter and a window, + *
  • {@link #PROPERTY_ACTUATOR_INVERTED} for modifying the value of a Channel, + *
+ *
    + *
  • {@link #PROPERTY_VSHUTTER_SCENELEVELS} for defining a virtual shutter. + *
  • {@link #PROPERTY_VSHUTTER_CURRENTLEVEL} for defining a virtual shutter. + *
+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBindingProperties { + + public static final String PROPERTY_BINDING_BUNDLEVERSION = "bundleVersion"; + + public static final String PROPERTY_SCENE_NAME = "sceneName"; + public static final String PROPERTY_SCENE_VELOCITY = "velocity"; + + public static final String PROPERTY_ACTUATOR_SERIALNUMBER = "serial"; + public static final String PROPERTY_ACTUATOR_NAME = "name"; + public static final String PROPERTY_ACTUATOR_INVERTED = "inverted"; + + public static final String PROPERTY_VSHUTTER_SCENELEVELS = "sceneLevels"; + public static final String PROPERTY_VSHUTTER_CURRENTLEVEL = "currentLevel"; + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridge.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridge.java new file mode 100644 index 0000000000000..5cc53904f76c4 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridge.java @@ -0,0 +1,284 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge; + +import java.util.Collections; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.BridgeAPI; +import org.openhab.binding.velux.bridge.common.BridgeCommunicationProtocol; +import org.openhab.binding.velux.bridge.common.Login; +import org.openhab.binding.velux.bridge.common.Logout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 2nd Level I/O interface towards the Velux bridge. + * It provides methods for pre- and post-communication + * as well as a common method for the real communication. + * The following class access methods exist: + *
    + *
  • {@link VeluxBridge#bridgeLogin} for pre-communication,
  • + *
  • {@link VeluxBridge#bridgeLogout} for post-communication,
  • + *
  • {@link VeluxBridge#bridgeCommunicate} as method for the common communication.
  • + *
+ *

+ * Each protocol-specific implementation provides a publicly visible + * set of supported protocols as variable {@link #supportedProtocols}. + * As root of several inheritance levels it predefines an + * interfacing method {@link VeluxBridge#bridgeAPI} which + * has to be implemented by any kind of protocol-specific + * communication returning the appropriate base (1st) level + * communication method as well as any other gateway + * interaction with {@link #bridgeDirectCommunicate}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class VeluxBridge { + private final Logger logger = LoggerFactory.getLogger(VeluxBridge.class); + + /* + * *************************** + * ***** Private Objects ***** + */ + + private final String emptyAuthenticationToken = ""; + + // Type definitions, variables + + /** + * Support protocols for the concrete implementation. + *

+ * For protocol-specific implementations this value has to be adapted along the inheritance i.e. + * with the protocol-specific class values. + */ + public Set supportedProtocols = Collections.emptySet(); + + /** BridgeCommunicationProtocol authentication token for Velux Bridge. */ + protected String authenticationToken = emptyAuthenticationToken; + + /** + * Handler to access global bridge instance methods + * + */ + protected VeluxBridgeInstance bridgeInstance; + + /* + * ************************ + * ***** Constructors ***** + */ + + /** + * Constructor. + *

+ * Initializes the binding-wide instance for dealing with common informations and + * the Velux bridge connectivity settings by preparing the configuration settings with help + * by VeluxBridgeConfiguration. + * + * @param bridgeInstance refers to the binding-wide instance for dealing for common informations + * like existing actuators and predefined scenes. + */ + public VeluxBridge(VeluxBridgeInstance bridgeInstance) { + logger.trace("VeluxBridge(constructor,bridgeInstance={}) called.", bridgeInstance); + this.bridgeInstance = bridgeInstance; + logger.trace("VeluxBridge(constructor) done."); + } + + // Destructor methods + + /** + * Destructor. + *

+ * Deinitializes the binding-wide instance. + * + */ + public void shutdown() { + logger.trace("shutdown() called."); + } + + // Class access methods + + /** + * Determines whether the binding is already authenticated against the bridge so that + * any other communication can occur without an additional care about authentication. + *

+ * This method automatically decides on availability of the stored authentication + * information {@link VeluxBridge#authenticationToken} whether a (re-)authentication is possible. + * + * @return true if the bridge is authenticated; false otherwise. + */ + private boolean isAuthenticated() { + boolean success = (authenticationToken.length() > 0); + logger.trace("isAuthenticated() returns {}.", success); + return success; + } + + /** + * Declares the binding as unauthenticated against the bridge so that the next + * communication will take care about (re-)authentication. + */ + protected void resetAuthentication() { + logger.trace("resetAuthentication() called."); + authenticationToken = emptyAuthenticationToken; + return; + } + + /** + * Prepare an authorization request and communicate it with the Velux veluxBridge. + * If login is successful, the returned authorization token will be stored within this class + * for any further communication via {@link#bridgeCommunicate} up + * to an authorization with method {@link VeluxBridge#bridgeLogout}. + * + * @return true if the login was successful, and false otherwise. + */ + public synchronized boolean bridgeLogin() { + logger.trace("bridgeLogin() called."); + + Login bcp = bridgeAPI().login(); + bcp.setPassword(bridgeInstance.veluxBridgeConfiguration().password); + if (bridgeCommunicate(bcp, false)) { + logger.trace("bridgeLogin(): communication succeeded."); + if (bcp.isCommunicationSuccessful()) { + logger.trace("bridgeLogin(): storing authentication token for further access."); + authenticationToken = bcp.getAuthToken(); + return true; + } + } + return false; + } + + /** + * Prepare an authenticated deauthorization request and communicate it with the Velux veluxBridge. + * The authorization token stored in this class will be destroyed, so that the + * next communication has to start with {@link VeluxBridge#bridgeLogin}. + * + * @return true if the logout was successful, and false otherwise. + */ + public synchronized boolean bridgeLogout() { + logger.trace("bridgeLogout() called: emptying authentication token."); + authenticationToken = ""; + + Logout bcp = bridgeAPI().logout(); + if (bridgeCommunicate(bcp, false)) { + logger.trace("bridgeLogout(): communication succeeded."); + if (bcp.isCommunicationSuccessful()) { + logger.trace("bridgeLogout(): logout successful."); + return true; + } + } + return false; + } + + /** + * Initializes a client/server communication towards Velux veluxBridge + * based on the Basic I/O interface {@link VeluxBridge} and parameters + * passed as arguments (see below) and provided by VeluxBridgeConfiguration. + * + * @param communication the intended communication, + * that is request and response interactions as well as appropriate URL definition. + * @param useAuthentication whether to use authenticated communication. + * @return true if communication was successful, and false otherwise. + */ + private synchronized boolean bridgeCommunicate(BridgeCommunicationProtocol communication, + boolean useAuthentication) { + logger.trace("bridgeCommunicate({},{}authenticated) called.", communication.name(), + useAuthentication ? "" : "un"); + + if (!isAuthenticated()) { + if (useAuthentication) { + logger.trace("bridgeCommunicate(): no auth token available, aborting."); + return false; + } else { + logger.trace("bridgeCommunicate(): no auth token available, continuing."); + } + } + return bridgeDirectCommunicate(communication, useAuthentication); + } + + /** + * Initializes a client/server communication towards Velux Bridge + * based on the Basic I/O interface {@link VeluxBridge} and parameters + * passed as arguments (see below) and provided by VeluxBridgeConfiguration. + * This method automatically decides to invoke a login communication before the + * intended request if there has not been an authentication before. + * + * @param communication the intended communication, that is request and response interactions as well as appropriate + * URL definition. + * @return true if communication was successful, and false otherwise. + */ + public synchronized boolean bridgeCommunicate(BridgeCommunicationProtocol communication) { + logger.trace("bridgeCommunicate({}) called.", communication.name()); + if (!isAuthenticated()) { + bridgeLogin(); + } + return bridgeCommunicate(communication, true); + } + + /** + * Returns the timestamp in milliseconds since Unix epoch + * of last communication. + *

+ * If possible, it should be overwritten by protocol specific implementation. + *

+ * + * @return timestamp (default zero). + */ + public synchronized long lastCommunication() { + logger.trace("lastCommunication() returns zero."); + return 0L; + } + + /** + * Returns the timestamp in milliseconds since Unix epoch + * of last successful communication. + *

+ * If possible, it should be overwritten by protocol specific implementation. + *

+ * + * @return timestamp (default zero). + */ + public synchronized long lastSuccessfulCommunication() { + logger.trace("lastSuccessfulCommunication() returns zero."); + return 0L; + } + + /** + * Provides information about the base-level communication method and + * any kind of available gateway interaction. + *

+ * For protocol-specific implementations this method has to be overwritten along the inheritance i.e. + * with the protocol-specific class implementations. + * + * @return bridgeAPI of type {@link org.openhab.binding.velux.bridge.common.BridgeAPI BridgeAPI}. + */ + public abstract BridgeAPI bridgeAPI(); + + /** + * Initializes a client/server communication towards Velux veluxBridge + * based on the protocol-specific implementations with common parameters + * passed as arguments (see below) and provided by VeluxBridgeConfiguration. + *

+ * For protocol-specific implementations this method has to be overwritten along the inheritance i.e. + * with the protocol-specific class implementations. + * + * @param communication Structure of interface type {@link BridgeCommunicationProtocol} describing the + * intended communication. + * @param useAuthentication boolean flag to decide whether to use authenticated communication. + * @return success of type boolean which signals the success of the communication. + */ + protected abstract boolean bridgeDirectCommunicate(BridgeCommunicationProtocol communication, + boolean useAuthentication); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeActuators.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeActuators.java new file mode 100644 index 0000000000000..ea9519d2b68f7 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeActuators.java @@ -0,0 +1,175 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.GetProduct; +import org.openhab.binding.velux.bridge.common.GetProducts; +import org.openhab.binding.velux.things.VeluxExistingProducts; +import org.openhab.binding.velux.things.VeluxKLFAPI; +import org.openhab.binding.velux.things.VeluxProduct; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeActuators} represents a complete set of transactions + * for retrieving of any available products into a structure {@link #channel} + * defined on the Velux bridge. + *

+ * It provides the methods: + *

    + *
  • {@link #getProducts} for retrieval of information from the bridge. + *
  • {@link #getChannel} for accessing the retrieved information. + *
  • {@link #autoRefresh} for retrieval of information for supporting the update the corresponding openHAB items. + *
+ * + * @see VeluxProduct + * @see VeluxExistingProducts + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeActuators { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeActuators.class); + + // Configuration constants + + /** + * Limitation of Discovery on parts of the System table + * + * Whereas the parameter {@link org.openhab.binding.velux.things.VeluxKLFAPI#KLF_SYSTEMTABLE_MAX} represents the + * maximum size of the system table in general, for speed up of the discovery process + * a binding-internal limitation of number of possible devices is introduced. + */ + private static final int VELUXBINDING_SYSTEMTABLE_MAX = 16; + + // Type definitions, class-internal variables + + /** + * Actuator information consisting of: + *
    + *
  • isRetrieved (boolean), + *
  • existingProducts ({@link VeluxExistingProducts}). + *
+ */ + public class Channel { // NO_UCD (use private) + public VeluxExistingProducts existingProducts = new VeluxExistingProducts(); + } + + private Channel channel; + + // Constructor methods + + /** + * Constructor. + *

+ * Initializes the internal data structure {@link #channel} of Velux actuators/products, + * which is publicly accessible via the method {@link #getChannel()}. + */ + public VeluxBridgeActuators() { + logger.trace("VeluxBridgeActuators(constructor) called."); + channel = new Channel(); + logger.trace("VeluxBridgeActuators(constructor) done."); + } + + // Class access methods + + /** + * Provide access to the internal structure of actuators/products. + * + * @return a channel describing the overall actuator situation. + */ + public Channel getChannel() { + return channel; + } + + /** + * Login into bridge, retrieve all products and logout from bridge based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. The results + * are stored within {@link org.openhab.binding.velux.things.VeluxExistingProducts + * VeluxExistingProducts}. + * + * @param bridge Initialized Velux bridge (communication) handler. + * @return true if successful, and false otherwise. + */ + @SuppressWarnings("null") + public boolean getProducts(VeluxBridge bridge) { + logger.trace("getProducts() called."); + + GetProducts bcp = bridge.bridgeAPI().getProducts(); + if (!bridge.bridgeInstance.veluxBridgeConfiguration().isBulkRetrievalEnabled || (bcp == null)) { + logger.trace("getProducts() working on step-by-step retrieval."); + GetProduct bcpSbS = bridge.bridgeAPI().getProduct(); + for (int nodeId = 0; nodeId < VeluxKLFAPI.KLF_SYSTEMTABLE_MAX + && nodeId < VELUXBINDING_SYSTEMTABLE_MAX; nodeId++) { + logger.trace("getProducts() {}.", new String(new char[80]).replace('\0', '*')); + try { + Thread.sleep(2000); + } catch (InterruptedException ie) { + logger.trace("io() wait interrupted."); + } + logger.trace("getProducts() working on product number {}.", nodeId); + bcpSbS.setProductId(nodeId); + if ((bridge.bridgeCommunicate(bcpSbS)) && (bcpSbS.isCommunicationSuccessful())) { + VeluxProduct veluxProduct = bcpSbS.getProduct(); + if (bcpSbS.isCommunicationSuccessful()) { + logger.debug("getProducts() found product {}.", veluxProduct); + if (!channel.existingProducts.isRegistered(veluxProduct)) { + channel.existingProducts.register(veluxProduct); + } + } + } + } + logger.debug("getProducts() finally has found products {}.", channel.existingProducts); + return true; + } else { + logger.trace("getProducts() working on bulk retrieval."); + if ((bridge.bridgeCommunicate(bcp)) && (bcp.isCommunicationSuccessful())) { + for (VeluxProduct product : bcp.getProducts()) { + logger.trace("getProducts() found product {} (type {}).", product.getProductName(), + product.getProductType()); + if (!channel.existingProducts.isRegistered(product)) { + logger.debug("getProducts() storing new product {}.", product); + channel.existingProducts.register(product); + } else { + logger.debug("getProducts() storing updates for product {}.", product); + channel.existingProducts.update(product); + } + } + logger.debug("getProducts() finally has found products {}.", channel.existingProducts); + return true; + } else { + logger.trace("getProducts() finished with failure."); + return false; + } + } + } + + /** + * In case of an empty list of recognized products, the method will + * initiate a product retrieval using {@link #getProducts(VeluxBridge)}. + * + * @param bridge Initialized Velux bridge (communication) handler. + * @return true if at least one product was found, and false otherwise. + */ + public boolean autoRefresh(VeluxBridge bridge) { + logger.trace("autoRefresh() called."); + if (channel.existingProducts.getNoMembers() == 0) { + logger.trace("autoRefresh(): is about to fetch existing products."); + getProducts(bridge); + } + return (channel.existingProducts.getNoMembers() > 0); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeDetectProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeDetectProducts.java new file mode 100644 index 0000000000000..758c04f37f384 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeDetectProducts.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.RunProductDiscovery; +import org.openhab.binding.velux.bridge.common.GetDeviceStatus; +import org.openhab.binding.velux.things.VeluxGwState; +import org.openhab.binding.velux.things.VeluxGwState.VeluxGatewaySubState; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeDetectProducts} represents a complete set of transactions + * for temporary activation of device detection mode on the Velux bridge. + *

+ * It therefore provides a method: + *

    + *
  • {@link VeluxBridgeDetectProducts#detectProducts} for starting the detection. + *
+ * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeDetectProducts { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeDetectProducts.class); + + // Type definitions, class-internal variables + + private static long waitMSecs = 2000L; + + // Class access methods + + /** + * Login into bridge, start process to detect (new) products, loop until bridge is idle again and logout from bridge + * based on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @return success + * of type boolean describing the overall result of this interaction. + */ + public boolean detectProducts(VeluxBridge bridge) { + logger.trace("detectProducts() called."); + boolean success = false; + + logger.trace("detectProducts() About to activate detection."); + RunProductDiscovery bcp1 = bridge.bridgeAPI().runProductDiscovery(); + if (!(bridge.bridgeCommunicate(bcp1)) || (bcp1.isCommunicationSuccessful())) { + while (true) { + logger.trace("detectProducts() About to query detection status."); + GetDeviceStatus bcp = bridge.bridgeAPI().getDeviceStatus(); + if (!(bridge.bridgeCommunicate(bcp)) || (bcp.isCommunicationSuccessful())) { + logger.trace("detectProducts() finished with failure."); + break; + } + VeluxGwState deviceStatus = bcp.getState(); + if (deviceStatus.getSubState() == (byte) VeluxGatewaySubState.GW_SS_P1.getStateValue()) { + logger.trace("detectProducts() bridge is still busy."); + } else if (deviceStatus.getSubState() == (byte) VeluxGatewaySubState.GW_SS_IDLE.getStateValue()) { + logger.trace("detectProducts() bridge is idle again, now."); + success = true; + break; + } else { + logger.info("detectProducts() unknown devicestatus ({}) received.", deviceStatus); + } + logger.trace("detectProducts() about to wait {} msecs.", waitMSecs); + try { + Thread.sleep(waitMSecs); + } catch (InterruptedException ie) { + logger.trace("detectProducts() wait interrupted."); + } + } + } else { + logger.trace("detectProducts() activate detection finished with failure."); + } + + logger.debug("detectProducts() finished {}.", success ? "successfully" : "with failure"); + return success; + } +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeDeviceCheckLostNodes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeDeviceCheckLostNodes.java new file mode 100644 index 0000000000000..aee96df01c5b0 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeDeviceCheckLostNodes.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.RunProductSearch; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeDeviceCheckLostNodes} represents a complete set of transactions + * for querying device status on the Velux bridge. + *

+ * It therefore provides a method + *

    + *
  • {@link #initiate} for starting the detection. + *
+ * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeDeviceCheckLostNodes { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeDeviceCheckLostNodes.class); + + // Class access methods + + /** + * Login into bridge, query the bridge for device status and logout from bridge + * based on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + */ + public void initiate(VeluxBridge bridge) { + logger.trace("initiate() called."); + RunProductSearch bcp = bridge.bridgeAPI().runProductSearch(); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + logger.trace("initiate() finished successfully."); + } else { + logger.trace("initiate() finished with failure."); + } + return; + } +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeDeviceStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeDeviceStatus.java new file mode 100644 index 0000000000000..3d04630a15e87 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeDeviceStatus.java @@ -0,0 +1,108 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.StringType; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.bridge.common.GetDeviceStatus; +import org.openhab.binding.velux.things.VeluxGwState; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeDeviceStatus} represents a complete set of transactions + * for querying device status on the Velux bridge. + *

+ * It therefore provides a method + *

    + *
  • {@link #retrieve} for starting the detection. + *
  • {@link #getChannel} for accessing the retrieved information. + *
+ * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeDeviceStatus { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeDeviceStatus.class); + + // Type definitions, class-internal variables + + /** + * Bridge information consisting of: + *
    + *
  • {@link #isRetrieved} describing the retrieval state, + *
  • {@link #gwState} containing the brief gateway state, + *
  • {@link #gwStateDescription} containing the verbose gateway state. + *
+ */ + public class Channel { + public boolean isRetrieved = false; + public StringType gwState = new StringType(VeluxBindingConstants.UNKNOWN); + public StringType gwStateDescription = new StringType(VeluxBindingConstants.UNKNOWN); // NO_UCD (use private) + } + + private Channel channel; + + // Constructor methods + + /** + * Constructor. + *

+ * Initializes the internal data structure {@link #channel} of Velux actuators/products, + * which is publicly accessible via the method {@link #getChannel()}. + */ + public VeluxBridgeDeviceStatus() { + logger.trace("VeluxBridgeDeviceStatus(constructor) called."); + channel = new Channel(); + } + + // Class access methods + + /** + * Provide access to the internal structure of the device status. + * + * @return a channel describing the overall actual device status. + */ + public Channel getChannel() { + return channel; + } + + /** + * Complete workflow for retrieving the firmware version, consisting of Login into bridge, querying the firmware + * version and logout from bridge based on a well-prepared environment of a {@link VeluxBridgeProvider}, where the + * results are stored in {@link VeluxBridgeDeviceStatus#channel}. + * + * @param bridge Initialized Velux bridge handler. + * @return channel of type {@link VeluxBridgeDeviceStatus.Channel} describing the overall result of this + * interaction. + */ + public Channel retrieve(VeluxBridge bridge) { + logger.trace("retrieve() called. About to query device status."); + GetDeviceStatus bcp = bridge.bridgeAPI().getDeviceStatus(); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + VeluxGwState state = bcp.getState(); + channel.gwState = new StringType(state.toString()); + channel.gwStateDescription = new StringType(state.toDescription()); + channel.isRetrieved = true; + logger.trace("retrieve() finished successfully with result {}.", state.toDescription()); + } else { + channel.isRetrieved = false; + logger.trace("retrieve() finished with failure."); + } + return channel; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeGetFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeGetFirmware.java new file mode 100644 index 0000000000000..ca5b9c8b2dd74 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeGetFirmware.java @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.StringType; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.bridge.common.GetFirmware; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeGetFirmware} represents a complete set of transactions + * for retrieving of firmware version string on the Velux bridge. + *

+ * It provides the following methods: + *

    + *
  • {@link #retrieve} for retrieval of information. + *
  • {@link #getChannel} for accessing the retrieved information. + *
+ * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeGetFirmware { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeGetFirmware.class); + + // Type definitions, class-internal variables + + /** + * Bridge information consisting of: + *
    + *
  • isRetrieved (boolean flag), + *
  • firmwareVersion (human readable String). + *
+ */ + public class Channel { + public boolean isRetrieved = false; + public StringType firmwareVersion = new StringType(VeluxBindingConstants.UNKNOWN); + } + + private Channel channel; + + // Constructor methods + + /** + * Constructor. + *

+ * Initializes the internal data structure {@link #channel} of Velux firmware information, + * which is publicly accessible via the method {@link #getChannel()}. + */ + public VeluxBridgeGetFirmware() { + logger.trace("VeluxBridgeGetFirmware(constructor) called."); + channel = new Channel(); + } + + // Class access methods + + /** + * Provide access to the internal structure of actuators/products. + * + * @return {@link Channel} describing the overall actuator situation. + */ + public Channel getChannel() { + return channel; + } + + /** + * Complete workflow for retrieving the firmware version, consisting of Login into bridge, querying the firmware + * version and logout from bridge based on a well-prepared environment of a {@link VeluxBridgeProvider}, where the + * results are stored in {@link VeluxBridgeGetFirmware#channel}. + * + * @param bridge Initialized Velux bridge handler. + * @return channel of type {@link VeluxBridgeGetFirmware.Channel} describing the overall result of this + * interaction. + */ + public Channel retrieve(VeluxBridge bridge) { + logger.trace("retrieve() called."); + + GetFirmware bcp = bridge.bridgeAPI().getFirmware(); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + this.channel.firmwareVersion = new StringType(bcp.getFirmware().getfirmwareVersion()); + this.channel.isRetrieved = true; + logger.trace("retrieve() found successfully firmware {}.", this.channel.firmwareVersion); + } else { + this.channel.isRetrieved = false; + logger.trace("retrieve() finished with failure."); + } + return channel; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeGetHouseStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeGetHouseStatus.java new file mode 100644 index 0000000000000..6fc9fe7e3fe92 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeGetHouseStatus.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.GetHouseStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeGetHouseStatus} represents a complete set of transactions + * for receiving the current state by the House Status Monitor on the Velux bridge. + *

+ * The HSM is responsible for continuous updates towards the communication initiator + * about any changes of actuator states. + *

+ * It therefore provides a method {@link VeluxBridgeGetHouseStatus#evaluateState} for check of availability of House + * Monitoring Messages. + * Any parameters are controlled by {@link org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration}. + * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeGetHouseStatus { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeGetHouseStatus.class); + + // Class access methods + + /** + * Login into bridge, fetch the HSM state and logout from bridge based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @return true if successful or false otherwise. + */ + public boolean evaluateState(VeluxBridge bridge) { + logger.trace("evaluateState({}) called."); + + boolean success = false; + GetHouseStatus bcp = bridge.bridgeAPI().getHouseStatus(); + if (bcp != null) { + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + success = true; + } + } + logger.debug("evaluateState() finished {}.", (success ? "successfully" : "with failure")); + return success; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeGetLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeGetLimitation.java new file mode 100644 index 0000000000000..fdb97ec54bba7 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeGetLimitation.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.GetProductLimitation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeGetLimitation} represents a complete set of transactions + * for executing a scene defined on the Velux bridge. + *

+ * It provides a method {@link VeluxBridgeGetLimitation#getLimitation} for querying a limitation of an actuators. + * Any parameters are controlled by {@link org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration}. + * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeGetLimitation { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeGetLimitation.class); + + // Class access methods + + /** + * Login into bridge, instruct the bridge to pass a command towards an actuator based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @param nodeId Number of Actuator to be modified. + * @param limitationMinimum query minimum or maximum. + * @return true if successful, and false otherwise. + */ + public boolean getLimitation(VeluxBridge bridge, int nodeId, boolean limitationMinimum) { + logger.trace("getLimitation(nodeId={}) called.", nodeId); + + boolean success = false; + GetProductLimitation bcp = bridge.bridgeAPI().getProductLimitation(); + if (bcp != null) { + bcp.setActuatorIdAndLimitationType(nodeId, limitationMinimum); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + success = true; + } + } + logger.debug("getLimitation() finished {}.", (success ? "successfully" : "with failure")); + return success; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeInstance.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeInstance.java new file mode 100644 index 0000000000000..37ffcf342f6d0 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeInstance.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration; +import org.openhab.binding.velux.things.VeluxExistingProducts; +import org.openhab.binding.velux.things.VeluxExistingScenes; + +/** + * This interface is implemented by classes that deal with a specific Velux bridge and its configuration. + *

+ * Configuration + *

+ *
    + *
  • {@link org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration VeluxBridgeConfiguration} + * for specification by a specific Velux bridge,
  • + *
+ * + *

+ * Status + *

+ * Two methods for bridge-internal configuration retrieval: + *
    + *
  • {@link #existingProducts} + * for retrieving scene information,
  • + *
  • {@link #existingScenes} + * for retrieving product information.
  • + *
+ * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public interface VeluxBridgeInstance { + + /** + * Bridge configuration + * + * @return VeluxBridgeConfiguration containing all bridge configuration settings. + */ + public VeluxBridgeConfiguration veluxBridgeConfiguration(); + + /** + * Information retrieved by {@link org.openhab.binding.velux.bridge.VeluxBridgeActuators#getProducts} + * + * @return VeluxExistingProducts containing all registered products, or null in case of any error. + */ + public VeluxExistingProducts existingProducts(); + + /** + * Information retrieved by {@link org.openhab.binding.velux.bridge.VeluxBridgeScenes#getScenes} + * + * @return VeluxExistingScenes containing all registered scenes, or null in case of any error. + */ + public VeluxExistingScenes existingScenes(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeLANConfig.java new file mode 100644 index 0000000000000..155cf3cdcaefc --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeLANConfig.java @@ -0,0 +1,114 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.bridge.common.GetLANConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeLANConfig} represents a complete set of transactions + * for retrieving the network configuration of the Velux bridge. + *

+ * It provides the following methods: + *

    + *
  • {@link #retrieve} for retrieval of information. + *
  • {@link #getChannel} for accessing the retrieved information. + *
+ * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeLANConfig { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeLANConfig.class); + + // Type definitions, class-internal variables + + /** + * IP Network configuration, consisting of: + *
    + *
  • isRetrieved (boolean flag), + *
  • ipAddress, + *
  • subnetMask, + *
  • defaultGW and + *
  • enabledDHCP. + *
+ */ + public class Channel { + public boolean isRetrieved = false; + public StringType openHABipAddress = new StringType(VeluxBindingConstants.UNKNOWN); + public StringType openHABsubnetMask = new StringType(VeluxBindingConstants.UNKNOWN); + public StringType openHABdefaultGW = new StringType(VeluxBindingConstants.UNKNOWN); + public OnOffType openHABenabledDHCP = OnOffType.OFF; + } + + private Channel channel; + + // Constructor methods + + /** + * Constructor. + *

+ * Initializes the internal data structure {@link #channel} of Velux LAN information, + * which is publicly accessible via the method {@link #getChannel()}. + */ + public VeluxBridgeLANConfig() { + logger.trace("VeluxBridgeLANConfig(constructor) called."); + channel = new Channel(); + } + + // Class access methods + + /** + * Provide access to the internal structure of LAN information. + * + * @return a channel describing the overall actual LAN information. + */ + public Channel getChannel() { + return channel; + } + + /** + * Complete workflow for retrieving the network configuration, consisting of Login into bridge, querying + * the network configuration and logout from bridge based on a well-prepared environment of a + * {@link VeluxBridgeProvider}, where the results are stored within as well in + * {@link VeluxBridgeLANConfig#channel}. + * + * @param bridge Initialized Velux bridge handler. + * @return channel of type {@link VeluxBridgeLANConfig.Channel} describing the overall result of this + * interaction. + */ + public Channel retrieve(VeluxBridge bridge) { + logger.trace("retrieve() called."); + + GetLANConfig bcp = bridge.bridgeAPI().getLANConfig(); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + logger.trace("retrieve() found successfully configuration {}.", bcp.getLANConfig()); + channel.openHABipAddress = new StringType(bcp.getLANConfig().getIpAddress()); + channel.openHABsubnetMask = new StringType(bcp.getLANConfig().getSubnetMask()); + channel.openHABdefaultGW = new StringType(bcp.getLANConfig().getDefaultGW()); + channel.openHABenabledDHCP = bcp.getLANConfig().getDHCP() ? OnOffType.ON : OnOffType.OFF; + channel.isRetrieved = true; + } else { + channel.isRetrieved = false; + logger.trace("retrieve() finished with failure."); + } + return channel; + } +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeProvider.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeProvider.java new file mode 100644 index 0000000000000..6b077af298184 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeProvider.java @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.bridge.common.BridgeAPI; +import org.openhab.binding.velux.bridge.common.BridgeCommunicationProtocol; + +/** + * This interface is implemented by classes that provide general communication with the Velux bridge. + *

+ * Communication + *

+ *
    + *
  • {@link VeluxBridgeProvider#bridgeCommunicate} for generic communication,
  • + *
  • {@link VeluxBridgeProvider#bridgeAPI} for access to all interaction/communication methods.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public interface VeluxBridgeProvider { + + /** + * Initializes a client/server communication towards Velux Bridge + * based on the Basic I/O interface {@link VeluxBridge} and parameters + * passed as arguments (see below) and provided by + * {@link org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration}. + * This method automatically decides to invoke a login communication before the + * intended request if there has not been an authentication before. + * + * @param communication {@link BridgeCommunicationProtocol} describing the intended + * communication, that is request and response interactions as well as appropriate URL + * definition. + * @return true if communication was successful, and false otherwise. + */ + + public boolean bridgeCommunicate(BridgeCommunicationProtocol communication); + + /** + * Returns the class {@link BridgeAPI} which summarizes all interfacing methods. + * + * @return BridgeAPI + * containing all API methods. + */ + public @Nullable BridgeAPI bridgeAPI(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeRunProductCommand.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeRunProductCommand.java new file mode 100644 index 0000000000000..7ff002512f1c5 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeRunProductCommand.java @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.RunProductCommand; +import org.openhab.binding.velux.things.VeluxProductPosition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeRunProductCommand} represents a complete set of transactions + * for executing a scene defined on the Velux bridge. + *

+ * It provides a method {@link VeluxBridgeRunProductCommand#sendCommand} for sending a parameter change command. + * Any parameters are controlled by {@link org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration}. + * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeRunProductCommand { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeRunProductCommand.class); + + // Class access methods + + /** + * Login into bridge, instruct the bridge to pass a command towards an actuator based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @param nodeId Number of Actuator to be modified. + * @param value Target value for Actuator main parameter. + * @return true if successful, and false otherwise. + */ + public boolean sendCommand(VeluxBridge bridge, int nodeId, VeluxProductPosition value) { + logger.trace("sendCommand(nodeId={},value={}) called.", nodeId, value); + + boolean success = false; + RunProductCommand bcp = bridge.bridgeAPI().runProductCommand(); + if (bcp != null) { + int veluxValue = value.getPositionAsVeluxType(); + + bcp.setNodeAndMainParameter(nodeId, veluxValue); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + success = true; + } + } + logger.debug("sendCommand() finished {}.", (success ? "successfully" : "with failure")); + return success; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeRunScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeRunScene.java new file mode 100644 index 0000000000000..0c7511b32d94c --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeRunScene.java @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.RunScene; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeRunScene} represents a complete set of transactions + * for executing a scene defined on the Velux bridge. + *

+ * It provides a method {@link VeluxBridgeRunScene#execute} for execution of a scene. + * Any parameters are controlled by {@link org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration}. + * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeRunScene { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeRunScene.class); + + /** + * Login into bridge, execute a scene and logout from bridge based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @param sceneNo Number of scene to be executed. + * @return true if successful, and false otherwise. + */ + public boolean execute(VeluxBridge bridge, int sceneNo) { + logger.trace("execute({}) called.", sceneNo); + + RunScene bcp = bridge.bridgeAPI().runScene().setSceneId(sceneNo); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + logger.debug("execute() finished successfully."); + return true; + } else { + logger.trace("execute() finished with failure."); + return false; + } + } + + /** + * Login into bridge, execute a scene and logout from bridge based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @param sceneNo Number of scene to be executed. + * @param velocity integer representing the velocity. + * @return true if successful, and false otherwise. + */ + public boolean execute(VeluxBridge bridge, int sceneNo, int velocity) { + logger.trace("execute({},{}) called.", sceneNo, velocity); + + RunScene bcp = bridge.bridgeAPI().runScene().setVelocity(velocity).setSceneId(sceneNo); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + logger.debug("execute() finished successfully."); + return true; + } else { + logger.trace("execute() finished with failure."); + return false; + } + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeScenes.java new file mode 100644 index 0000000000000..73d689cda1423 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeScenes.java @@ -0,0 +1,130 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.GetScenes; +import org.openhab.binding.velux.things.VeluxExistingScenes; +import org.openhab.binding.velux.things.VeluxScene; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeScenes} represents a complete set of transactions + * for retrieving of any available scenes into a structure {@link VeluxExistingScenes} + * defined on the Velux bridge. + *

+ * It provides the following methods: + *

    + *
  • {@link #getScenes} for retrieval of information. + *
  • {@link #getChannel} for accessing the retrieved information. + *
  • {@link #autoRefresh} for retrieval of information in case of an + * empty list of actuators. + *
+ * + * @see VeluxScene + * @see VeluxExistingScenes + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeScenes { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeScenes.class); + + // Type definitions, class-internal variables + + /** + * Actuator information consisting of: + *
    + *
  • existingScenes ({@link VeluxExistingScenes}). + *
+ */ + public class Channel { + public VeluxExistingScenes existingScenes = new VeluxExistingScenes(); + } + + private Channel channel; + + // Constructor methods + + /** + * Constructor. + *

+ * Initializes the internal data structure {@link #channel} of Velux scenes, + * which is publicly accessible via the method {@link #getChannel()}. + */ + public VeluxBridgeScenes() { + logger.trace("VeluxBridgeScenes(constructor) called."); + channel = new Channel(); + } + + // Class access methods + + /** + * Provide access to the internal structure of scenes. + * + * @return a channel describing the overall scenes situation. + */ + public Channel getChannel() { + return channel; + } + + /** + * Login into bridge, retrieve all scenes and logout from bridge based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. The results + * are stored within a public structure {@link org.openhab.binding.velux.things.VeluxExistingScenes + * VeluxExistingScenes}. + * + * @param bridge Initialized Velux bridge (communication) handler. + * @return true if successful, or false otherwise. + */ + public boolean getScenes(VeluxBridge bridge) { + logger.trace("getScenes() called."); + + GetScenes bcp = bridge.bridgeAPI().getScenes(); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + for (VeluxScene scene : bcp.getScenes()) { + logger.trace("getScenes() found scene {}.", scene.toString()); + + VeluxScene veluxScene = new VeluxScene(scene); + logger.trace("getScenes() storing scene {}.", veluxScene); + if (!channel.existingScenes.isRegistered(veluxScene)) { + channel.existingScenes.register(veluxScene); + } + logger.trace("getScenes() stored scene {}.", veluxScene); + } + logger.debug("getScenes() finally has found scenes {}.", channel.existingScenes); + return true; + } else { + logger.trace("getScenes() finished with failure."); + return false; + } + } + + /** + * In case of an empty list of recognized scenes, the method will + * initiate a product retrieval using {@link #getScenes(VeluxBridge)}. + * + * @param bridge Initialized Velux bridge (communication) handler. + * @return true if at lease one scene was found, and false otherwise. + */ + public boolean autoRefresh(VeluxBridge bridge) { + if (channel.existingScenes.getNoMembers() == 0) { + logger.trace("autoRefresh(): is about to fetch existing scenes."); + getScenes(bridge); + } + return (channel.existingScenes.getNoMembers() > 0); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeSetHouseStatusMonitor.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeSetHouseStatusMonitor.java new file mode 100644 index 0000000000000..ad9b77cabb048 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeSetHouseStatusMonitor.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.SetHouseStatusMonitor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeSetHouseStatusMonitor} represents a complete set of transactions + * for modifying the service state of the House Status Monitor on the Velux bridge. + *

+ * The HSM is responsible for continuous updates towards the communication initiator + * about any changes of actuator states. + *

+ * It therefore provides a method {@link VeluxBridgeSetHouseStatusMonitor#modifyHSM} for modifying the HSM settings. + * Any parameters are controlled by {@link org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration}. + * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeSetHouseStatusMonitor { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeSetHouseStatusMonitor.class); + + // Class access methods + + /** + * Login into bridge, modify HSM and logout from bridge based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @param enableService Flag whether the HSM should be activated. + * @return true if successful or false otherwise. + */ + public boolean modifyHSM(VeluxBridge bridge, boolean enableService) { + logger.trace("modifyHSM({}) called.", enableService); + + boolean success = false; + SetHouseStatusMonitor bcp = bridge.bridgeAPI().setHouseStatusMonitor(); + if (bcp != null) { + bcp.serviceActivation(enableService); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + success = true; + } + } + logger.debug("modifyHSM() finished {}.", (success ? "successfully" : "with failure")); + return success; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeSetSceneVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeSetSceneVelocity.java new file mode 100644 index 0000000000000..6cd5f344e366e --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeSetSceneVelocity.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.SetSceneVelocity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeSetSceneVelocity} represents a complete set of transactions + * for modifying the silent-mode of a scene defined on the Velux bridge. + *

+ * It therefore provides a method + *

    + *
  • {@link VeluxBridgeSetSceneVelocity#setSilentMode} for modifying the behaviour of a scene. + *
+ * Any parameters are controlled by {@link org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration}. + * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeSetSceneVelocity { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeSetSceneVelocity.class); + + // Class access methods + + /** + * Login into bridge, modify the scene parameters and logout from bridge based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @param sceneNo Number of scene to be modified. + * @param silentMode Mode of this mentioned scene. + * @return true if successful, and false otherwise. + */ + public boolean setSilentMode(VeluxBridge bridge, int sceneNo, boolean silentMode) { + logger.trace("setSilentMode({},{}) called.", sceneNo, silentMode); + + SetSceneVelocity bcp = bridge.bridgeAPI().setSceneVelocity(); + bcp.setMode(sceneNo, silentMode); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + logger.info("setSilentMode() finished successfully."); + return true; + } + logger.trace("setSilentMode() finished with failure."); + return false; + } +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeWLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeWLANConfig.java new file mode 100644 index 0000000000000..cd03557f0fec3 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeWLANConfig.java @@ -0,0 +1,108 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.StringType; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.bridge.common.GetWLANConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeWLANConfig} represents a complete set of transactions + * for retrieving the wireless network configuration of the Velux bridge. + *

+ * It provides the following methods: + *

    + *
  • {@link #retrieve} for retrieval of information. + *
  • {@link #getChannel} for accessing the retrieved information. + *
+ *

+ * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeWLANConfig { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeWLANConfig.class); + + // Type definitions, class-internal variables + + /** + * Wireless network configuration, consisting of: + *

    + *
  • isRetrieved, + *
  • wlanSSID, + *
  • wlanPassword. + *
+ */ + public class Channel { + public boolean isRetrieved = false; + public StringType openHABwlanSSID = new StringType(VeluxBindingConstants.UNKNOWN); + public StringType openHABwlanPassword = new StringType(VeluxBindingConstants.UNKNOWN); + } + + private Channel channel; + + // Constructor methods + + /** + * Constructor. + *

+ * Initializes the internal data structure {@link #channel} of Velux WLAN information, + * which is publicly accessible via the method {@link #getChannel()}. + */ + public VeluxBridgeWLANConfig() { + logger.trace("VeluxBridgeWLANConfig(constructor) called."); + channel = new Channel(); + } + + // Class access methods + + /** + * Provide access to the internal structure of WLAN information. + * + * @return a channel describing the overall WLAN situation. + */ + public Channel getChannel() { + return channel; + } + + /** + * Complete workflow for retrieving the wireless network configuration, consisting of Login into bridge, querying + * the network configuration and logout from bridge based on a well-prepared environment of a + * {@link VeluxBridgeProvider}, where the results are stored within {@link VeluxBridgeWLANConfig#channel}. + * + * @param bridge Initialized Velux bridge handler. + * @return channel - or null - + * of type {@link VeluxBridgeWLANConfig.Channel} describing the overall result of this interaction. + */ + public Channel retrieve(VeluxBridge bridge) { + logger.trace("retrieve() called."); + + GetWLANConfig bcp = bridge.bridgeAPI().getWLANConfig(); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + logger.trace("retrieve() found successfully configuration {}.", bcp.getWLANConfig()); + channel.openHABwlanSSID = new StringType(bcp.getWLANConfig().getSSID()); + channel.openHABwlanPassword = new StringType(bcp.getWLANConfig().getPassword()); + channel.isRetrieved = true; + } else { + channel.isRetrieved = false; + logger.trace("retrieve() finished with failure."); + } + return channel; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/BridgeAPI.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/BridgeAPI.java new file mode 100644 index 0000000000000..10f51ea99c691 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/BridgeAPI.java @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Definition of the 3rd Level I/O interface towards the Velux bridge. + *

+ * It provides the one-and-only protocol specific 1st-level communication class. + * Additionally it provides all methods for different gateway interactions. + *

+ * The following class access methods exist: + *

    + *
  • {@link #getDeviceStatus} for retrieving the bridge state (i.e. IDLE, BUSY, a.s.o),
  • + *
  • {@link #getFirmware} for retrieving the firmware version of the bridge,
  • + *
  • {@link #getHouseStatus} for retrieving the information about device state changes recognized by the + * bridge,
  • + *
  • {@link #getLANConfig} for retrieving the complete LAN information of the bridge,
  • + *
  • {@link #getProduct} for retrieving the any information about a device behind the bridge,
  • + *
  • {@link #getProductLimitation} for retrieving the limitation information about a device behind the + * bridge,
  • + *
  • {@link #getProducts} for retrieving the any information for all devices behind the bridge,
  • + *
  • {@link #getScenes} for retrieving the any information for all scenes defined on the bridge,
  • + *
  • {@link #getWLANConfig} for retrieving the complete WLAN information of the bridge,
  • + *
  • {@link #login} for establishing a trusted connectivity by authentication,
  • + *
  • {@link #logout} for tearing down the trusted connectivity by deauthentication,
  • + *
  • {@link #runProductCommand} for manipulation of a device behind the bridge (i.e. instructing to + * modify a position),
  • + *
  • {@link #runProductDiscovery} for activation of learning mode of the bridge to discovery new + * products,
  • + *
  • {@link #runProductIdentification} for human-oriented identification a device behind the bridge (i.e. + * by winking or switching on-and-off),
  • + *
  • {@link #runProductSearch} for searching for lost products on the bridge,
  • + *
  • {@link #runScene} for manipulation of a set of devices behind the bridge which are tied together as + * scene,
  • + *
  • {@link #setHouseStatusMonitor} for activation or deactivation of the house monitoring mode to be + * informed about device state changes recognized by the bridge,
  • + *
  • {@link #setSceneVelocity} for changes the velocity of a scene defined on the bridge (i.e. silent or + * fast mode).
  • + *
+ *

+ * Message semantic: Retrieval of Bridge configuration and information of devices beyond the bridge. + *

+ * + * It defines information how to send query and receive answer through the + * VeluxBridgeProvider as described by the BridgeCommunicationProtocol. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public interface BridgeAPI { + + public Login login(); + + public Logout logout(); + + public @Nullable SetHouseStatusMonitor setHouseStatusMonitor(); + + public @Nullable GetHouseStatus getHouseStatus(); + + public RunProductDiscovery runProductDiscovery(); + + public RunProductSearch runProductSearch(); + + public RunProductIdentification runProductIdentification(); + + public GetDeviceStatus getDeviceStatus(); + + public GetFirmware getFirmware(); + + public GetLANConfig getLANConfig(); + + public GetWLANConfig getWLANConfig(); + + public GetProducts getProducts(); + + public @Nullable GetProduct getProduct(); + + public @Nullable GetProductLimitation getProductLimitation(); + + public @Nullable RunProductCommand runProductCommand(); + + public GetScenes getScenes(); + + public SetSceneVelocity setSceneVelocity(); + + public RunScene runScene(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/BridgeCommunicationProtocol.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/BridgeCommunicationProtocol.java new file mode 100644 index 0000000000000..2f4a056d73a7f --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/BridgeCommunicationProtocol.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Protocol independent bridge communication supported by the Velux bridge. + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 2nd level interface it defines the methods to help in sending a query and + * processing the received answer. + *

+ * Methods in this interface for the appropriate interaction: + *

    + *
  • {@link name} to return the name of the interaction for human interface.
  • + *
  • {@link isCommunicationSuccessful} to signal the success of the interaction (only available + * after storing the response).
  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public interface BridgeCommunicationProtocol { + + /** + * Returns the name of this communication pair. + * + * @return name of the communication pair for human beings. + */ + public String name(); + + /** + * Returns the communication status included within the response message. + * + * @return true if the communication was successful, and false otherwise. + */ + public boolean isCommunicationSuccessful(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetDeviceStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetDeviceStatus.java new file mode 100644 index 0000000000000..d3b5beb7b63a5 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetDeviceStatus.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.things.VeluxGwState; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #getState} for retrieval of information. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class GetDeviceStatus implements BridgeCommunicationProtocol { + /** + * Parameter of the device state. + * + * @return thisState as VeluxGwState describing the current status of the bridge. + */ + public abstract VeluxGwState getState(); +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetFirmware.java new file mode 100644 index 0000000000000..53cd3517b1faa --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetFirmware.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.things.VeluxGwFirmware; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #getFirmware} for retrieving the Velux firmware information. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class GetFirmware implements BridgeCommunicationProtocol { + + /** + * Retrieval of firmware version string + * + * @return firmware as VeluxGwFirmware describing the current software of the bridge. + */ + public abstract VeluxGwFirmware getFirmware(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetHouseStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetHouseStatus.java new file mode 100644 index 0000000000000..f0f8058376391 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetHouseStatus.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class GetHouseStatus implements BridgeCommunicationProtocol { + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetLANConfig.java new file mode 100644 index 0000000000000..b357656d8625f --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetLANConfig.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.things.VeluxGwLAN; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #getLANConfig} for retrieving the Velux LAN configuration information. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class GetLANConfig implements BridgeCommunicationProtocol { + + /** + * Retrieval of the parameters of the LAN configuration. + * + * @return lanConfig as VeluxGwLAN describing the current status of the bridge. + */ + public abstract VeluxGwLAN getLANConfig(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetProduct.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetProduct.java new file mode 100644 index 0000000000000..6f430c131318f --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetProduct.java @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.things.VeluxProduct; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #setProductId} for defining the intended product. + *
  • {@link #getProduct} for accessing the retrieved information. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class GetProduct implements BridgeCommunicationProtocol { + + /** + * Set the intended node identifier to be queried + * + * @param nodeId Gateway internal node identifier (zero to 199) + */ + public abstract void setProductId(int nodeId); + + /** + * Retrieval of information about the selected product + * + * @return veluxProduct as VeluxProduct. + */ + public abstract VeluxProduct getProduct(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetProductLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetProductLimitation.java new file mode 100644 index 0000000000000..a2c007c288975 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetProductLimitation.java @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #setActuatorIdAndLimitationType} for defining the intended actuator and the query type. + *
  • {@link #getLimitation} for accessing the retrieved information. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class GetProductLimitation implements BridgeCommunicationProtocol { + + /** + * Set the intended node identifier to be queried + * + * @param nodeId Gateway internal node identifier (zero to 199). + * @param limitationMinimum Minimum or Maximum. + */ + public abstract void setActuatorIdAndLimitationType(int nodeId, boolean limitationMinimum); + + /** + * Retrieval of information about the selected product + * + * @return limitation as int. + */ + public abstract int getLimitation(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetProducts.java new file mode 100644 index 0000000000000..1105ce943142b --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetProducts.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.things.VeluxProduct; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #getProducts} for retrieving the Velux products/actuators information. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + * + */ +@NonNullByDefault +public abstract class GetProducts implements BridgeCommunicationProtocol { + + /** + * Retrieval of information about all products + * + * @return arrayOfVeluxProducts as Array of VeluxProduct. + */ + public abstract VeluxProduct[] getProducts(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetScenes.java new file mode 100644 index 0000000000000..c0518664fb6fc --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetScenes.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.things.VeluxScene; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #getScenes} for retrieving the Velux scenes information. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class GetScenes implements BridgeCommunicationProtocol { + + /** + * Retrieval of information about all defined scenes + * + * @return arrayOfScenes as Array of VeluxScene describing the current scene configuration of the bridge. + */ + public abstract VeluxScene[] getScenes(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetWLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetWLANConfig.java new file mode 100644 index 0000000000000..441232adaf4c2 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetWLANConfig.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.things.VeluxGwWLAN; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #getWLANConfig} for retrieving the Velux WLAN configuration information. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class GetWLANConfig implements BridgeCommunicationProtocol { + + /** + * Retrieval of the parameters of the wireless LAN configuration. + * + * @return wlanConfig as VeluxGwWLAN describing the current status of the bridge. + */ + public abstract VeluxGwWLAN getWLANConfig(); +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/Login.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/Login.java new file mode 100644 index 0000000000000..a8d7cc2651987 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/Login.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Communication to authenticate itself, resulting in a return of current bridge state. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #setPassword} for defining the intended authentication value. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class Login implements BridgeCommunicationProtocol { + + /** + * Sets the intended password string to be used for authentication + * + * @param thisPassword Password passed as String. + */ + public void setPassword(String thisPassword) { + } + + /** + * Returns the authentication information optionally to be used for later following + * messages. + * + * @return authentication token as String which can be used for next operations. + */ + public String getAuthToken() { + return ""; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/Logout.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/Logout.java new file mode 100644 index 0000000000000..7af29948ac675 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/Logout.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Communication to authenticate itself, resulting in a return of current bridge state. + *

+ * Note: even before the deauthentication, an authentication is intended. + *

+ * Each protocol-specific implementation has to provide the common + * methods defined by {@link BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class Logout implements BridgeCommunicationProtocol { +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductCommand.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductCommand.java new file mode 100644 index 0000000000000..636086e2ddf01 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductCommand.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #setNodeAndMainParameter} for defining the intended node and the main parameter value. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class RunProductCommand implements BridgeCommunicationProtocol { + + /** + * Modifies the state of an actuator + * + * @param actuatorId Gateway internal actuator identifier (zero to 199). + * @param parameterValue target device state. + * @return reference to the class instance. + */ + public abstract RunProductCommand setNodeAndMainParameter(int actuatorId, int parameterValue); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductDiscovery.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductDiscovery.java new file mode 100644 index 0000000000000..94fa3c91aeeeb --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductDiscovery.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * Each protocol-specific implementation has to provide the common + * methods defined by {@link BridgeCommunicationProtocol}. + * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class RunProductDiscovery implements BridgeCommunicationProtocol { + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductIdentification.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductIdentification.java new file mode 100644 index 0000000000000..56571208722b7 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductIdentification.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #setProductId} for defining the intended node. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class RunProductIdentification implements BridgeCommunicationProtocol { + + /** + * Set the intended node identifier to be identified + * + * @param id Gateway internal node identifier (zero to 199) + * @return reference to the class instance. + */ + public RunProductIdentification setProductId(int id) { + return this; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductSearch.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductSearch.java new file mode 100644 index 0000000000000..f37989c8353e6 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductSearch.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * Each protocol-specific implementation has to provide the common + * methods defined by {@link BridgeCommunicationProtocol}. + * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class RunProductSearch implements BridgeCommunicationProtocol { + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunScene.java new file mode 100644 index 0000000000000..31cc7166b73b5 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunScene.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #setSceneId} for defining the intended scene. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class RunScene implements BridgeCommunicationProtocol { + + /** + * Sets the intended scene identifier to be executed + * + * @param id Gateway internal scene identifier + * @return reference to the class instance. + */ + public RunScene setSceneId(int id) { + return this; + } + + /** + * Sets the intended scene velocity for later execution + * + * @param velocity setting as String. + * @return reference to the class instance. + */ + public RunScene setVelocity(int velocity) { + return this; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/SetHouseStatusMonitor.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/SetHouseStatusMonitor.java new file mode 100644 index 0000000000000..e7d115f6fd46b --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/SetHouseStatusMonitor.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #serviceActivation} for defining the intended parameter value. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class SetHouseStatusMonitor implements BridgeCommunicationProtocol { + + /** + * Modifies the intended state of the gateway-internal house monitoring settings. + * + * @param enableService as boolean. + * @return reference to the class instance. + */ + public abstract SetHouseStatusMonitor serviceActivation(boolean enableService); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/SetSceneVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/SetSceneVelocity.java new file mode 100644 index 0000000000000..1d2bbff39a0f5 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/SetSceneVelocity.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #setMode} for retrieval of information. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class SetSceneVelocity implements BridgeCommunicationProtocol { + + public abstract SetSceneVelocity setMode(int id, boolean silent); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/package-info.java new file mode 100644 index 0000000000000..92c0d7d95fc39 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/package-info.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2010-2019 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 + */ +/** + * Interface definitions being used to implement the protocol-dependent interactions to IO-homecontrolled devices. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.bridge.common; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetDeviceStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetDeviceStatus.java new file mode 100644 index 0000000000000..3911b6a38fbad --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetDeviceStatus.java @@ -0,0 +1,168 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.json; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.bridge.common.GetDeviceStatus; +import org.openhab.binding.velux.things.VeluxGwState; +import org.openhab.binding.velux.things.VeluxGwState.VeluxGatewayState; +import org.openhab.binding.velux.things.VeluxGwState.VeluxGatewaySubState; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Retrieval of current bridge state. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.bridge.json.JsonBridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JCgetDeviceStatus extends GetDeviceStatus implements JsonBridgeCommunicationProtocol { + + private static final String URL = "/api/v1/device"; + private static final String DESCRIPTION = "get device status"; + + private Request request = new Request(); + private Response response = new Response(); + + /* + * Message Objects + */ + + /** + * Bridge I/O Request message used by {@link org.openhab.binding.velux.bridge.json.JsonVeluxBridge JsonVeluxBridge} + * for serializing. + * + * Resulting JSON: + * + *

+     * {"action":"getDeviceStatus","params":{}}
+     * 
+ * + * NOTE: the gateway software is extremely sensitive to this exact JSON structure. + * Any modifications (like omitting empty params) will lead to an gateway error. + */ + private static class Request { + + @SuppressWarnings("unused") + private String action; + + @SuppressWarnings("unused") + private Map params; + + public Request() { + this.action = "getDeviceStatus"; + this.params = new HashMap(); + } + } + + /** + * Bridge I/O Response message used by {@link JsonVeluxBridge} for deserializing with including component access + * methods + *

+ * Expected JSON (sample): + * + *

+     * {
+     *  "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+     *  "result":true,
+     *  "deviceStatus":"discovering",       or "IDLE"
+     *  "data":{},
+     *  "errors":[]
+     * }
+     * 
+ */ + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + @SuppressWarnings("unused") + private @Nullable Object data = null; + private String[] errors = {}; + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object thisResponse) { + response = (Response) thisResponse; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.result; + } + + @Override + public String getDeviceStatus() { + return response.deviceStatus; + } + + @Override + public String[] getErrors() { + return response.errors; + } + + /* + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public VeluxGwState getState() { + String deviceStatus = this.getDeviceStatus(); + byte stateValue = (byte) VeluxGatewayState.GW_S_GWM.getStateValue(); + byte subStateValue; + if (deviceStatus.equals("discovering")) { + subStateValue = (byte) VeluxGatewaySubState.GW_SS_P1.getStateValue(); + } else if (deviceStatus.equals("IDLE")) { + subStateValue = (byte) VeluxGatewaySubState.GW_SS_IDLE.getStateValue(); + } else { + subStateValue = (byte) VeluxGatewaySubState.GW_SS_P2.getStateValue(); + } + return new VeluxGwState(stateValue, subStateValue); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetFirmware.java new file mode 100644 index 0000000000000..8422fc1551f72 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetFirmware.java @@ -0,0 +1,163 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.json; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.bridge.common.GetFirmware; +import org.openhab.binding.velux.things.VeluxGwFirmware; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Retrieval of Bridge configuration. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.bridge.json.JsonBridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JCgetFirmware extends GetFirmware implements JsonBridgeCommunicationProtocol { + + private static final String URL = "/api/v1/settings"; + private static final String DESCRIPTION = "get firmware version"; + + private Request request = new Request(); + private Response response = new Response(); + + /* + * Message Objects + */ + + /** + * Bridge I/O Request message used by {@link org.openhab.binding.velux.bridge.json.JsonVeluxBridge JsonVeluxBridge} + * for serializing. + *

+ * Resulting JSON: + * + *

+     * {"action":"getFirmware","params":{}}
+     * 
+ */ + private static class Request { + + @SuppressWarnings("unused") + private String action; + + @SuppressWarnings("unused") + private Map params; + + public Request() { + this.action = "getFirmware"; + this.params = new HashMap(); + } + } + + /** + * Bridge Communication Structure containing the version of the firmware. + *

+ * Used within structure {@link JCgetFirmware} to describe the software of the Bridge. + */ + private static class BCfirmwareVersion { + /* + * "version": "0.1.1.0.41.0" + */ + private String version = VeluxBindingConstants.UNKNOWN; + } + + /** + * Bridge I/O Response message used by {@link org.openhab.binding.velux.bridge.json.JsonVeluxBridge} for + * deserializing with including component access methods + *

+ * Expected JSON (sample): + * + *

+     * {
+     *  "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+     *  "result":true,
+     *  "deviceStatus":"IDLE",
+     *  "data":{"version":"0.1.1.0.41.0"},
+     *  "errors":[]
+     * }
+     * 
+ */ + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + private BCfirmwareVersion data = new BCfirmwareVersion(); + private String[] errors = {}; + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object thisResponse) { + response = (Response) thisResponse; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.result; + } + + @Override + public String getDeviceStatus() { + return response.deviceStatus; + } + + @Override + public String[] getErrors() { + return response.errors; + } + + /** + * Methods in addition to interface {@link JsonBridgeCommunicationProtocol}. + */ + @Override + public VeluxGwFirmware getFirmware() { + VeluxGwFirmware gwFirmware = new VeluxGwFirmware(response.data.version); + return gwFirmware; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetLANConfig.java new file mode 100644 index 0000000000000..03bcad2b2309b --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetLANConfig.java @@ -0,0 +1,187 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.json; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.bridge.common.BridgeCommunicationProtocol; +import org.openhab.binding.velux.bridge.common.GetLANConfig; +import org.openhab.binding.velux.things.VeluxGwLAN; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Retrieval of LAN configuration. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.bridge.json.JsonBridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JCgetLANConfig extends GetLANConfig implements BridgeCommunicationProtocol, JsonBridgeCommunicationProtocol { + + private static final String URL = "/api/v1/lan"; + private static final String DESCRIPTION = "get LAN configuration"; + + private Request request = new Request(); + private Response response = new Response(); + + /* + * Message Objects + */ + + /** + * Bridge I/O Request message used by {@link JsonVeluxBridge} + * for serializing. + *

+ * Resulting JSON: + * + *

+     * {"action":"get","params":{}}
+     * 
+ */ + private static class Request { + + @SuppressWarnings("unused") + private String action; + + @SuppressWarnings("unused") + private Map params; + + public Request() { + this.action = "get"; + this.params = new HashMap(); + } + } + + /** + * Bridge Communication Structure containing the network parameters. + *

+ * Used within structure {@link JCgetLANConfig} to describe the network connectivity of the Bridge. + * + *

+     * {"ipAddress":"192.168.45.9","subnetMask":"255.255.255.0","defaultGateway":"192.168.45.129","dhcp":false}
+     * 
+ */ + private static class BCLANConfig { + private String ipAddress = VeluxBindingConstants.UNKNOWN; + private String subnetMask = VeluxBindingConstants.UNKNOWN; + private String defaultGateway = VeluxBindingConstants.UNKNOWN; + private boolean dhcp; + + @Override + public String toString() { + return String.format("ipAddress=%s,subnetMask=%s,defaultGateway=%s,dhcp=%s", this.ipAddress, + this.subnetMask, this.defaultGateway, this.dhcp ? "on" : "off"); + } + } + + /** + * Bridge I/O Response message used by {@link JsonVeluxBridge} for unmarshalling with including component access + * methods + *

+ * Expected JSON (sample): + * + *

+     * {
+     *  "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+     *  "result":true,
+     *  "deviceStatus":"IDLE",
+     *  "data":"ipAddress":"192.168.45.9","subnetMask":"255.255.255.0","defaultGateway":"192.168.45.129","dhcp":false},
+     *  "errors":[]
+     * }
+     * 
+ */ + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + private BCLANConfig data = new BCLANConfig(); + private String[] errors = {}; + + public boolean getResult() { + return result; + } + + public String getDeviceStatus() { + return deviceStatus; + } + + public String[] getErrors() { + return errors; + } + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object response) { + this.response = (Response) response; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.getResult(); + } + + @Override + public String getDeviceStatus() { + return response.getDeviceStatus(); + } + + @Override + public String[] getErrors() { + return response.getErrors(); + } + + /** + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + @Override + public VeluxGwLAN getLANConfig() { + VeluxGwLAN gwLAN = new VeluxGwLAN(response.data.ipAddress, response.data.subnetMask, + response.data.defaultGateway, response.data.dhcp); + return gwLAN; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetProducts.java new file mode 100644 index 0000000000000..2a9b5f1c8c612 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetProducts.java @@ -0,0 +1,194 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.json; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.bridge.common.GetProducts; +import org.openhab.binding.velux.things.VeluxProduct; +import org.openhab.binding.velux.things.VeluxProduct.ProductBridgeIndex; +import org.openhab.binding.velux.things.VeluxProductName; +import org.openhab.binding.velux.things.VeluxProductType; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Retrieval of products. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.bridge.json.JsonBridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JCgetProducts extends GetProducts implements JsonBridgeCommunicationProtocol { + + private static final String URL = "/api/v1/products"; + private static final String DESCRIPTION = "get Products"; + + private Request request = new Request(); + private Response response = new Response(); + + /** + * Bridge Communication class describing a product + * + *

+     * "name": "Rolladen Bad",
+     * "category": "Roller shutter",
+     * "id": 2,
+     * "typeId": 2,
+     * "subtype": 0,
+     * "scenes": [
+     * "V_DG_Shutter_Mitte_000",
+     * "V_DG_Shutter_Mitte_085",
+     * "V_DG_Shutter_Mitte_100"
+     * ]
+     * 
+ */ + private class BCproduct { + private String name = VeluxBindingConstants.UNKNOWN; + @SuppressWarnings("unused") + private String category = VeluxBindingConstants.UNKNOWN; + private int id; + private int typeId; + @SuppressWarnings("unused") + private int subtype; + @SuppressWarnings("unused") + private String[] scenes = {}; + } + + /** + * Bridge I/O Request message used by {@link org.openhab.binding.velux.bridge.json.JsonVeluxBridge JsonVeluxBridge} + * for serializing. + *

+ * Resulting JSON: + * + *

+     * {"action":"get","params":{}}
+     * 
+ */ + private static class Request { + + @SuppressWarnings("unused") + private String action; + + @SuppressWarnings("unused") + private Map params; + + public Request() { + this.action = "get"; + this.params = new HashMap(); + } + } + + /** + * Bridge I/O Response message used by {@link org.openhab.binding.velux.bridge.VeluxBridge VeluxBridge} for + * deserialization with including component access methods + *

+ * Expected JSON (sample): + * + *

+     * {
+     * "token": "pESIc/9zDWa1CJR6hCDzLw==",
+     * "result": true,
+     * "deviceStatus": "IDLE",
+     * "data": [
+     *  { "name": "Bad",
+     *    "category": "Window opener",
+     *    "id": 0,
+     *    "typeId": 4,
+     *    "subtype": 1,
+     *    "scenes": [
+     *       "V_DG_Window_Mitte_000",
+     *       "V_DG_Window_Mitte_100"
+     *    ]
+     *  },
+     * ],
+     * "errors": []
+     * }
+     * 
+ */ + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + private JCgetProducts.BCproduct[] data = {}; + private String[] errors = {}; + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object thisResponse) { + response = (Response) thisResponse; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.result; + } + + @Override + public String getDeviceStatus() { + return response.deviceStatus; + } + + @Override + public String[] getErrors() { + return response.errors; + } + + /** + * Methods in addition to interface {@link JsonBridgeCommunicationProtocol}. + */ + @Override + public VeluxProduct[] getProducts() { + VeluxProduct[] products = new VeluxProduct[response.data.length]; + for (int productIdx = 0; productIdx < response.data.length; productIdx++) { + products[productIdx] = new VeluxProduct(new VeluxProductName(response.data[productIdx].name), + VeluxProductType.get(response.data[productIdx].typeId), + new ProductBridgeIndex(response.data[productIdx].id)); + } + return products; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetScenes.java new file mode 100644 index 0000000000000..36a011e4e9d13 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetScenes.java @@ -0,0 +1,225 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.json; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.bridge.common.GetScenes; +import org.openhab.binding.velux.things.VeluxProductName; +import org.openhab.binding.velux.things.VeluxProductReference; +import org.openhab.binding.velux.things.VeluxProductState; +import org.openhab.binding.velux.things.VeluxScene; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Retrieval of scene configurations. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.bridge.json.JsonBridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JCgetScenes extends GetScenes implements JsonBridgeCommunicationProtocol { + + private static final String URL = "/api/v1/scenes"; + private static final String DESCRIPTION = "get Scenes"; + + private Request request = new Request(); + private Response response = new Response(); + + /** + * Bridge Communication Structure containing the state of a product. + *

+ * Therefore it includes the typeId and name identifying the product, as well as actuator and status. + *

+ * Used within structure {@link BCscene} to describe the final states of the products belonging to this scene. + * + *

+     * "typeId": 2,
+     * "name": "Rolladen Schlafzimmer",
+     * "actuator": 0,
+     * "status": 0
+     * 
+ */ + private static class BCproductState { + private int typeId; + private String name = VeluxBindingConstants.UNKNOWN; + private int actuator; + private int status; + } + + /** + * Bridge Communication Structure containing a scene with different states of products. + *

+ * Therefore it includes the name and id identifying the scene, a flag about silence-mode, as well as the different + * states. + *

+ * These states are defined by an array of {@link BCproductState} as part of this structure. + * + *

+     * {
+     * "name": "V_DG_Shutter_West_100",
+     * "id": 0,
+     * "silent": true,
+     * "bCproductStates": [
+     * {
+     * "typeId": 2,
+     * "name": "Rolladen Schlafzimmer",
+     * "actuator": 0,
+     * "status": 100
+     * }
+     * ]
+     * },
+     * 
+ */ + private static class BCscene { + private String name = VeluxBindingConstants.UNKNOWN; + private int id; + private boolean silent; + private BCproductState[] products = {}; + } + + /** + * Bridge I/O Request message used by {@link org.openhab.binding.velux.bridge.json.JsonVeluxBridge JsonVeluxBridge} + * for serializing. + *

+ * Resulting JSON: + * + *

+     * {"action":"get","params":{}}
+     * 
+ */ + private static class Request { + + @SuppressWarnings("unused") + private String action; + @SuppressWarnings("unused") + private Map params; + + public Request() { + this.action = "get"; + this.params = new HashMap(); + } + } + + /** + * Bridge Communication Structure describing a response to be received from the Velux Bridge. + * + *
+     * {
+     * "token": "kWwXRQ5mlwgYfvk23g2zXw==",
+     * "result": true,
+     * "deviceStatus": "IDLE",
+     * "data": [
+     * {
+     * "name": "V_DG_Shutter_West_100",
+     * "id": 0,
+     * "silent": true,
+     * "bCproductStates": [
+     * {
+     * "typeId": 2,
+     * "name": "Rolladen Schlafzimmer",
+     * "actuator": 0,
+     * "status": 100
+     * }
+     * ]
+     * },
+     * "errors": []
+     * }
+     * 
+ */ + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + private BCscene[] data = {}; + private String[] errors = {}; + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object thisResponse) { + response = (Response) thisResponse; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.result; + } + + @Override + public String getDeviceStatus() { + return response.deviceStatus; + } + + @Override + public String[] getErrors() { + return response.errors; + } + + /** + * Methods in addition to interface {@link JsonBridgeCommunicationProtocol}. + */ + @Override + public VeluxScene[] getScenes() { + VeluxScene[] scenes = new VeluxScene[response.data.length]; + for (int sceneIdx = 0; sceneIdx < response.data.length; sceneIdx++) { + + VeluxProductState[] productStates = new VeluxProductState[response.data[sceneIdx].products.length]; + for (int productIdx = 0; productIdx < response.data[sceneIdx].products.length; productIdx++) { + productStates[productIdx] = new VeluxProductState( + new VeluxProductReference( + new VeluxProductName(response.data[sceneIdx].products[productIdx].name), + response.data[sceneIdx].products[productIdx].typeId), + response.data[sceneIdx].products[productIdx].actuator, + response.data[sceneIdx].products[productIdx].status); + } + scenes[sceneIdx] = new VeluxScene(response.data[sceneIdx].name, response.data[sceneIdx].id, + response.data[sceneIdx].silent, productStates); + } + return scenes; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetWLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetWLANConfig.java new file mode 100644 index 0000000000000..79f1f9d2f248a --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetWLANConfig.java @@ -0,0 +1,181 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.json; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.bridge.common.GetWLANConfig; +import org.openhab.binding.velux.things.VeluxGwWLAN; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Retrieval of WLAN configuration. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.bridge.json.JsonBridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JCgetWLANConfig extends GetWLANConfig implements JsonBridgeCommunicationProtocol { + + private static final String URL = "/api/v1/settings"; + private static final String DESCRIPTION = "get WLAN configuration"; + + private Request request = new Request(); + private Response response = new Response(); + + /* + * Message Objects + */ + + /** + * Bridge I/O Request message used by {@link org.openhab.binding.velux.bridge.json.JsonVeluxBridge JsonVeluxBridge} + * for serializing. + *

+ * Resulting JSON: + * + *

+     * {"action":"wifi","params":{}}
+     * 
+ */ + private static class Request { + + @SuppressWarnings("unused") + private String action; + + @SuppressWarnings("unused") + private Map params; + + public Request() { + this.action = "wifi"; + this.params = new HashMap(); + } + } + + /** + * Bridge Communication Structure containing the version of the firmware. + *

+ * Used within structure {@link JCgetWLANConfig} to describe the network connectivity of the Bridge. + * + *

+     * {"password":"Esf56mxqFY","name":"VELUX_KLF_847C"}
+     * 
+ */ + private static class BCWLANConfig { + + private String password = VeluxBindingConstants.UNKNOWN; + private String name = VeluxBindingConstants.UNKNOWN; + + @Override + public String toString() { + return String.format("SSID=%s,password=********", this.name); + } + } + + /** + * Bridge I/O Response message used by {@link JsonBridgeCommunicationProtocol} for deserialization with including + * component access + * methods + *

+ * Expected JSON (sample): + * + *

+     * {
+     *  "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+     *  "result":true,
+     *  "deviceStatus":"IDLE",
+     *  "data":{"password":"Esf56mxqFY","name":"VELUX_KLF_847C"},
+     *  "errors":[]
+     * }
+     * 
+ */ + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + private BCWLANConfig data = new BCWLANConfig(); + private String[] errors = {}; + + public boolean getResult() { + return result; + } + + @Override + public String toString() { + return data.toString(); + } + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object response) { + this.response = (Response) response; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.getResult(); + } + + @Override + public String getDeviceStatus() { + return response.deviceStatus; + } + + @Override + public String[] getErrors() { + return response.errors; + } + + /** + * Methods in addition to interface {@link JsonBridgeCommunicationProtocol}. + */ + @Override + public VeluxGwWLAN getWLANConfig() { + VeluxGwWLAN gwWLAN = new VeluxGwWLAN(response.data.name, response.data.password); + return gwWLAN; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JClogin.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JClogin.java new file mode 100644 index 0000000000000..164f86d3f51d7 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JClogin.java @@ -0,0 +1,173 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.json; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.bridge.common.Login; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Communication to authenticate itself, resulting in a return of current bridge state. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.bridge.json.JsonBridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JClogin extends Login implements JsonBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(JClogin.class); + + private static final String URL = "/api/v1/auth"; + private static final String DESCRIPTION = "authenticate / login"; + + private static Request request = new Request(); + private static Response response = new Response(); + + /* + * Message Objects + */ + private static class ParamsLogin { + @SuppressWarnings("unused") + private String password = VeluxBindingConstants.UNKNOWN; + } + + /** + * Bridge I/O Request message used by {@link JsonVeluxBridge} + * for serializing. + *

+ * Resulting JSON: + * + *

+     * {"action":"login","params":{"password":"PASSWORD"}}
+     * 
+ */ + private static class Request { + + @SuppressWarnings("unused") + private final String action = "login"; + private ParamsLogin params; + + public Request() { + this.params = new ParamsLogin(); + } + } + + /** + * Bridge I/O Response message used by {@link JsonVeluxBridge} for deserializing with including component access + * methods + *

+ * Expected JSON (sample): + * + *

+     * '{"token": "PHPnfLda71xfGlxoYEOTGQ==", "result": true, "deviceStatus": "IDLE", "data": {}, "errors": [] }'
+     * 
+ */ + private static class Response { + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + @SuppressWarnings("unused") + private @Nullable Object data; + private String[] errors = {}; + + public String getToken() { + return token; + } + + public boolean getResult() { + return result; + } + } + + /* + * Constructor Method + */ + + public JClogin() { + logger.trace("JClogin(constructor) called."); + } + + @Override + public void finalize() { + logger.trace("finalize({}) called.", this); + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object thisResponse) { + response = (Response) thisResponse; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.getResult(); + } + + @Override + public String getDeviceStatus() { + return response.deviceStatus; + } + + @Override + public String[] getErrors() { + return response.errors; + } + + /* + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public void setPassword(String thisPassword) { + logger.trace("setPassword({}) called.", thisPassword); + request.params.password = thisPassword; + } + + @Override + public String getAuthToken() { + return response.getToken(); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JClogout.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JClogout.java new file mode 100644 index 0000000000000..4c7dae4a3d861 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JClogout.java @@ -0,0 +1,135 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.json; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.bridge.common.Logout; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Communication to deauthenticate itself, resulting in a return of current bridge state. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.bridge.json.JsonBridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JClogout extends Logout implements JsonBridgeCommunicationProtocol { + + private static final String URL = "/api/v1/auth"; + private static final String DESCRIPTION = "deauthenticate / logout"; + + private Request request = new Request(); + private Response response = new Response(); + + /** + * Bridge I/O Request message used by {@link JsonVeluxBridge} for serializing. + *

+ * Resulting JSON: + * + *

+     * {"action":"logout","params":{}}
+     * 
+ */ + private static class Request { + + @SuppressWarnings("unused") + private String action; + @SuppressWarnings("unused") + private Map params; + + public Request() { + this.action = "logout"; + this.params = new HashMap(); + } + } + + /** + * Bridge I/O Response message used by {@link JsonVeluxBridge} for deserializing with including component access + * methods + *

+ * Expected JSON (sample): + * + *

+     * '{"token": "PHPnfLda71xfGlxoYEOTGQ==", "result": true, "deviceStatus": "IDLE", "data": {}, "errors": [] }'
+     * 
+ */ + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + @SuppressWarnings("unused") + private @Nullable Object data = null; + private String[] errors = {}; + + public boolean getResult() { + return result; + } + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object thisResponse) { + response = (Response) thisResponse; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.getResult(); + } + + @Override + public String getDeviceStatus() { + return response.deviceStatus; + } + + @Override + public String[] getErrors() { + return response.errors; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunProductDiscovery.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunProductDiscovery.java new file mode 100644 index 0000000000000..52dc3bc6d638f --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunProductDiscovery.java @@ -0,0 +1,157 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.json; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.bridge.common.RunProductDiscovery; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic:Action to start discovery of products, i.e. Velux devices. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link JsonBridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JCrunProductDiscovery extends RunProductDiscovery implements JsonBridgeCommunicationProtocol { + + private static final String URL = "/api/v1/products"; + private static final String DESCRIPTION = "discover products"; + + private Request request = new Request(); + private Response response = new Response(); + + /* + * Message Objects + */ + + /** + * Bridge I/O Request message used by {@link JsonVeluxBridge} + * for serializing. + * + * Resulting JSON: + * + *

+     * {"action":"discover","params":{}}
+     * 
+ * + * NOTE: the gateway software is extremely sensitive to this exact JSON structure. + * Any modifications (like omitting empty params) will lead to an gateway error. + */ + private static class Request { + + @SuppressWarnings("unused") + private String action; + + @SuppressWarnings("unused") + private Map params; + + public Request() { + this.action = "discover"; + this.params = new HashMap(); + } + } + + /** + * Bridge I/O Response message used by {@link JsonVeluxBridge} for deserializing with including component access + * methods. + * + * Expected JSON (sample): + * + *
+     * {
+     *  "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+     *  "result":true,
+     *  "deviceStatus":"discovering",
+     *  "data":{},
+     *  "errors":[]
+     * }
+     * 
+ */ + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + @SuppressWarnings("unused") + private @Nullable Object data = null; + private String[] errors = {}; + + public boolean getResult() { + return result; + } + + public String getDeviceStatus() { + return deviceStatus; + } + + public String[] getErrors() { + return errors; + } + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object thisResponse) { + response = (Response) thisResponse; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.getResult(); + } + + @Override + public String getDeviceStatus() { + return response.getDeviceStatus(); + } + + @Override + public String[] getErrors() { + return response.getErrors(); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunProductIdentification.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunProductIdentification.java new file mode 100644 index 0000000000000..8a644df0484fe --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunProductIdentification.java @@ -0,0 +1,173 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.json; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.bridge.common.RunProductIdentification; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Trigger action to identify a product, resulting in a return of current bridge state. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link JsonBridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault + +class JCrunProductIdentification extends RunProductIdentification implements JsonBridgeCommunicationProtocol { + private static final int DEFAULT_IDENTIFY_TIME = 50; + + private static final String URL = "/api/v1/products"; + private static final String DESCRIPTION = "identify one product"; + + private Request request = new Request(); + private Response response = new Response(); + + private static int productId; + private static int identifyTime = DEFAULT_IDENTIFY_TIME; + + /* + * Message Objects + */ + private static class ParamsIdentifyProduct { + @SuppressWarnings("unused") + private int id; + @SuppressWarnings("unused") + private int time; + + private ParamsIdentifyProduct(int id, int time) { + this.id = id; + this.time = time; + } + } + + /** + * Bridge I/O Request message used by {@link JsonVeluxBridge} for serializing. + *

+ * Resulting JSON (sample): + * + *

+     * {"action":"identify","params":{"id":23,"time":254}}
+     * 
+ */ + private static class Request { + @SuppressWarnings("unused") + private String action; + @SuppressWarnings("unused") + private ParamsIdentifyProduct params; + + public Request() { + this.action = "identify"; + this.params = new ParamsIdentifyProduct(JCrunProductIdentification.productId, + JCrunProductIdentification.identifyTime); + } + } + + /** + * Bridge I/O Response message used by {@link JsonVeluxBridge} for deserializing with including component access + * methods + *

+ * Expected JSON (sample): + * + *

+     * {
+     * "token": "NkR/AA5xXj7iL6NiIW8keA==",
+     * "result": false,
+     * "deviceStatus": "IDLE",
+     * "data": {},
+     * "errors": [ 104 ]
+     * }
+     * 
+ */ + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + @SuppressWarnings("unused") + private @Nullable Object data; + private String[] errors = {}; + + public boolean getResult() { + return result; + } + + public String getDeviceStatus() { + return deviceStatus; + } + + public String[] getErrors() { + return errors; + } + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object response) { + this.response = (Response) response; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.getResult(); + } + + @Override + public String getDeviceStatus() { + return response.getDeviceStatus(); + } + + @Override + public String[] getErrors() { + return response.getErrors(); + } + + /* + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + @Override + public JCrunProductIdentification setProductId(int id) { + JCrunProductIdentification.productId = id; + return this; + } +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunProductSearch.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunProductSearch.java new file mode 100644 index 0000000000000..1e3b565a7d37f --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunProductSearch.java @@ -0,0 +1,147 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.json; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.bridge.common.RunProductSearch; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: query for lost nodes, resulting in a return of current bridge state. + *

+ * Implementing the abstract class {@link RunProductSearch}. + *

+ * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.bridge.json.JsonBridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JCrunProductSearch extends RunProductSearch implements JsonBridgeCommunicationProtocol { + + private static final String URL = "/api/v1/device"; + private static final String DESCRIPTION = "check lost nodes"; + + private Request request = new Request(); + private Response response = new Response(); + + /* + * Message Objects + */ + + /** + * Bridge I/O Request message used by {@link org.openhab.binding.velux.bridge.json.JsonVeluxBridge JsonVeluxBridge} + * for serializing. + * + * Resulting JSON: + * + *

+     * {"action":"checkLostNodes","params":{}}
+     * 
+ * + * NOTE: the gateway software is extremely sensitive to this exact JSON structure. + * Any modifications (like omitting empty params) will lead to an gateway error. + */ + private static class Request { + + @SuppressWarnings("unused") + private String action; + + @SuppressWarnings("unused") + private Map params; + + public Request() { + this.action = "checkLostNodes"; + this.params = new HashMap(); + } + } + + /** + * Bridge I/O Response message used by {@link org.openhab.binding.velux.bridge.json.JsonVeluxBridge} for + * deserializing with including component access methods + * + * Expected JSON (sample): + * + *
+     * {
+     *  "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+     *  "result":true,
+     *  "deviceStatus":"IDLE",
+     *  "data":[],
+     *  "errors":[]
+     * }
+     * 
+ */ + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + @SuppressWarnings("unused") + private String[] data = {}; + private String[] errors = {}; + + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object thisResponse) { + response = (Response) thisResponse; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.result; + } + + @Override + public String getDeviceStatus() { + return response.deviceStatus; + } + + @Override + public String[] getErrors() { + return response.errors; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunScene.java new file mode 100644 index 0000000000000..7ca78793be913 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunScene.java @@ -0,0 +1,154 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.json; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.bridge.common.RunScene; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Trigger activation of a specific scene, resulting in a return of current bridge state. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.bridge.json.JsonBridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JCrunScene extends RunScene implements JsonBridgeCommunicationProtocol { + + private static final String URL = "/api/v1/scenes"; + private static final String DESCRIPTION = "run Scene"; + + private Request request = new Request(); + private Response response = new Response(); + + /* + * Message Objects + */ + private static class ParamsRunScene { + @SuppressWarnings("unused") + private int id; + } + + /** + * Bridge I/O Request message used by {@link JsonVeluxBridge} + * for serializing. + *

+ * Resulting JSON (sample): + * + *

+     * {"action":"run","params":{"id":9}}
+     * 
+ */ + private static class Request { + @SuppressWarnings("unused") + private String action; + private ParamsRunScene params; + + public Request() { + this.action = "run"; + this.params = new ParamsRunScene(); + } + } + + /** + * Bridge I/O Response message used by {@link JsonVeluxBridge} for deserializing with including component access + * methods + *

+ * Expected JSON (sample): + * + *

+     * {
+     *  "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+     *  "result":true,
+     *  "deviceStatus":"IDLE",
+     *  "data":{},
+     *  "errors":[]
+     * }
+     * 
+ */ + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + @SuppressWarnings("unused") + private @Nullable Object data; + private String[] errors = {}; + + public boolean getResult() { + return result; + } + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object response) { + this.response = (Response) response; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.getResult(); + } + + @Override + public String getDeviceStatus() { + return response.deviceStatus; + } + + @Override + public String[] getErrors() { + return response.errors; + } + + /* + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + @Override + public JCrunScene setSceneId(int id) { + request.params.id = id; + return this; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCsetSceneVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCsetSceneVelocity.java new file mode 100644 index 0000000000000..a338f8407de19 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCsetSceneVelocity.java @@ -0,0 +1,166 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.json; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.bridge.common.SetSceneVelocity; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: setting of scene silent mode, resulting in a return of current bridge state. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.bridge.json.JsonBridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JCsetSceneVelocity extends SetSceneVelocity implements JsonBridgeCommunicationProtocol { + + private static final String URL = "/api/v1/scenes"; + private static final String DESCRIPTION = "modify silent mode"; + + private Request request = new Request(); + private Response response = new Response(); + + private static int productId; + private static boolean silentMode; + + /* + * Message Objects + */ + private static class ParamsRunScene { + @SuppressWarnings("unused") + private int id; + @SuppressWarnings("unused") + private boolean silent; + + private ParamsRunScene(int id, boolean silent) { + this.id = id; + this.silent = silent; + } + } + + /** + * Bridge I/O Request message used by {@link JsonVeluxBridge} + * for serializing. + *

+ * Resulting JSON (sample): + * + *

+     * {"action":"setSilentMode","params":{"id":9,"silent":false}}}
+     * 
+ */ + private static class Request { + @SuppressWarnings("unused") + private String action; + @SuppressWarnings("unused") + private ParamsRunScene params; + + public Request() { + this.action = "setSilentMode"; + this.params = new ParamsRunScene(JCsetSceneVelocity.productId, JCsetSceneVelocity.silentMode); + } + } + + /** + * Bridge I/O Response message used by {@link JsonVeluxBridge} for deserializing with including component access + * methods + *

+ * Expected JSON (sample): + * + *

+     * {
+     *  "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+     *  "result":true,
+     *  "deviceStatus":"IDLE",
+     *  "data":{},
+     *  "errors":[]
+     * }
+     * 
+ */ + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + @SuppressWarnings("unused") + private @Nullable Object data; + private String[] errors = {}; + + public boolean getResult() { + return result; + } + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object response) { + this.response = (Response) response; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.getResult(); + } + + @Override + public String getDeviceStatus() { + return response.deviceStatus; + } + + @Override + public String[] getErrors() { + return response.errors; + } + + /* + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + @Override + public JCsetSceneVelocity setMode(int id, boolean silent) { + JCsetSceneVelocity.productId = id; + JCsetSceneVelocity.silentMode = silent; + return this; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JsonBridgeAPI.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JsonBridgeAPI.java new file mode 100644 index 0000000000000..a31be34a1ac03 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JsonBridgeAPI.java @@ -0,0 +1,203 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.json; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.bridge.VeluxBridgeInstance; +import org.openhab.binding.velux.bridge.common.BridgeAPI; +import org.openhab.binding.velux.bridge.common.GetDeviceStatus; +import org.openhab.binding.velux.bridge.common.GetFirmware; +import org.openhab.binding.velux.bridge.common.GetHouseStatus; +import org.openhab.binding.velux.bridge.common.GetLANConfig; +import org.openhab.binding.velux.bridge.common.GetProduct; +import org.openhab.binding.velux.bridge.common.GetProductLimitation; +import org.openhab.binding.velux.bridge.common.GetProducts; +import org.openhab.binding.velux.bridge.common.GetScenes; +import org.openhab.binding.velux.bridge.common.GetWLANConfig; +import org.openhab.binding.velux.bridge.common.Login; +import org.openhab.binding.velux.bridge.common.Logout; +import org.openhab.binding.velux.bridge.common.RunProductCommand; +import org.openhab.binding.velux.bridge.common.RunProductDiscovery; +import org.openhab.binding.velux.bridge.common.RunProductIdentification; +import org.openhab.binding.velux.bridge.common.RunProductSearch; +import org.openhab.binding.velux.bridge.common.RunScene; +import org.openhab.binding.velux.bridge.common.SetHouseStatusMonitor; +import org.openhab.binding.velux.bridge.common.SetSceneVelocity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * JSON-based 3rd Level I/O interface towards the Velux bridge. + *

+ * It provides the one-and-only protocol specific 1st-level communication class. + * Additionally it provides all methods for different gateway interactions. + *

+ * The following class access methods exist: + *

    + *
  • {@link JsonBridgeAPI#getDeviceStatus} for retrieving the bridge state (i.e. IDLE, BUSY, a.s.o),
  • + *
  • {@link JsonBridgeAPI#getFirmware} for retrieving the firmware version of the bridge,
  • + *
  • {@link JsonBridgeAPI#getHouseStatus} for retrieving the information about device state changes recognized by the + * bridge,
  • + *
  • {@link JsonBridgeAPI#getLANConfig} for retrieving the complete LAN information of the bridge,
  • + *
  • {@link JsonBridgeAPI#getProduct} for retrieving the any information about a device behind the bridge,
  • + *
  • {@link JsonBridgeAPI#getProductLimitation} for retrieving the limitation information about a device behind the + * bridge,
  • + *
  • {@link JsonBridgeAPI#getProducts} for retrieving the any information for all devices behind the bridge,
  • + *
  • {@link JsonBridgeAPI#getScenes} for retrieving the any information for all scenes defined on the bridge,
  • + *
  • {@link JsonBridgeAPI#getWLANConfig} for retrieving the complete WLAN information of the bridge,
  • + *
  • {@link JsonBridgeAPI#login} for establishing a trusted connectivity by authentication,
  • + *
  • {@link JsonBridgeAPI#logout} for tearing down the trusted connectivity by deauthentication,
  • + *
  • {@link JsonBridgeAPI#runProductCommand} for manipulation of a device behind the bridge (i.e. instructing to + * modify a position),
  • + *
  • {@link JsonBridgeAPI#runProductDiscovery} for activation of learning mode of the bridge to discovery new + * products,
  • + *
  • {@link JsonBridgeAPI#runProductIdentification} for human-oriented identification a device behind the bridge (i.e. + * by winking or switching on-and-off),
  • + *
  • {@link JsonBridgeAPI#runProductSearch} for searching for lost products on the bridge,
  • + *
  • {@link JsonBridgeAPI#runScene} for manipulation of a set of devices behind the bridge which are tied together as + * scene,
  • + *
  • {@link JsonBridgeAPI#setHouseStatusMonitor} for activation or deactivation of the house monitoring mode to be + * informed about device state changes recognized by the bridge,
  • + *
  • {@link JsonBridgeAPI#setSceneVelocity} for changes the velocity of a scene defined on the bridge (i.e. silent or + * fast mode).
  • + *
+ *

+ * As most derived class of the several inheritance levels it defines an + * interfacing method which returns the JSON-protocol-specific communication for gateway interaction. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JsonBridgeAPI implements BridgeAPI { + private final Logger logger = LoggerFactory.getLogger(JsonBridgeAPI.class); + + private static final GetDeviceStatus GETDEVICESTATUS = new JCgetDeviceStatus(); + private static final GetFirmware GETFIRMWARE = new JCgetFirmware(); + private static final GetLANConfig GETLANCONFIG = new JCgetLANConfig(); + private static final GetProducts GETPRODUCTS = new JCgetProducts(); + private static final GetScenes GETSCENES = new JCgetScenes(); + private static final GetWLANConfig GETWLANCONFIG = new JCgetWLANConfig(); + private static final Login LOGIN = new JClogin(); + private static final Logout LOGOUT = new JClogout(); + private static final RunProductDiscovery RUNPRODUCTDISCOVERY = new JCrunProductDiscovery(); + private static final RunProductIdentification RUNPRODUCTIDENTIFICATION = new JCrunProductIdentification(); + private static final RunProductSearch RUNPRODUCTSEARCH = new JCrunProductSearch(); + private static final RunScene RUNSCENE = new JCrunScene(); + private static final SetSceneVelocity SETSCENEVELOCITY = new JCsetSceneVelocity(); + + /** + * Constructor. + *

+ * Inherits the initialization of the binding-wide instance for dealing for common information and + * initializes the handler {@link org.openhab.binding.velux.bridge.json.JsonVeluxBridge#bridgeAPI + * JsonVeluxBridge.bridgeAPI} + * to pass the interface methods. + * + * @param bridgeInstance refers to the binding-wide instance for dealing for common informations. + */ + JsonBridgeAPI(VeluxBridgeInstance bridgeInstance) { + logger.trace("JsonBridgeAPI(constructor) called."); + } + + @Override + public GetDeviceStatus getDeviceStatus() { + return GETDEVICESTATUS; + } + + @Override + public GetFirmware getFirmware() { + return GETFIRMWARE; + } + + @Override + public @Nullable GetHouseStatus getHouseStatus() { + return null; + } + + @Override + public GetLANConfig getLANConfig() { + return GETLANCONFIG; + } + + @Override + public @Nullable GetProduct getProduct() { + return null; + } + + @Override + public @Nullable GetProductLimitation getProductLimitation() { + return null; + } + + @Override + public GetProducts getProducts() { + return GETPRODUCTS; + } + + @Override + public GetScenes getScenes() { + return GETSCENES; + } + + @Override + public GetWLANConfig getWLANConfig() { + return GETWLANCONFIG; + } + + @Override + public Login login() { + return LOGIN; + } + + @Override + public Logout logout() { + return LOGOUT; + } + + @Override + public @Nullable RunProductCommand runProductCommand() { + return null; + } + + @Override + public RunProductDiscovery runProductDiscovery() { + return RUNPRODUCTDISCOVERY; + } + + @Override + public RunProductIdentification runProductIdentification() { + return RUNPRODUCTIDENTIFICATION; + } + + @Override + public RunProductSearch runProductSearch() { + return RUNPRODUCTSEARCH; + } + + @Override + public RunScene runScene() { + return RUNSCENE; + } + + @Override + public @Nullable SetHouseStatusMonitor setHouseStatusMonitor() { + return null; + } + + @Override + public SetSceneVelocity setSceneVelocity() { + return SETSCENEVELOCITY; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JsonBridgeCommunicationProtocol.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JsonBridgeCommunicationProtocol.java new file mode 100644 index 0000000000000..6225240a8e6f0 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JsonBridgeCommunicationProtocol.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.json; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.BridgeCommunicationProtocol; + +/** + * Common JSON-based bridge communication message scheme supported by the Velux bridge. + *

+ * This bridge communication is an extension of the common + * {@link org.openhab.binding.velux.bridge.common.BridgeCommunicationProtocol BridgeCommunicationProtocol}. + *

+ * Message semantic will be defined by the implementation of the separate message classes, + * which are defined within {@link org.openhab.binding.velux.bridge.json.JsonBridgeAPI JsonBridgeAPI}. + *

+ * The implementations will define the information which to send query and receive answer + * through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider}. + *

+ * (Methods in this interface for the appropriate interaction: + *

    + *
  • {@link #getURL} to return the URL suffix for accessing the specific service access point.
  • + *
  • {@link #getObjectOfRequest} to return the request object for further JSON serialization.
  • + *
  • {@link #getClassOfResponse} to retrieve the class of the object of response message for further JSON + * deserialization.
  • + *
  • {@link #setResponse} for storing the response according to the desired class after JSON deserialization.
  • + *
  • {@link #getDeviceStatus} to retrieve the current device status.
  • + *
  • {@link #getErrors} to retrieve the current error status.
  • + *
+ * + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +interface JsonBridgeCommunicationProtocol extends BridgeCommunicationProtocol { + + /** + * Returning the URL suffix for accessing the specific service access point. + * + * @return sapURL + * as String which is to be combined with the bridge address. + */ + String getURL(); + + /** + * Returning the request object for further JSON serialization. + * + * @return ObjectOfRequest + * is an Object. + */ + Object getObjectOfRequest(); + + /** + * Returning the class of the object of response message for further JSON deserialization. + * + * @return ClassOfResponseObject + * is the appropriate class Object. + */ + Class getClassOfResponse(); + + /** + * Storing the response according to the desired class after JSON deserialization. + * + * @param response is the appropriate object of previously given class Object. + */ + void setResponse(Object response); + + /** + * Returning the communication status included within the response message. + * + * @return deviceStatus as String describing the current status of the bridge. + */ + String getDeviceStatus(); + + /** + * Returning the communication status included within the response message. + * + * @return errors as String[] describing the status of the operation according to the request in depth. + */ + String[] getErrors(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JsonVeluxBridge.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JsonVeluxBridge.java new file mode 100644 index 0000000000000..72cb26b4f65ba --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JsonVeluxBridge.java @@ -0,0 +1,320 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.json; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.TreeSet; + +import org.apache.commons.io.IOUtils; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.VeluxBridge; +import org.openhab.binding.velux.bridge.VeluxBridgeInstance; +import org.openhab.binding.velux.bridge.common.BridgeAPI; +import org.openhab.binding.velux.bridge.common.BridgeCommunicationProtocol; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; + +/** + * JSON-based 2nd Level I/O interface towards the Velux bridge. + *

+ * It provides methods for pre- and postcommunication + * as well as a common method for the real communication. + *

+ * The following class access methods exist: + *

    + *
  • {@link VeluxBridge#bridgeLogin} for pre-communication,
  • + *
  • {@link VeluxBridge#bridgeLogout} for post-communication,
  • + *
  • {@link VeluxBridge#bridgeCommunicate} as method for the common communication.
  • + *
+ *

+ * As root of several inheritance levels it predefines an + * interfacing method {@link VeluxBridge#bridgeAPI} which + * has to be implemented by any kind of protocol-specific + * communication returning the appropriate base (1st) level + * communication method as well as any other gateway + * interaction. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public class JsonVeluxBridge extends VeluxBridge { + private final Logger logger = LoggerFactory.getLogger(JsonVeluxBridge.class); + + /** + * Timestamp of last communication in milliseconds. + * + */ + private long lastCommunicationInMSecs = 0; + + /** + * Timestamp of last successful communication in milliseconds. + * + */ + private long lastSuccessfulCommunicationInMSecs = 0; + + /** + * Handler passing the interface methods to other classes. + * Can be accessed via method {@link org.openhab.binding.velux.bridge.common.BridgeAPI BridgeAPI}. + * + */ + private BridgeAPI bridgeAPI; + + /** + * Constructor. + *

+ * Inherits the initialization of the binding-wide instance for dealing for common informations and + * initializes the Velux bridge connectivity settings. + * + * @param bridgeInstance refers to the binding-wide instance for dealing for common informations. + */ + public JsonVeluxBridge(VeluxBridgeInstance bridgeInstance) { + super(bridgeInstance); + logger.trace("JsonVeluxBridge(constructor) called."); + bridgeAPI = new JsonBridgeAPI(bridgeInstance); + supportedProtocols = new TreeSet(); + supportedProtocols.add("http"); + supportedProtocols.add("https"); + logger.trace("JsonVeluxBridge(constructor) done."); + } + + /** + * Provides information about the base-level communication method and + * any kind of available gateway interactions. + *

+ * Note: the implementation within this class {@link JsonVeluxBridge} as inherited from {@link VeluxBridge} + * will return the protocol-specific class implementations. + *

+ * The information will be initialized by the corresponding API class {@link JsonBridgeAPI}. + * + * @return bridgeAPI of type {@link BridgeAPI} contains all possible methods. + */ + @Override + public BridgeAPI bridgeAPI() { + logger.trace("bridgeAPI() called."); + return bridgeAPI; + } + + /** + * Method as implementation of abstract superclass method. + *

+ * Initializes a client/server communication towards Velux veluxBridge + * based on the Basic I/O interface {@link #io} and parameters + * passed as arguments (see below). + * + * @param communication Structure of interface type {@link JsonBridgeCommunicationProtocol} describing the intended + * communication, that is request and response interactions as well as appropriate URL definition. + * @param useAuthentication boolean flag to decide whether to use authenticated communication. + * @return success of type boolean which signals the success of the communication. + * + */ + @Override + protected boolean bridgeDirectCommunicate(BridgeCommunicationProtocol communication, boolean useAuthentication) { + logger.trace("bridgeDirectCommunicate(BCP: {},{}authenticated) called.", communication.name(), + useAuthentication ? "" : "un"); + return bridgeDirectCommunicate((JsonBridgeCommunicationProtocol) communication, useAuthentication); + } + + /** + * Returns the timestamp in milliseconds since Unix epoch + * of last (potentially faulty) communication. + * + * @return timestamp in milliseconds. + */ + @Override + public synchronized long lastCommunication() { + return lastCommunicationInMSecs; + } + + /** + * Returns the timestamp in milliseconds since Unix epoch + * of last successful communication. + * + * @return timestamp in milliseconds. + */ + @Override + public synchronized long lastSuccessfulCommunication() { + return lastSuccessfulCommunicationInMSecs; + } + + /** + * Initializes a client/server communication towards Velux veluxBridge + * based on the Basic I/O interface {@link VeluxBridge} and parameters + * passed as arguments (see below). + * + * @param communication Structure of interface type {@link JsonBridgeCommunicationProtocol} describing the + * intended + * communication, + * that is request and response interactions as well as appropriate URL definition. + * @param useAuthentication boolean flag to decide whether to use authenticated communication. + * @return response of type boolean will indicate the success of the communication. + */ + private synchronized boolean bridgeDirectCommunicate(JsonBridgeCommunicationProtocol communication, + boolean useAuthentication) { + logger.trace("bridgeDirectCommunicate({},{}authenticated) called.", communication.name(), + useAuthentication ? "" : "un"); + + String sapURL = this.bridgeInstance.veluxBridgeConfiguration().protocol.concat("://") + .concat(this.bridgeInstance.veluxBridgeConfiguration().ipAddress).concat(":") + .concat(Integer.toString(this.bridgeInstance.veluxBridgeConfiguration().tcpPort)) + .concat(communication.getURL()); + logger.trace("bridgeCommunicate(): using SAP {}.", sapURL); + Object getRequest = communication.getObjectOfRequest(); + Class classOfResponse = communication.getClassOfResponse(); + Object response; + + try { + if (useAuthentication) { + response = ioAuthenticated(sapURL, authenticationToken, getRequest, classOfResponse); + } else { + response = ioUnauthenticated(sapURL, getRequest, classOfResponse); + } + communication.setResponse(response); + logger.trace("bridgeCommunicate(): communication result is {}, returning details.", + communication.isCommunicationSuccessful()); + return true; + } catch (IOException ioe) { + logger.warn("bridgeCommunicate(): Exception occurred on accessing {}: {}.", sapURL, ioe.getMessage()); + return false; + } catch (JsonSyntaxException jse) { + logger.warn("bridgeCommunicate(): Exception occurred on (de-)serialization during accessing {}: {}.", + sapURL, jse.getMessage()); + return false; + } + } + + /** + * Base level communication with the Velux bridge. + * + * @param url as String describing the Service Access Point location i.e. http://localhost/api . + * @param authentication as String providing the Authentication token to be passed with the request header. + * @param Request as Object representing the structure of the message request body to be converted into + * JSON. + * @param classOfResponse as Class representing the expected structure of the message response body to be converted + * from JSON. + * @return response of type Object containing all resulting informations, i.e. device status, errors a.s.o. + * Will + * return + * null in case of communication or decoding error. + * @throws java.io.IOException in case of continuous communication I/O failures. + * @throws JsonSyntaxException in case of unusual communication failures. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + private Object io(String url, String authentication, Object Request, Class classOfResponse) + throws JsonSyntaxException, IOException { + /** Local handles */ + int retryCount = 0; + IOException lastIOE; + + lastCommunicationInMSecs = System.currentTimeMillis(); + do { + try { + Gson gson = new Gson(); + String jsonRequest = gson.toJson(Request); + logger.trace("io() to {} using request {}.", url, jsonRequest); + + Properties headerItems = new Properties(); + if (authentication.length() > 0) { + headerItems.setProperty("Authorization", String.format("Bearer %s", authentication)); + } + InputStream content = IOUtils.toInputStream(jsonRequest, "UTF-8"); + + String jsonResponse = org.eclipse.smarthome.io.net.http.HttpUtil.executeUrl("PUT", url, headerItems, + content, "application/json", this.bridgeInstance.veluxBridgeConfiguration().timeoutMsecs); + if (jsonResponse == null) { + throw new IOException("transport error"); + } + logger.trace("io(): wait time {} msecs.", this.bridgeInstance.veluxBridgeConfiguration().timeoutMsecs); + // Give the bridge some time to breathe + try { + Thread.sleep(this.bridgeInstance.veluxBridgeConfiguration().timeoutMsecs); + } catch (InterruptedException ie) { + logger.trace("io() wait interrupted."); + } + + logger.trace("io() got response {}.", jsonResponse.replaceAll("\\p{C}", ".")); + jsonResponse = jsonResponse.replaceAll("^.+,\n", ""); + logger.trace("io() cleaned response {}.", jsonResponse); + try { + Object response = gson.fromJson(jsonResponse, classOfResponse); + lastCommunicationInMSecs = lastSuccessfulCommunicationInMSecs = System.currentTimeMillis(); + return response; + } catch (JsonSyntaxException jse) { + logger.info("io(): Exception occurred on deserialization: {}, aborting.", jse.getMessage()); + throw jse; + } + } catch (IOException ioe) { + logger.trace("io(): Exception occurred during I/O: {}.", ioe.getMessage()); + lastIOE = ioe; + // Error Retries with Exponential Backoff + long waitTime = ((long) Math.pow(2, retryCount) + * this.bridgeInstance.veluxBridgeConfiguration().timeoutMsecs); + logger.trace("io(): wait time {} msecs.", waitTime); + try { + Thread.sleep(waitTime); + } catch (InterruptedException ie) { + logger.trace("io() wait interrupted."); + } + } + } while (retryCount++ < this.bridgeInstance.veluxBridgeConfiguration().retries); + logger.info("io(): socket I/O failed ({} times).", this.bridgeInstance.veluxBridgeConfiguration().retries); + throw lastIOE; + } + + /** + * Initializes an authenticated communication with the {@link JsonVeluxBridge Velux bridge}. + * + * @param url as String describing the Service Access Point location i.e. http://localhost/api . + * @param authentication as String providing the Authentication token to be passed with the request header. + * @param Request as Object representing the structure of the message request body to be converted into + * JSON. + * @param classOfResponse as Class representing the expected structure of the message response body to be converted + * from JSON. + * @return response of type T containing all resulting informations, i.e. device status, errors a.s.o. Will + * return + * null in case of communication or decoding error. + * @throws java.io.IOException in case of continuous communication I/O failures. + * @throws JsonSyntaxException in case of unusual communication failures. + */ + @SuppressWarnings("rawtypes") + private Object ioAuthenticated(String url, String authentication, Object Request, Class classOfResponse) + throws JsonSyntaxException, IOException { + return io(url, authentication, Request, classOfResponse); + } + + /** + * Initializes an unauthenticated communication with the {@link JsonVeluxBridge Velux bridge}. + * + * @param url as String describing the Service Access Point location i.e. http://localhost/api . + * @param Request as Object representing the structure of the message request body to be converted into + * JSON. + * @param classOfResponse as Class representing the expected structure of the message response body to be converted + * from JSON. + * @return response of type Object containing all resulting informations, i.e. device status, errors a.s.o. + * Will + * return + * null in case of communication or decoding error. + * @throws java.io.IOException in case of continuous communication I/O failures. + * @throws JsonSyntaxException in case of unusual communication failures. + */ + @SuppressWarnings("rawtypes") + private Object ioUnauthenticated(String url, Object Request, Class classOfResponse) + throws JsonSyntaxException, IOException { + return io(url, "", Request, classOfResponse); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/package-info.java new file mode 100644 index 0000000000000..524662922782c --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/package-info.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2010-2019 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 + */ +/** + * JSON-protocol specific implementations of the interactions to IO-homecontrolled devices. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.bridge.json; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/package-info.java new file mode 100644 index 0000000000000..cad2e9548328b --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/package-info.java @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2010-2019 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 + */ +/** + * Classes for Generic protocol-independent interactions to IO-homecontrolled devices via the Velux gateway. + * This layer is responsible for the transformation of openHAB "objects" into Velux "object" and vice versa. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.bridge; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetDeviceStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetDeviceStatus.java new file mode 100644 index 0000000000000..9da451bba237f --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetDeviceStatus.java @@ -0,0 +1,152 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.GetDeviceStatus; +import org.openhab.binding.velux.bridge.slip.util.KLF200Response; +import org.openhab.binding.velux.bridge.slip.util.Packet; +import org.openhab.binding.velux.things.VeluxGwState; +import org.openhab.binding.velux.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Get Bridge Device Status + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #getState} to retrieve the Velux gateway status.
  • + *
+ * + * @see GetDeviceStatus + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCgetDeviceStatus extends GetDeviceStatus implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCgetDeviceStatus.class); + + private static final String DESCRIPTION = "Get Bridge Device Status"; + private static final Command COMMAND = Command.GW_GET_STATE_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private int cfmGatewayState; + private int cfmSubState; + @SuppressWarnings("unused") + private int cfmStateData; + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Methods required for interface {@link SlipBridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + logger.trace("getRequestDataAsArrayOfBytes() returns data."); + requestData = new byte[0]; + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_GET_STATE_CFM: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 6)) { + finished = true; + break; + } + cfmGatewayState = responseData.getOneByteValue(0); + cfmSubState = responseData.getOneByteValue(1); + cfmStateData = responseData.getFourByteValue(2); + finished = true; + success = true; + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + finished = true; + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public VeluxGwState getState() { + VeluxGwState thisGwState = new VeluxGwState((byte) cfmGatewayState, (byte) cfmSubState); + logger.trace("getState() returns {} ({}).", thisGwState, thisGwState.toDescription()); + return thisGwState; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetFirmware.java new file mode 100644 index 0000000000000..45532b8d3b2eb --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetFirmware.java @@ -0,0 +1,165 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.GetFirmware; +import org.openhab.binding.velux.bridge.slip.util.KLF200Response; +import org.openhab.binding.velux.bridge.slip.util.Packet; +import org.openhab.binding.velux.things.VeluxGwFirmware; +import org.openhab.binding.velux.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Get Firmware Version + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #getFirmware} to retrieve the Velux firmware version.
  • + *
+ * + * @see GetFirmware + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCgetFirmware extends GetFirmware implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCgetFirmware.class); + + private static final String DESCRIPTION = "Retrieve firmware version"; + private static final Command COMMAND = Command.GW_GET_VERSION_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private int cfmSoftwareVersionCommand = 0; + private int cfmSoftwareVersionWhole = 0; + private int cfmSoftwareVersionSub = 0; + private int cfmSoftwareVersionBranch = 0; + private int cfmSoftwareVersionBuild = 0; + private int cfmSoftwareVersionMicroBuild = 0; + private int cfmHardwareVersion = 0; + private int cfmProductGroup = 0; + private int cfmProductType = 0; + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Methods required for interface {@link SlipBridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + requestData = new byte[1]; + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_GET_VERSION_CFM: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 9)) { + finished = true; + break; + } + cfmSoftwareVersionCommand = responseData.getOneByteValue(0); + cfmSoftwareVersionWhole = responseData.getOneByteValue(1); + cfmSoftwareVersionSub = responseData.getOneByteValue(2); + cfmSoftwareVersionBranch = responseData.getOneByteValue(3); + cfmSoftwareVersionBuild = responseData.getOneByteValue(4); + cfmSoftwareVersionMicroBuild = responseData.getOneByteValue(5); + cfmHardwareVersion = responseData.getOneByteValue(6); + cfmProductGroup = responseData.getOneByteValue(7); + cfmProductType = responseData.getOneByteValue(8); + success = true; + finished = true; + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + finished = true; + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public VeluxGwFirmware getFirmware() { + String result = String.format("Software version %d.%d.%d.%d.%d.%d, Hardware version %d.%d.%d", + cfmSoftwareVersionCommand, cfmSoftwareVersionWhole, cfmSoftwareVersionSub, cfmSoftwareVersionBranch, + cfmSoftwareVersionBuild, cfmSoftwareVersionMicroBuild, cfmHardwareVersion, cfmProductGroup, + cfmProductType); + logger.trace("getFirmware() returns {}.", result); + return new VeluxGwFirmware(result); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetHouseStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetHouseStatus.java new file mode 100644 index 0000000000000..42d76696d1050 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetHouseStatus.java @@ -0,0 +1,184 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.BridgeCommunicationProtocol; +import org.openhab.binding.velux.bridge.common.GetHouseStatus; +import org.openhab.binding.velux.bridge.slip.util.KLF200Response; +import org.openhab.binding.velux.bridge.slip.util.Packet; +import org.openhab.binding.velux.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Retrieve House Status + *

+ * Common Message semantic: Communication from the bridge and storing returned information within the class itself. + *

+ * As 3rd level class it defines informations how to receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #getNtfNodeID} to retrieve the node identifier which has been changed.
  • + *
  • {@link #getNtfState} to retrieve the state of the node which has been changed.
  • + *
  • {@link #getNtfCurrentPosition} to retrieve the actual position of this node.
  • + *
  • {@link #getNtfTarget} to retrieve the target position of this node.
  • + *
+ *

+ * NOTE: the class does NOT define a request as it only works as receiver. + * + * @see BridgeCommunicationProtocol + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCgetHouseStatus extends GetHouseStatus implements BridgeCommunicationProtocol, SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCgetHouseStatus.class); + + private static final String DESCRIPTION = "Retrieve House Status"; + private static final Command COMMAND = Command.GW_OPENHAB_RECEIVEONLY; + + /* + * =========================================================== + * Message Objects + */ + + @SuppressWarnings("unused") + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + private int ntfNodeID; + private int ntfState; + private int ntfCurrentPosition; + private int ntfTarget; + + /* + * =========================================================== + * Methods required for interface {@link SlipBridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + logger.debug("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + return new byte[0]; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = true; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_NODE_STATE_POSITION_CHANGED_NTF: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 20)) { + break; + } + ntfNodeID = responseData.getOneByteValue(0); + ntfState = responseData.getOneByteValue(1); + ntfCurrentPosition = responseData.getTwoByteValue(2); + ntfTarget = responseData.getTwoByteValue(4); + @SuppressWarnings("unused") + int ntfFP1CurrentPosition = responseData.getTwoByteValue(6); + @SuppressWarnings("unused") + int ntfFP2CurrentPosition = responseData.getTwoByteValue(8); + @SuppressWarnings("unused") + int ntfFP3CurrentPosition = responseData.getTwoByteValue(10); + @SuppressWarnings("unused") + int ntfFP4CurrentPosition = responseData.getTwoByteValue(12); + int ntfRemainingTime = responseData.getTwoByteValue(14); + int ntfTimeStamp = responseData.getFourByteValue(16); + // Extracting information items + logger.trace("setResponse(): ntfNodeID={}.", ntfNodeID); + logger.trace("setResponse(): ntfState={}.", ntfState); + logger.trace("setResponse(): ntfCurrentPosition={}.", ntfCurrentPosition); + logger.trace("setResponse(): ntfTarget={}.", ntfTarget); + logger.trace("setResponse(): ntfRemainingTime={}.", ntfRemainingTime); + logger.trace("setResponse(): ntfTimeStamp={}.", ntfTimeStamp); + success = true; + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return true; + } + + @Override + public boolean isCommunicationSuccessful() { + return true; + } + + /* + * =========================================================== + * Methods in addition to the interface {@link BridgeCommunicationProtocol} + */ + + /** + * @return ntfNodeID returns the Actuator Id as int. + */ + public int getNtfNodeID() { + return ntfNodeID; + } + + /** + * @return ntfState returns the state of the Actuator as int. + */ + public int getNtfState() { + return ntfState; + } + + /** + * @return ntfCurrentPosition returns the current position of the Actuator as int. + */ + public int getNtfCurrentPosition() { + return ntfCurrentPosition; + } + + /** + * @return ntfTarget returns the target position of the Actuator as int. + */ + public int getNtfTarget() { + return ntfTarget; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetLANConfig.java new file mode 100644 index 0000000000000..583d2196b3d22 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetLANConfig.java @@ -0,0 +1,153 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.GetLANConfig; +import org.openhab.binding.velux.bridge.slip.util.KLF200Response; +import org.openhab.binding.velux.bridge.slip.util.Packet; +import org.openhab.binding.velux.things.VeluxGwLAN; +import org.openhab.binding.velux.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Retrieve LAN configuration + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #getLANConfig} to retrieve the current LAN configuration.
  • + *
+ * + * @see GetLANConfig + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCgetLANConfig extends GetLANConfig implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCgetLANConfig.class); + + private static final String DESCRIPTION = "Retrieve LAN configuration"; + private static final Command COMMAND = Command.GW_GET_NETWORK_SETUP_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private int cfmIpAddress; + private int cfmMask; + private int cfmDefGW; + private boolean cfmDHCP; + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Methods required for interface {@link SlipBridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + requestData = new byte[1]; + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_GET_NETWORK_SETUP_CFM: + finished = true; + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 13)) { + break; + } + cfmIpAddress = responseData.getFourByteValue(0); + cfmMask = responseData.getFourByteValue(4); + cfmDefGW = responseData.getFourByteValue(8); + cfmDHCP = responseData.getOneByteValue(12) == 0 ? false : true; + success = true; + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + finished = true; + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public VeluxGwLAN getLANConfig() { + logger.trace("getLANConfig() called."); + VeluxGwLAN result = new VeluxGwLAN(Packet.intToIPAddressString(cfmIpAddress), + Packet.intToIPAddressString(cfmMask), Packet.intToIPAddressString(cfmDefGW), cfmDHCP); + logger.debug("getLANConfig() returns {}.", result); + return result; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetLimitation.java new file mode 100644 index 0000000000000..6b8dc3ba88916 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetLimitation.java @@ -0,0 +1,234 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip; + +import java.util.Random; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.GetProductLimitation; +import org.openhab.binding.velux.bridge.slip.util.KLF200Response; +import org.openhab.binding.velux.bridge.slip.util.Packet; +import org.openhab.binding.velux.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Retrieve Product + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #setActuatorIdAndLimitationType(int,boolean)} to define the one specific product.
  • + *
+ * + * @see GetProductLimitation + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCgetLimitation extends GetProductLimitation implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCgetLimitation.class); + + private static final String DESCRIPTION = "Retrieve Actuator Limitation"; + private static final Command COMMAND = Command.GW_GET_LIMITATION_STATUS_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private int reqSessionID = 0; + private int reqCommandOriginator = 8; // SAAC + private int reqPriorityLevel = 5; // Comfort Level 2 + private int reqIndexArrayCount = 1; // One node will be addressed + private int reqIndexArray01 = 1; // This is the node + private int reqParameterID = 0; // MP = Main parameter + private int reqLimitationType = 0; // Resulting minimum limitation. 1= maximum. + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Constructor Method + */ + + public SCgetLimitation() { + logger.debug("SCgetLimitation(Constructor) called."); + Random rand = new Random(); + reqSessionID = rand.nextInt(0x0fff); + logger.debug("SCgetLimitation(): starting sessions with the random number {}.", reqSessionID); + } + + /* + * =========================================================== + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + Packet request = new Packet(new byte[27]); + reqSessionID = (reqSessionID + 1) & 0xffff; + request.setTwoByteValue(0, reqSessionID); + request.setOneByteValue(2, reqCommandOriginator); + request.setOneByteValue(3, reqPriorityLevel); + request.setOneByteValue(4, reqIndexArrayCount); + request.setOneByteValue(5, reqIndexArray01); + request.setOneByteValue(25, reqParameterID); + request.setOneByteValue(26, reqLimitationType); + logger.trace("getRequestDataAsArrayOfBytes(): ntfSessionID={}.", reqSessionID); + logger.trace("getRequestDataAsArrayOfBytes(): reqCommandOriginator={}.", reqCommandOriginator); + logger.trace("getRequestDataAsArrayOfBytes(): reqPriorityLevel={}.", reqPriorityLevel); + logger.trace("getRequestDataAsArrayOfBytes(): reqIndexArrayCount={}.", reqIndexArrayCount); + logger.trace("getRequestDataAsArrayOfBytes(): reqIndexArray01={}.", reqIndexArray01); + logger.trace("getRequestDataAsArrayOfBytes(): reqParameterID={}.", reqParameterID); + logger.trace("getRequestDataAsArrayOfBytes(): reqLimitationType={}.", reqLimitationType); + requestData = request.toByteArray(); + logger.trace("getRequestDataAsArrayOfBytes() data is {}.", new Packet(requestData).toString()); + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_GET_LIMITATION_STATUS_CFM: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 3)) { + finished = true; + break; + } + int cfmSessionID = responseData.getTwoByteValue(0); + int cfmStatus = responseData.getOneByteValue(2); + switch (cfmStatus) { + case 0: + logger.info("setResponse(): returned status: Error – Command rejected."); + finished = true; + break; + case 1: + logger.debug("setResponse(): returned status: OK - Command is accepted."); + if (!KLF200Response.check4matchingSessionID(logger, cfmSessionID, reqSessionID)) { + finished = true; + } + break; + default: + logger.warn("setResponse(): returned status={} (not defined).", cfmStatus); + finished = true; + break; + } + if (!isSequentialEnforced) { + finished = true; + } + break; + + case GW_LIMITATION_STATUS_NTF: + finished = true; + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 10)) { + break; + } + // Extracting information items + int ntfSessionID = responseData.getTwoByteValue(0); + int ntfNodeID = responseData.getOneByteValue(2); + int ntfParameterID = responseData.getOneByteValue(3); + int ntfMinValue = responseData.getTwoByteValue(4); + int ntfMaxValue = responseData.getTwoByteValue(6); + int ntfLimitationOriginator = responseData.getOneByteValue(8); + int ntfLimitationTime = responseData.getOneByteValue(9); + + if (!KLF200Response.check4matchingSessionID(logger, ntfSessionID, reqSessionID)) { + finished = true; + break; + } + + logger.info("setResponse(): nodeId={}.", ntfNodeID); + logger.info("setResponse(): ntfParameterID={}.", ntfParameterID); + logger.info("setResponse(): ntfMinValue={}.", ntfMinValue); + logger.info("setResponse(): ntfMaxValue={}.", ntfMaxValue); + logger.info("setResponse(): ntfLimitationOriginator={}.", ntfLimitationOriginator); + logger.info("setResponse(): ntfLimitationTime={}.", ntfLimitationTime); + + success = true; + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to the interface {@link BridgeCommunicationProtocol} + * and the abstract class {@link GetProductLimitation} + */ + + @Override + public void setActuatorIdAndLimitationType(int nodeId, boolean limitationMinimum) { + logger.trace("setProductId({},{}) called.", nodeId, limitationMinimum); + this.reqIndexArray01 = nodeId; + this.reqLimitationType = limitationMinimum ? 0 : 1; + return; + } + + @Override + public int getLimitation() { + return 0; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetProduct.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetProduct.java new file mode 100644 index 0000000000000..4d948c76aa1b9 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetProduct.java @@ -0,0 +1,268 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.GetProduct; +import org.openhab.binding.velux.bridge.slip.util.KLF200Response; +import org.openhab.binding.velux.bridge.slip.util.Packet; +import org.openhab.binding.velux.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.things.VeluxProduct; +import org.openhab.binding.velux.things.VeluxProduct.ProductBridgeIndex; +import org.openhab.binding.velux.things.VeluxProductName; +import org.openhab.binding.velux.things.VeluxProductSerialNo; +import org.openhab.binding.velux.things.VeluxProductType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Retrieve Product + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #setProductId(int)} to define the one specific product.
  • + *
  • {@link #getProduct} to retrieve one specific product.
  • + *
+ * + * @see GetProduct + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCgetProduct extends GetProduct implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCgetProduct.class); + + private static final String DESCRIPTION = "Retrieve Product"; + private static final Command COMMAND = Command.GW_GET_NODE_INFORMATION_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private int reqNodeID; + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + private VeluxProduct product = VeluxProduct.UNKNOWN; + + /* + * =========================================================== + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + logger.trace("getRequestDataAsArrayOfBytes() returns data for retrieving node with id {}.", reqNodeID); + Packet request = new Packet(new byte[1]); + request.setOneByteValue(0, reqNodeID); + requestData = request.toByteArray(); + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_GET_NODE_INFORMATION_CFM: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 2)) { + finished = true; + break; + } + int cfmStatus = responseData.getOneByteValue(0); + int cfmNodeID = responseData.getOneByteValue(1); + switch (cfmStatus) { + case 0: + logger.trace("setResponse(): returned status: OK - Request accepted."); + if (!KLF200Response.check4matchingNodeID(logger, reqNodeID, cfmNodeID)) { + finished = true; + } + break; + case 1: + finished = true; + logger.trace("setResponse(): returned status: Error – Request rejected."); + break; + case 2: + finished = true; + logger.trace("setResponse(): returned status: Error – Invalid node index."); + break; + default: + finished = true; + logger.warn("setResponse({}): returned status={} (Reserved/unknown).", + Command.get(responseCommand).toString(), cfmStatus); + break; + } + break; + + case GW_GET_NODE_INFORMATION_NTF: + finished = true; + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 124)) { + break; + } + // Extracting information items + int ntfNodeID = responseData.getOneByteValue(0); + logger.trace("setResponse(): ntfNodeID={}.", ntfNodeID); + int ntfOrder = responseData.getTwoByteValue(1); + logger.trace("setResponse(): ntfOrder={}.", ntfOrder); + int ntfPlacement = responseData.getOneByteValue(3); + logger.trace("setResponse(): ntfPlacement={}.", ntfPlacement); + String ntfName = responseData.getString(4, 64); + logger.trace("setResponse(): ntfName={}.", ntfName); + int ntfVelocity = responseData.getOneByteValue(68); + logger.trace("setResponse(): ntfVelocity={}.", ntfVelocity); + int ntfNodeTypeSubType = responseData.getTwoByteValue(69); + logger.trace("setResponse(): ntfNodeTypeSubType={} ({}).", ntfNodeTypeSubType, + VeluxProductType.get(ntfNodeTypeSubType)); + logger.trace("setResponse(): derived product description={}.", + VeluxProductType.toString(ntfNodeTypeSubType)); + int ntfProductGroup = responseData.getTwoByteValue(71); + logger.trace("setResponse(): ntfProductGroup={}.", ntfProductGroup); + int ntfProductType = responseData.getOneByteValue(72); + logger.trace("setResponse(): ntfProductType={}.", ntfProductType); + int ntfNodeVariation = responseData.getOneByteValue(73); + logger.trace("setResponse(): ntfNodeVariation={}.", ntfNodeVariation); + int ntfPowerMode = responseData.getOneByteValue(74); + logger.trace("setResponse(): ntfPowerMode={}.", ntfPowerMode); + int ntfBuildNumber = responseData.getOneByteValue(75); + logger.trace("setResponse(): ntfBuildNumber={}.", ntfBuildNumber); + byte[] ntfSerialNumber = responseData.getByteArray(76, 8); + logger.trace("setResponse(): ntfSerialNumber={}.", ntfSerialNumber); + int ntfState = responseData.getOneByteValue(84); + logger.trace("setResponse(): ntfState={}.", ntfState); + int ntfCurrentPosition = responseData.getTwoByteValue(85); + logger.trace("setResponse(): ntfCurrentPosition={}.", ntfCurrentPosition); + int ntfTarget = responseData.getTwoByteValue(87); + logger.trace("setResponse(): ntfTarget={}.", ntfTarget); + int ntfFP1CurrentPosition = responseData.getTwoByteValue(89); + logger.trace("setResponse(): ntfFP1CurrentPosition={}.", ntfFP1CurrentPosition); + int ntfFP2CurrentPosition = responseData.getTwoByteValue(91); + logger.trace("setResponse(): ntfFP2CurrentPosition={}.", ntfFP2CurrentPosition); + int ntfFP3CurrentPosition = responseData.getTwoByteValue(93); + logger.trace("setResponse(): ntfFP3CurrentPosition={}.", ntfFP3CurrentPosition); + int ntfFP4CurrentPosition = responseData.getTwoByteValue(95); + logger.trace("setResponse(): ntfFP4CurrentPosition={}.", ntfFP4CurrentPosition); + int ntfRemainingTime = responseData.getFourByteValue(97); + logger.trace("setResponse(): ntfRemainingTime={}.", ntfRemainingTime); + int ntfTimeStamp = responseData.getFourByteValue(99); + logger.trace("setResponse(): ntfTimeStamp={}.", ntfTimeStamp); + int ntfNbrOfAlias = responseData.getOneByteValue(103); + logger.trace("setResponse(): ntfNbrOfAlias={}.", ntfNbrOfAlias); + int ntfAliasOne = responseData.getFourByteValue(104); + logger.trace("setResponse(): ntfAliasOne={}.", ntfAliasOne); + int ntfAliasTwo = responseData.getFourByteValue(108); + logger.trace("setResponse(): ntfAliasTwo={}.", ntfAliasTwo); + int ntfAliasThree = responseData.getFourByteValue(112); + logger.trace("setResponse(): ntfAliasThree={}.", ntfAliasThree); + int ntfAliasFour = responseData.getFourByteValue(116); + logger.trace("setResponse(): ntfAliasFour={}.", ntfAliasFour); + int ntfAliasFive = responseData.getFourByteValue(120); + logger.trace("setResponse(): ntfAliasFive={}.", ntfAliasFive); + + if (!KLF200Response.check4matchingNodeID(logger, reqNodeID, ntfNodeID)) { + break; + } + + if (ntfName.length() == 0) { + ntfName = "#".concat(String.valueOf(ntfNodeID)); + logger.debug("setResponse(): device provided invalid name, using '{}' instead.", ntfName); + } + String commonSerialNumber = VeluxProductSerialNo.toString(ntfSerialNumber); + if (VeluxProductSerialNo.isInvalid(ntfSerialNumber)) { + commonSerialNumber = new String(ntfName); + logger.debug("setResponse(): device provided invalid serial number, using name '{}' instead.", + commonSerialNumber); + } + + product = new VeluxProduct(new VeluxProductName(ntfName), VeluxProductType.get(ntfNodeTypeSubType), + new ProductBridgeIndex(ntfNodeID), ntfOrder, ntfPlacement, ntfVelocity, ntfNodeVariation, + ntfPowerMode, commonSerialNumber, ntfState, ntfCurrentPosition, ntfTarget, ntfRemainingTime, + ntfTimeStamp); + success = true; + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to the interface {@link BridgeCommunicationProtocol} + * and the abstract class {@link GetProduct} + */ + + @Override + public void setProductId(int nodeId) { + logger.trace("setProductId({}) called.", nodeId); + this.reqNodeID = nodeId; + return; + } + + @Override + public VeluxProduct getProduct() { + logger.trace("getProduct(): returning product {}.", product); + return product; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetProducts.java new file mode 100644 index 0000000000000..1cf191c5c50ec --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetProducts.java @@ -0,0 +1,276 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.GetProducts; +import org.openhab.binding.velux.bridge.slip.util.KLF200Response; +import org.openhab.binding.velux.bridge.slip.util.Packet; +import org.openhab.binding.velux.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.things.VeluxProduct; +import org.openhab.binding.velux.things.VeluxProduct.ProductBridgeIndex; +import org.openhab.binding.velux.things.VeluxProductName; +import org.openhab.binding.velux.things.VeluxProductSerialNo; +import org.openhab.binding.velux.things.VeluxProductType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Retrieve Products + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #getProducts()} to retrieve the currently registered products.
  • + *
+ * + * @see GetProducts + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCgetProducts extends GetProducts implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCgetProducts.class); + + private static final String DESCRIPTION = "Retrieve Products"; + private static final Command COMMAND = Command.GW_GET_ALL_NODES_INFORMATION_REQ; + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + private VeluxProduct[] productArray = new VeluxProduct[0]; + private int totalNumberOfProducts = 0; + private int nextProductArrayItem = 0; + + /* + * =========================================================== + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + requestData = new byte[0]; + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_GET_ALL_NODES_INFORMATION_CFM: + logger.trace("setResponse(): got GW_GET_ALL_NODES_INFORMATION_CFM."); + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 2)) { + finished = true; + break; + } + int cfmStatus = responseData.getOneByteValue(0); + int cfmTotalNumberOfNodes = responseData.getOneByteValue(1); + logger.trace("setResponse(): status={}.", cfmStatus); + logger.trace("setResponse(): TotalNumberOfNodes={}.", cfmTotalNumberOfNodes); + + // Initialize storage area + productArray = new VeluxProduct[0]; + nextProductArrayItem = 0; + switch (cfmStatus) { + case 0: + logger.trace("setResponse(): returned status: OK - Request accepted."); + totalNumberOfProducts = cfmTotalNumberOfNodes; + productArray = new VeluxProduct[totalNumberOfProducts]; + break; + case 1: + logger.trace("setResponse(): returned status: Error – System table empty."); + finished = true; + break; + default: + finished = true; + logger.warn("setResponse({}): returned status={} (Reserved/unknown).", + Command.get(responseCommand).toString(), cfmStatus); + break; + } + break; + case GW_GET_ALL_NODES_INFORMATION_NTF: + logger.trace("setResponse(): got GW_GET_ALL_NODES_INFORMATION_NTF."); + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 124)) { + finished = true; + break; + } + if (productArray.length == 0) { + logger.warn("setResponse({}): sequence of answers unexpected.", + Command.get(responseCommand).toString()); + finished = true; + break; + } + // Extracting information items + int ntfNodeID = responseData.getOneByteValue(0); + logger.trace("setResponse(): ntfNodeID={}.", ntfNodeID); + int ntfOrder = responseData.getTwoByteValue(1); + logger.trace("setResponse(): ntfOrder={}.", ntfOrder); + int ntfPlacement = responseData.getOneByteValue(3); + logger.trace("setResponse(): ntfPlacement={}.", ntfPlacement); + String ntfName = responseData.getString(4, 64); + logger.trace("setResponse(): ntfName={}.", ntfName); + int ntfVelocity = responseData.getOneByteValue(68); + logger.trace("setResponse(): ntfVelocity={}.", ntfVelocity); + int ntfNodeTypeSubType = responseData.getTwoByteValue(69); + logger.trace("setResponse(): ntfNodeTypeSubType={} ({}).", ntfNodeTypeSubType, + VeluxProductType.get(ntfNodeTypeSubType)); + logger.trace("setResponse(): derived product description={}.", + VeluxProductType.toString(ntfNodeTypeSubType)); + int ntfProductGroup = responseData.getOneByteValue(71); + logger.trace("setResponse(): ntfProductGroup={}.", ntfProductGroup); + int ntfProductType = responseData.getOneByteValue(72); + logger.trace("setResponse(): ntfProductType={}.", ntfProductType); + int ntfNodeVariation = responseData.getOneByteValue(73); + logger.trace("setResponse(): ntfNodeVariation={}.", ntfNodeVariation); + int ntfPowerMode = responseData.getOneByteValue(74); + logger.trace("setResponse(): ntfPowerMode={}.", ntfPowerMode); + int ntfBuildNumber = responseData.getOneByteValue(75); + logger.trace("setResponse(): ntfBuildNumber={}.", ntfBuildNumber); + byte[] ntfSerialNumber = responseData.getByteArray(76, 8); + logger.trace("setResponse(): ntfSerialNumber={}.", ntfSerialNumber); + int ntfState = responseData.getOneByteValue(84); + logger.trace("setResponse(): ntfState={}.", ntfState); + int ntfCurrentPosition = responseData.getTwoByteValue(85); + logger.trace("setResponse(): ntfCurrentPosition={}.", ntfCurrentPosition); + int ntfTarget = responseData.getTwoByteValue(87); + logger.trace("setResponse(): ntfTarget={}.", ntfTarget); + int ntfFP1CurrentPosition = responseData.getTwoByteValue(89); + logger.trace("setResponse(): ntfFP1CurrentPosition={}.", ntfFP1CurrentPosition); + int ntfFP2CurrentPosition = responseData.getTwoByteValue(91); + logger.trace("setResponse(): ntfFP2CurrentPosition={}.", ntfFP2CurrentPosition); + int ntfFP3CurrentPosition = responseData.getTwoByteValue(93); + logger.trace("setResponse(): ntfFP3CurrentPosition={}.", ntfFP3CurrentPosition); + int ntfFP4CurrentPosition = responseData.getTwoByteValue(95); + logger.trace("setResponse(): ntfFP4CurrentPosition={}.", ntfFP4CurrentPosition); + int ntfRemainingTime = responseData.getTwoByteValue(97); + logger.trace("setResponse(): ntfRemainingTime={}.", ntfRemainingTime); + int ntfTimeStamp = responseData.getFourByteValue(99); + logger.trace("setResponse(): ntfTimeStamp={}.", ntfTimeStamp); + int ntfNbrOfAlias = responseData.getOneByteValue(103); + logger.trace("setResponse(): ntfNbrOfAlias={}.", ntfNbrOfAlias); + int ntfAliasOne = responseData.getFourByteValue(104); + logger.trace("setResponse(): ntfAliasOne={}.", ntfAliasOne); + int ntfAliasTwo = responseData.getFourByteValue(108); + logger.trace("setResponse(): ntfAliasTwo={}.", ntfAliasTwo); + int ntfAliasThree = responseData.getFourByteValue(112); + logger.trace("setResponse(): ntfAliasThree={}.", ntfAliasThree); + int ntfAliasFour = responseData.getFourByteValue(116); + logger.trace("setResponse(): ntfAliasFour={}.", ntfAliasFour); + int ntfAliasFive = responseData.getFourByteValue(120); + logger.trace("setResponse(): ntfAliasFive={}.", ntfAliasFive); + + if (ntfName.length() == 0) { + ntfName = "#".concat(String.valueOf(ntfNodeID)); + logger.debug("setResponse(): device provided invalid name, using '{}' instead.", ntfName); + } + + String commonSerialNumber = VeluxProductSerialNo.toString(ntfSerialNumber); + if (VeluxProductSerialNo.isInvalid(ntfSerialNumber)) { + commonSerialNumber = new String(ntfName); + logger.debug("setResponse(): device provided invalid serial number, using name '{}' instead.", + commonSerialNumber); + } + + VeluxProduct product = new VeluxProduct(new VeluxProductName(ntfName), + VeluxProductType.get(ntfNodeTypeSubType), new ProductBridgeIndex(ntfNodeID), ntfOrder, + ntfPlacement, ntfVelocity, ntfNodeVariation, ntfPowerMode, commonSerialNumber, ntfState, + ntfCurrentPosition, ntfTarget, ntfRemainingTime, ntfTimeStamp); + if (nextProductArrayItem < totalNumberOfProducts) { + productArray[nextProductArrayItem++] = product; + } else { + logger.warn("setResponse(): expected {} products, received one more, ignoring it.", + totalNumberOfProducts); + } + success = true; + break; + + case GW_GET_ALL_NODES_INFORMATION_FINISHED_NTF: + logger.trace("setResponse(): got GW_GET_ALL_NODES_INFORMATION_FINISHED_NTF."); + logger.debug("setResponse(): finished-packet received."); + success = true; + finished = true; + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + finished = true; + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to the interface {@link BridgeCommunicationProtocol} + * and the abstract class {@link GetProducts} + */ + + @Override + public VeluxProduct[] getProducts() { + if (success && finished) { + logger.trace("getProducts(): returning array of {} products.", productArray.length); + return productArray; + } else { + logger.trace("getProducts(): returning null."); + return new VeluxProduct[0]; + } + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetScenes.java new file mode 100644 index 0000000000000..e0cfb0cf8f9e5 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetScenes.java @@ -0,0 +1,175 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.GetScenes; +import org.openhab.binding.velux.bridge.slip.util.KLF200Response; +import org.openhab.binding.velux.bridge.slip.util.Packet; +import org.openhab.binding.velux.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.things.VeluxProductState; +import org.openhab.binding.velux.things.VeluxScene; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Retrieve Scenes + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #getScenes()} to retrieve the set of current scenes.
  • + *
+ * + * @see GetScenes + * @see SlipBridgeCommunicationProtocol + * + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCgetScenes extends GetScenes implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCgetScenes.class); + + private static final String DESCRIPTION = "Retrieve Scenes"; + private static final Command COMMAND = Command.GW_GET_SCENE_LIST_REQ; + + /* + * Message Objects + */ + + private boolean success; + private boolean finished; + + private int sceneIdx; + private VeluxScene[] scenes = new VeluxScene[0]; + + /* + * =========================================================== + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + return EMPTYDATA; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_GET_SCENE_LIST_CFM: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 1)) { + finished = true; + break; + } + int ntfTotalNumberOfObjects = responseData.getOneByteValue(0); + scenes = new VeluxScene[ntfTotalNumberOfObjects]; + if (ntfTotalNumberOfObjects == 0) { + logger.trace("setResponse(): no scenes defined."); + success = true; + finished = true; + } else { + logger.trace("setResponse(): {} scenes defined.", ntfTotalNumberOfObjects); + } + sceneIdx = 0; + break; + case GW_GET_SCENE_LIST_NTF: + if (thisResponseData.length < 1) { + logger.trace("setResponse(): malformed response packet (length is {} less than one).", + thisResponseData.length); + finished = true; + break; + } + int ntfNumberOfObject = responseData.getOneByteValue(0); + logger.trace("setResponse(): NTF number of objects={}.", ntfNumberOfObject); + if (ntfNumberOfObject == 0) { + logger.trace("setResponse(): finished."); + finished = true; + success = true; + } + if (thisResponseData.length != (2 + 65 * ntfNumberOfObject)) { + logger.trace("setResponse(): malformed response packet (real length {}, expected length {}).", + thisResponseData.length, (2 + 65 * ntfNumberOfObject)); + finished = true; + break; + } + for (int objectIndex = 0; objectIndex < ntfNumberOfObject; objectIndex++) { + int ntfSceneID = responseData.getOneByteValue(1 + 65 * objectIndex); + int beginOfString = 2 + 65 * objectIndex; + String ntfSceneName = responseData.getString(beginOfString, 64); + logger.trace("setResponse(): scene {}, name {}.", ntfSceneID, ntfSceneName); + scenes[sceneIdx++] = new VeluxScene(ntfSceneName, ntfSceneID, false, new VeluxProductState[0]); + } + int ntfRemainingNumberOfObject = responseData.getOneByteValue(1 + 65 * ntfNumberOfObject); + logger.trace("setResponse(): {} scenes remaining.", ntfRemainingNumberOfObject); + if (ntfRemainingNumberOfObject == 0) { + logger.trace("setResponse(): finished."); + finished = true; + success = true; + } + break; + default: + KLF200Response.errorLogging(logger, responseCommand); + finished = true; + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /** + * =========================================================== + *

+ * Public Methods required for abstract class {@link GetScenes}. + */ + @Override + public VeluxScene[] getScenes() { + logger.trace("getScenes(): returning {} scenes.", scenes.length); + return scenes; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetWLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetWLANConfig.java new file mode 100644 index 0000000000000..49db153eb4cfb --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetWLANConfig.java @@ -0,0 +1,124 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.bridge.common.GetWLANConfig; +import org.openhab.binding.velux.bridge.slip.util.Packet; +import org.openhab.binding.velux.things.VeluxGwWLAN; +import org.openhab.binding.velux.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Retrieve WLAN configuration + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #getWLANConfig} to retrieve the current WLAN configuration.
  • + *
+ * + * @see GetWLANConfig + * @see SlipBridgeCommunicationProtocol + * + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCgetWLANConfig extends GetWLANConfig implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCgetWLANConfig.class); + + private static final String DESCRIPTION = "Retrieve WLAN configuration"; + private static final Command COMMAND = Command.GW_GET_NETWORK_SETUP_REQ; + + private static final String UNSUPPORTED = "*** unsupported-by-current-gateway-firmware ***"; + + /* + * Message Objects + */ + + private byte[] requestData = new byte[0]; + private short responseCommand; + @SuppressWarnings("unused") + private byte @Nullable [] responseData; + + /* + * =========================================================== + * Constructor Method + */ + + public SCgetWLANConfig() { + logger.trace("SCgetWLANConfig(constructor) called."); + requestData = new byte[1]; + } + + /* + * =========================================================== + * Methods required for interface {@link SlipBridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + return requestData; + } + + @Override + public void setResponse(short thisResponseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + logger.trace("setResponseCommand({}, {}) called.", thisResponseCommand, new Packet(thisResponseData)); + responseCommand = thisResponseCommand; + responseData = thisResponseData; + } + + @Override + public boolean isCommunicationFinished() { + return true; + } + + @Override + public boolean isCommunicationSuccessful() { + return (responseCommand == Command.GW_GET_NETWORK_SETUP_CFM.getShort()); + } + + /* + * =========================================================== + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public VeluxGwWLAN getWLANConfig() { + logger.trace("getWLANConfig() called."); + // Enhancement idea: Velux should provide an enhanced API. + return new VeluxGwWLAN(UNSUPPORTED, UNSUPPORTED); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SClogin.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SClogin.java new file mode 100644 index 0000000000000..682a4769143cd --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SClogin.java @@ -0,0 +1,164 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.bridge.common.Login; +import org.openhab.binding.velux.bridge.slip.util.KLF200Response; +import org.openhab.binding.velux.bridge.slip.util.Packet; +import org.openhab.binding.velux.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Authenticate / login + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #setPassword(String)} to define the authentication reqPassword to be used.
  • + *
  • {@link #getAuthToken()} to retrieve the authentication reqPassword.
  • + *
+ * + * @see Login + * @see SlipBridgeCommunicationProtocol + * + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SClogin extends Login implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SClogin.class); + + private static final String DESCRIPTION = "Authenticate / login"; + private static final Command COMMAND = Command.GW_PASSWORD_ENTER_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private String reqPassword = ""; + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Methods required for interface {@link SlipBridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + requestData = new byte[32]; + byte[] password = reqPassword.getBytes(); + System.arraycopy(password, 0, requestData, 0, password.length); + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = true; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_PASSWORD_ENTER_CFM: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 1)) { + break; + } + int cfmStatus = responseData.getOneByteValue(0); + switch (cfmStatus) { + case 0: + logger.info("{} bridge connection successfully established (login succeeded).", + VeluxBindingConstants.BINDING_ID); + logger.debug("setResponse(): returned status: The request was successful."); + success = true; + break; + case 1: + logger.debug("setResponse(): returned status: The request failed."); + break; + default: + logger.warn("setResponse(): returned status={} (not defined).", cfmStatus); + break; + } + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + finished = true; + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public void setPassword(String thisPassword) { + logger.trace("setPassword({}) called.", thisPassword); + reqPassword = thisPassword; + return; + } + + @Override + public String getAuthToken() { + logger.trace("getAuthToken() called, returning {}.", reqPassword); + return reqPassword; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SClogout.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SClogout.java new file mode 100644 index 0000000000000..189f7aec06ff9 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SClogout.java @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.Logout; +import org.openhab.binding.velux.bridge.slip.util.KLF200Response; +import org.openhab.binding.velux.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Retrieve LAN configuration + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * There are no methods in addition to the mentioned interface. + * + * @see Logout + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SClogout extends Logout implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SClogout.class); + + private static final String DESCRIPTION = "Deauthenticate / logout"; + private static final Command COMMAND = Command.GW_OPENHAB_CLOSE; + + /* + * =========================================================== + * Message Objects + */ + + private final byte[] emptyPacket = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Methods required for interface {@link SlipBridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + return emptyPacket; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = true; + finished = true; + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductCommand.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductCommand.java new file mode 100644 index 0000000000000..ad21d50b0e5c6 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductCommand.java @@ -0,0 +1,313 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip; + +import java.util.Random; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.RunProductCommand; +import org.openhab.binding.velux.bridge.slip.util.KLF200Response; +import org.openhab.binding.velux.bridge.slip.util.Packet; +import org.openhab.binding.velux.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Send Command to Actuator + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #setNodeAndMainParameter} to define the node and intended parameter value.
  • + *
+ * + * @see RunProductCommand + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCrunProductCommand extends RunProductCommand implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCrunProductCommand.class); + + private static final String DESCRIPTION = "Send Command to Actuator"; + private static final Command COMMAND = Command.GW_COMMAND_SEND_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private int reqSessionID = 0; + private int reqCommandOriginator = 8; // SAAC + private int reqPriorityLevel = 5; // Comfort Level 2 + private int reqParameterActive = 0; // Main Parameter + private int reqFPI1 = 0; // Functional Parameter Indicator 1 set of bits + private int reqFPI2 = 0; // Functional Parameter Indicator 2 set of bits + private int reqMainParameter = 0; // for FunctionalParameterValueArray + private int reqIndexArrayCount = 1; // One node will be addressed + private int reqIndexArray01 = 1; // This is the node + private int reqPriorityLevelLock = 0; // Do not set a new lock on priority level + private int reqPL03 = 0; // unused + private int reqPL47 = 0; // unused + private int reqLockTime = 0; // 30 seconds + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Constructor Method + */ + + public SCrunProductCommand() { + logger.debug("SCgetProduct(Constructor) called."); + Random rand = new Random(); + reqSessionID = rand.nextInt(0x0fff); + logger.debug("SCgetProduct(): starting sessions with the random number {}.", reqSessionID); + } + + /* + * =========================================================== + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {}.", COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + Packet request = new Packet(new byte[66]); + reqSessionID = (reqSessionID + 1) & 0xffff; + request.setTwoByteValue(0, reqSessionID); + request.setOneByteValue(2, reqCommandOriginator); + request.setOneByteValue(3, reqPriorityLevel); + request.setOneByteValue(4, reqParameterActive); + request.setOneByteValue(5, reqFPI1); + request.setOneByteValue(6, reqFPI2); + request.setTwoByteValue(7, reqMainParameter); + request.setOneByteValue(41, reqIndexArrayCount); + request.setOneByteValue(42, reqIndexArray01); + request.setOneByteValue(62, reqPriorityLevelLock); + request.setOneByteValue(63, reqPL03); + request.setOneByteValue(64, reqPL47); + request.setOneByteValue(65, reqLockTime); + logger.trace("getRequestDataAsArrayOfBytes(): ntfSessionID={}.", reqSessionID); + logger.trace("getRequestDataAsArrayOfBytes(): reqCommandOriginator={}.", reqCommandOriginator); + logger.trace("getRequestDataAsArrayOfBytes(): reqPriorityLevel={}.", reqPriorityLevel); + logger.trace("getRequestDataAsArrayOfBytes(): reqParameterActive={}.", reqParameterActive); + logger.trace("getRequestDataAsArrayOfBytes(): reqFPI1={}.", reqFPI1); + logger.trace("getRequestDataAsArrayOfBytes(): reqFPI2={}.", reqFPI2); + logger.trace("getRequestDataAsArrayOfBytes(): reqMainParameter={}.", reqMainParameter); + logger.trace("getRequestDataAsArrayOfBytes(): reqIndexArrayCount={}.", reqIndexArrayCount); + logger.trace("getRequestDataAsArrayOfBytes(): reqIndexArray01={}.", reqIndexArray01); + logger.trace("getRequestDataAsArrayOfBytes(): reqPriorityLevelLock={}.", reqPriorityLevelLock); + logger.trace("getRequestDataAsArrayOfBytes(): reqPL03={}.", reqPL03); + logger.trace("getRequestDataAsArrayOfBytes(): reqPL47={}.", reqPL47); + logger.trace("getRequestDataAsArrayOfBytes(): reqLockTime={}.", reqLockTime); + requestData = request.toByteArray(); + logger.trace("getRequestDataAsArrayOfBytes() data is {}.", new Packet(requestData).toString()); + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_COMMAND_SEND_CFM: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 3)) { + finished = true; + break; + } + int cfmSessionID = responseData.getTwoByteValue(0); + int cfmStatus = responseData.getOneByteValue(2); + switch (cfmStatus) { + case 0: + logger.info("setResponse(): returned status: Error – Command rejected."); + finished = true; + break; + case 1: + logger.debug("setResponse(): returned status: OK - Command is accepted."); + if (!KLF200Response.check4matchingSessionID(logger, cfmSessionID, reqSessionID)) { + finished = true; + } else if (!isSequentialEnforced) { + logger.trace( + "setResponse(): skipping wait for more packets as sequential processing is not enforced."); + finished = true; + success = true; + } + break; + default: + logger.warn("setResponse(): returned status={} (not defined).", cfmStatus); + finished = true; + break; + } + break; + + case GW_COMMAND_RUN_STATUS_NTF: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 13)) { + finished = true; + break; + } + int ntfSessionID = responseData.getTwoByteValue(0); + int ntfStatusiD = responseData.getOneByteValue(2); + int ntfIndex = responseData.getOneByteValue(3); + int ntfNodeParameter = responseData.getOneByteValue(4); + int ntfParameterValue = responseData.getTwoByteValue(5); + int ntfRunStatus = responseData.getOneByteValue(7); + int ntfStatusReply = responseData.getOneByteValue(8); + int ntfInformationCode = responseData.getFourByteValue(9); + // Extracting information items + logger.debug("setResponse(): ntfSessionID={} (requested {}).", ntfSessionID, reqSessionID); + logger.debug("setResponse(): ntfStatusiD={}.", ntfStatusiD); + logger.debug("setResponse(): ntfIndex={}.", ntfIndex); + logger.debug("setResponse(): ntfNodeParameter={}.", ntfNodeParameter); + logger.debug("setResponse(): ntfParameterValue={}.", ntfParameterValue); + logger.debug("setResponse(): ntfRunStatus={}.", ntfRunStatus); + logger.debug("setResponse(): ntfStatusReply={}.", ntfStatusReply); + logger.debug("setResponse(): ntfInformationCode={}.", ntfInformationCode); + + if (!KLF200Response.check4matchingSessionID(logger, ntfSessionID, reqSessionID)) { + finished = true; + } + switch (ntfRunStatus) { + case 0: + logger.debug("setResponse(): returned ntfRunStatus: EXECUTION_COMPLETED."); + success = true; + break; + case 1: + logger.info("setResponse(): returned ntfRunStatus: EXECUTION_FAILED."); + finished = true; + break; + case 2: + logger.debug("setResponse(): returned ntfRunStatus: EXECUTION_ACTIVE."); + break; + default: + logger.warn("setResponse(): returned ntfRunStatus={} (not defined).", ntfRunStatus); + finished = true; + break; + } + if (!isSequentialEnforced) { + logger.trace( + "setResponse(): skipping wait for more packets as sequential processing is not enforced."); + success = true; + finished = true; + } + break; + + case GW_COMMAND_REMAINING_TIME_NTF: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 6)) { + finished = true; + break; + } + int timeNtfSessionID = responseData.getTwoByteValue(0); + int timeNtfIndex = responseData.getOneByteValue(2); + int timeNtfNodeParameter = responseData.getOneByteValue(3); + int timeNtfSeconds = responseData.getTwoByteValue(4); + + if (!KLF200Response.check4matchingSessionID(logger, timeNtfSessionID, reqSessionID)) { + finished = true; + } + + // Extracting information items + logger.debug("setResponse(): timeNtfSessionID={}.", timeNtfSessionID); + logger.debug("setResponse(): timeNtfIndex={}.", timeNtfIndex); + logger.debug("setResponse(): timeNtfNodeParameter={}.", timeNtfNodeParameter); + logger.debug("setResponse(): timeNtfSeconds={}.", timeNtfSeconds); + if (!isSequentialEnforced) { + logger.trace( + "setResponse(): skipping wait for more packets as sequential processing is not enforced."); + success = true; + finished = true; + } + break; + + case GW_SESSION_FINISHED_NTF: + finished = true; + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 2)) { + break; + } + int finishedNtfSessionID = responseData.getTwoByteValue(0); + if (!KLF200Response.check4matchingSessionID(logger, finishedNtfSessionID, reqSessionID)) { + break; + } + logger.debug("setResponse(): finishedNtfSessionID={}.", finishedNtfSessionID); + success = true; + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + finished = true; + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to the interface {@link BridgeCommunicationProtocol} + * and the abstract class {@link RunProductCommand} + */ + + @Override + public SCrunProductCommand setNodeAndMainParameter(int nodeId, int value) { + logger.debug("setNodeAndMainParameter({}) called.", nodeId); + this.reqIndexArray01 = nodeId; + this.reqMainParameter = value; + return this; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductDiscovery.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductDiscovery.java new file mode 100644 index 0000000000000..f3a7228b5bad0 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductDiscovery.java @@ -0,0 +1,169 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.RunProductDiscovery; +import org.openhab.binding.velux.bridge.slip.util.KLF200Response; +import org.openhab.binding.velux.bridge.slip.util.Packet; +import org.openhab.binding.velux.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Ask the Bridge to detect (new) products/actuators. + *

+ * + * It defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + * + * Protocol specific bridge communication supported by the Velux bridge: + * Detect Products/Actuators + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within + * the class itself. + *

+ * Implementing the protocol-independent class {@link RunProductDiscovery}. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link SlipBridgeCommunicationProtocol}. + *

+ * There are no methods in addition to the mentioned interface. + * + * @see RunProductDiscovery + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +// ToDo: Velux has to provide an updated API doc. +@NonNullByDefault +class SCrunProductDiscovery extends RunProductDiscovery implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCrunProductDiscovery.class); + + private static final String DESCRIPTION = "Detect Products/Actuators"; + private static final Command COMMAND = Command.GW_CS_DISCOVER_NODES_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private int reqNodeType = 0; // NO_TYPE (All nodes except controller) + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + logger.trace("getRequestDataAsArrayOfBytes() returns data for detection with type {}.", reqNodeType); + Packet request = new Packet(new byte[1]); + request.setOneByteValue(0, reqNodeType); + requestData = request.toByteArray(); + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_CS_DISCOVER_NODES_CFM: + logger.trace("setResponse({}): received confirmation for discovery mode."); + break; + + case GW_CS_DISCOVER_NODES_NTF: + finished = true; + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 131)) { + break; + } + int ntfDiscoverStatus = responseData.getOneByteValue(130); + switch (ntfDiscoverStatus) { + case 0: + logger.trace("setResponse(): returned status: OK. Discovered nodes. See bit array."); + success = true; + break; + case 5: + logger.warn("setResponse(): returned status: ERROR - Failed. CS not ready."); + break; + case 6: + logger.trace( + "setResponse(): returned status: OK. But some nodes were not added to system table (e.g. System table has reached its limit)."); + break; + case 7: + logger.warn("setResponse(): returned status: ERROR - CS busy with another task."); + break; + default: + logger.warn("setResponse({}): returned status={} (Reserved/unknown).", + Command.get(responseCommand).toString(), ntfDiscoverStatus); + break; + } + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + finished = true; + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductIdentification.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductIdentification.java new file mode 100644 index 0000000000000..59aaa546f4563 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductIdentification.java @@ -0,0 +1,204 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip; + +import java.util.Random; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.RunProductIdentification; +import org.openhab.binding.velux.bridge.slip.util.KLF200Response; +import org.openhab.binding.velux.bridge.slip.util.Packet; +import org.openhab.binding.velux.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Identify Product + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #setProductId(int)} to define the intended product.
  • + *
+ * + * @see RunProductIdentification + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCrunProductIdentification extends RunProductIdentification implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCrunProductIdentification.class); + + private static final String DESCRIPTION = "Identify Product"; + private static final Command COMMAND = Command.GW_WINK_SEND_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private int reqSessionID = 0; + private int reqCommandOriginator = 8; // SAAC + private int reqPriorityLevel = 5; // Comfort Level 2 + private int reqWinkState = 1; // Enable wink + private int reqWinkTime = 2; // Winktime = 2 seconds + private int reqIndexArrayCount = 1; // Number of actuators to be winking + private int reqIndexValue0 = 0; // Value for the ONE actuator + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Constructor Method + */ + + /** + * Constructor. + *

+ * Initializes the session id {@link #reqSessionID} with a random start value. + */ + public SCrunProductIdentification() { + logger.debug("SCrunProductIdentification(Constructor) called."); + Random rand = new Random(); + reqSessionID = rand.nextInt(0x0fff); + logger.debug("SCrunProductIdentification(): starting sessions with the random number {}.", reqSessionID); + } + + /* + * =========================================================== + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + Packet request = new Packet(new byte[27]); + reqSessionID = (reqSessionID + 1) & 0xffff; + request.setTwoByteValue(0, reqSessionID); + request.setOneByteValue(2, reqCommandOriginator); + request.setOneByteValue(3, reqPriorityLevel); + request.setOneByteValue(4, reqWinkState); + request.setOneByteValue(5, reqWinkTime); + request.setOneByteValue(6, reqIndexArrayCount); + request.setTwoByteValue(7, reqIndexValue0); + requestData = request.toByteArray(); + logger.trace("getRequestDataAsArrayOfBytes() data is {}.", new Packet(requestData).toString()); + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_WINK_SEND_CFM: + finished = true; + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 3)) { + break; + } + int cfmSessionID = responseData.getTwoByteValue(0); + int cfmStatus = responseData.getOneByteValue(2); + switch (cfmStatus) { + case 0: + logger.trace("setResponse(): returned status: Error - Wink is rejected."); + break; + case 1: + if (!KLF200Response.check4matchingSessionID(logger, cfmSessionID, reqSessionID)) { + break; + } + logger.trace("setResponse(): returned status: OK – Wink is accepted."); + success = true; + break; + default: + logger.warn("setResponse({}): returned status={} (Reserved/unknown).", + Command.get(responseCommand).toString(), cfmStatus); + break; + } + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + finished = true; + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to the interface {@link BridgeCommunicationProtocol} + * and the abstract class {@link RunProductIdentification} + */ + + /** + * Constructor Addon Method. + *

+ * Passes the intended Actuator Identifier towards this class for building the request. + * + * @param actuatorId as type int describing the scene to be processed. + * @return this of type {@link SCrunProductIdentification} as class itself. + */ + @Override + public SCrunProductIdentification setProductId(int actuatorId) { + logger.trace("setProductId({}) called.", actuatorId); + this.reqIndexValue0 = actuatorId; + return this; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductSearch.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductSearch.java new file mode 100644 index 0000000000000..d8ca427f00ccf --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductSearch.java @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.RunProductSearch; +import org.openhab.binding.velux.bridge.slip.util.Packet; +import org.openhab.binding.velux.things.VeluxGwState; +import org.openhab.binding.velux.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Check for lost Nodes + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * Implementing the protocol-independent class {@link RunProductSearch}. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link SCrunProductSearch#getState} to retrieve the Velux gateway status.
  • + *
+ * + * @see RunProductSearch + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCrunProductSearch extends RunProductSearch implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCrunProductSearch.class); + + private static final String DESCRIPTION = "Check for lost Nodes"; + private static final Command COMMAND = Command.GW_GET_STATE_REQ; + + /* + * Message Objects + */ + + private byte[] requestData = new byte[0]; + private short responseCommand; + private byte[] responseData = new byte[0]; + + /* + * =========================================================== + * Methods required for interface {@link SlipBridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + requestData = new byte[0]; + return requestData; + } + + @Override + public void setResponse(short thisResponseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + logger.trace("setResponseCommand({}, {}) called.", thisResponseCommand, new Packet(thisResponseData)); + responseCommand = thisResponseCommand; + responseData = thisResponseData; + } + + @Override + public boolean isCommunicationFinished() { + return (responseCommand == Command.GW_GET_STATE_CFM.getShort()); + } + + @Override + public boolean isCommunicationSuccessful() { + return (responseCommand == Command.GW_GET_STATE_CFM.getShort()); + } + + /* + * =========================================================== + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + + public VeluxGwState getState() { + byte stateValue = responseData[0]; + byte subStateValue = responseData[1]; + VeluxGwState thisGwState = new VeluxGwState(stateValue, subStateValue); + logger.trace("getState() returns {} ({}).", thisGwState, thisGwState.toDescription()); + return thisGwState; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunScene.java new file mode 100644 index 0000000000000..7ebc62a0dc98c --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunScene.java @@ -0,0 +1,291 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip; + +import java.util.Random; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.RunScene; +import org.openhab.binding.velux.bridge.slip.util.KLF200Response; +import org.openhab.binding.velux.bridge.slip.util.Packet; +import org.openhab.binding.velux.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Run Scene + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #setSceneId} to define the scene to be executed.
  • + *
+ * + * @see RunScene + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCrunScene extends RunScene implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCrunScene.class); + + private static final String DESCRIPTION = "Run Scene"; + private static final Command COMMAND = Command.GW_ACTIVATE_SCENE_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private int reqSessionID = 0; + private int reqCommandOriginator = 8; // SAAC + private int reqPriorityLevel = 5; // Comfort Level 2 + private int reqSceneID = -1; // SceneID as one unsigned byte number + private int reqVelocity = 0; // The product group operates by its default velocity. + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Constructor Method + */ + + /** + * Constructor. + *

+ * Initializes the session id {@link #reqSessionID} with a random start value. + */ + public SCrunScene() { + logger.debug("SCrunScene(Constructor) called."); + Random rand = new Random(); + reqSessionID = rand.nextInt(0x0fff); + logger.debug("SCrunScene(): starting sessions with the random number {}.", reqSessionID); + } + + /* + * =========================================================== + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.trace("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + Packet request = new Packet(new byte[6]); + reqSessionID = (reqSessionID + 1) & 0xffff; + request.setTwoByteValue(0, reqSessionID); + request.setOneByteValue(2, reqCommandOriginator); + request.setOneByteValue(3, reqPriorityLevel); + request.setOneByteValue(4, reqSceneID); + request.setOneByteValue(5, reqVelocity); + requestData = request.toByteArray(); + logger.trace("getRequestCommand(): SessionID={}.", reqSessionID); + logger.trace("getRequestCommand(): CommandOriginator={}.", reqCommandOriginator); + logger.trace("getRequestCommand(): PriorityLevel={}.", reqPriorityLevel); + logger.trace("getRequestCommand(): SceneID={}.", reqSceneID); + logger.trace("getRequestCommand(): Velocity={}.", reqVelocity); + logger.debug("getRequestCommand() returns {} ({}) with SceneID {}.", COMMAND.name(), COMMAND.getCommand(), + reqSceneID); + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_ACTIVATE_SCENE_CFM: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 3)) { + finished = true; + break; + } + int cfmStatus = responseData.getOneByteValue(0); + int cfmSessionID = responseData.getTwoByteValue(1); + switch (cfmStatus) { + case 0: + logger.trace("setResponse(): returned status: OK - Request accepted."); + if (!KLF200Response.check4matchingSessionID(logger, cfmSessionID, reqSessionID)) { + finished = true; + } else if (!isSequentialEnforced) { + logger.trace( + "setResponse(): skipping wait for more packets as sequential processing is not enforced."); + success = true; + finished = true; + } + break; + case 1: + finished = true; + logger.trace("setResponse(): returned status: Error – Invalid parameter."); + break; + case 2: + finished = true; + logger.trace("setResponse(): returned status: Error – Request rejected."); + break; + default: + finished = true; + logger.warn("setResponse({}): returned status={} (Reserved/unknown).", + Command.get(responseCommand).toString(), cfmStatus); + break; + } + break; + + case GW_COMMAND_RUN_STATUS_NTF: + logger.trace("setResponse(): received GW_COMMAND_RUN_STATUS_NTF, continuing."); + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 13)) { + logger.trace("setResponse(): GW_COMMAND_RUN_STATUS_NTF received with invalid length."); + finished = true; + break; + } + // Extracting information items + int ntfSessionID = responseData.getTwoByteValue(0); + int ntfStatusID = responseData.getOneByteValue(2); + int ntfIndex = responseData.getOneByteValue(3); + int ntfNodeParameter = responseData.getOneByteValue(4); + int ntfParameterValue = responseData.getTwoByteValue(5); + int ntfRunStatus = responseData.getOneByteValue(7); + int ntfStatusReply = responseData.getOneByteValue(8); + int ntfInformationCode = responseData.getFourByteValue(9); + + logger.trace("setResponse(): SessionID={}.", ntfSessionID); + logger.trace("setResponse(): StatusID={}.", ntfStatusID); + logger.trace("setResponse(): Index={}.", ntfIndex); + logger.trace("setResponse(): NodeParameter={}.", ntfNodeParameter); + logger.trace("setResponse(): ParameterValue={}.", ntfParameterValue); + logger.trace("setResponse(): RunStatus={}.", ntfRunStatus); + logger.trace("setResponse(): StatusReply={}.", ntfStatusReply); + logger.trace("setResponse(): InformationCode={}.", ntfInformationCode); + + if (!isSequentialEnforced) { + logger.trace( + "setResponse(): skipping wait for more packets as sequential processing is not enforced."); + success = true; + finished = true; + } + break; + + case GW_COMMAND_REMAINING_TIME_NTF: + logger.trace("setResponse(): received GW_COMMAND_REMAINING_TIME_NTF, continuing."); + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 6)) { + logger.trace("setResponse(): GW_COMMAND_REMAINING_TIME_NTF received with invalid length."); + finished = true; + break; + } + // Extracting information items + ntfSessionID = responseData.getTwoByteValue(0); + ntfIndex = responseData.getOneByteValue(2); + ntfNodeParameter = responseData.getOneByteValue(3); + int ntfSeconds = responseData.getTwoByteValue(4); + + logger.trace("setResponse(): SessionID={}.", ntfSessionID); + logger.trace("setResponse(): Index={}.", ntfIndex); + logger.trace("setResponse(): NodeParameter={}.", ntfNodeParameter); + logger.trace("setResponse(): Seconds={}.", ntfSeconds); + + if (!isSequentialEnforced) { + logger.trace( + "setResponse(): skipping wait for more packets as sequential processing is not enforced."); + success = true; + finished = true; + } + break; + + case GW_SESSION_FINISHED_NTF: + logger.trace("setResponse(): received GW_SESSION_FINISHED_NTF."); + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 2)) { + logger.trace("setResponse(): GW_SESSION_FINISHED_NTF received with invalid length."); + finished = true; + break; + } + // Extracting information items + ntfSessionID = responseData.getTwoByteValue(0); + + logger.trace("setResponse(): SessionID={}.", ntfSessionID); + + success = true; + finished = true; + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + finished = true; + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to the interface {@link BridgeCommunicationProtocol} + * and the abstract class {@link RunScene} + */ + + @Override + public SCrunScene setSceneId(int sceneId) { + logger.trace("setProductId({}) called.", sceneId); + this.reqSceneID = sceneId; + return this; + } + + @Override + public SCrunScene setVelocity(int velocity) { + logger.trace("setVelocity({}) called.", velocity); + this.reqVelocity = velocity; + return this; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCsetHouseStatusMonitor.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCsetHouseStatusMonitor.java new file mode 100644 index 0000000000000..55dbe4f57c31f --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCsetHouseStatusMonitor.java @@ -0,0 +1,145 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.SetHouseStatusMonitor; +import org.openhab.binding.velux.bridge.slip.util.KLF200Response; +import org.openhab.binding.velux.bridge.slip.util.Packet; +import org.openhab.binding.velux.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Modify HouseStatusMonitor + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #serviceActivation} to define the new service activation state.
  • + *
+ * + * @see SetHouseStatusMonitor + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCsetHouseStatusMonitor extends SetHouseStatusMonitor implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCsetHouseStatusMonitor.class); + + private static final String DESCRIPTION = "Modify HouseStatusMonitor"; + + /* + * =========================================================== + * Message Content Parameters + */ + + private boolean activateService = false; + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + Command command = activateService ? Command.GW_HOUSE_STATUS_MONITOR_ENABLE_REQ + : Command.GW_HOUSE_STATUS_MONITOR_DISABLE_REQ; + success = false; + finished = false; + logger.debug("getRequestCommand() returns {} ({}).", command.name(), command.getCommand()); + return command.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + logger.debug("getRequestDataAsArrayOfBytes() data is {}.", new Packet(requestData).toString()); + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = true; + switch (Command.get(responseCommand)) { + case GW_HOUSE_STATUS_MONITOR_ENABLE_CFM: + logger.trace("setResponse(): service enable confirmed by bridge."); + // returned enabled: successful if enable requested + success = activateService; + break; + case GW_HOUSE_STATUS_MONITOR_DISABLE_CFM: + logger.trace("setResponse(): service disable confirmed by bridge."); + // returned disabled: successful if disable requested + success = !activateService; + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to the interface {@link BridgeCommunicationProtocol} + * and the abstract class {@link SetHouseStatusMonitor} + */ + + @Override + public SetHouseStatusMonitor serviceActivation(boolean enableService) { + this.activateService = enableService; + return this; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCsetSceneVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCsetSceneVelocity.java new file mode 100644 index 0000000000000..e204f61a30fd0 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCsetSceneVelocity.java @@ -0,0 +1,175 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.SetSceneVelocity; +import org.openhab.binding.velux.bridge.slip.util.KLF200Response; +import org.openhab.binding.velux.bridge.slip.util.Packet; +import org.openhab.binding.velux.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.things.VeluxProductVelocity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Modify Velocity of an Actuator + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #setMode} to define the new silence mode of the intended actuator.
  • + *
+ * + * @see SetSceneVelocity + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +// ToDo: THIS MESSAGE EXCHANGE IS AN UNDOCUMENTED FEATURE. Check the updated Velux doc against this implementation. +@NonNullByDefault +class SCsetSceneVelocity extends SetSceneVelocity implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCsetSceneVelocity.class); + + private static final String DESCRIPTION = "Modify Velocity of an Actuator"; + private static final Command COMMAND = Command.GW_SET_NODE_VELOCITY_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private int reqNodeID = -1; + private int reqNodeVelocity = -1; + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {}.", COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + Packet request = new Packet(new byte[2]); + request.setOneByteValue(0, reqNodeID); + request.setOneByteValue(1, reqNodeVelocity); + requestData = request.toByteArray(); + logger.trace("getRequestDataAsArrayOfBytes() data is {}.", new Packet(requestData).toString()); + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_SET_NODE_VELOCITY_CFM: + finished = true; + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 3)) { + break; + } + int cfmStatus = responseData.getOneByteValue(0); + switch (cfmStatus) { + case 0: + logger.trace("setResponse(): returned status: Error - Wink is rejected."); + break; + case 1: + logger.trace("setResponse(): returned status: OK – Wink is accepted."); + success = true; + break; + default: + logger.warn("setResponse({}): returned status={} (Reserved/unknown).", + Command.get(responseCommand).toString(), cfmStatus); + break; + } + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + finished = true; + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to the interface {@link BridgeCommunicationProtocol} + * and the abstract class {@link RunProductIdentification} + */ + + /** + * Constructor Addon Method. + *

+ * Passes the intended Actuator Identifier towards this class for building the request lateron. + * + * @param actuatorId as type int describing the scene to be processed. + * @param silent as type boolean describing the silence mode of this node. + * @return this of type {@link SCsetSceneVelocity} as class itself. + */ + @Override + public SCsetSceneVelocity setMode(int actuatorId, boolean silent) { + logger.trace("setProductId({},{}) called.", actuatorId, silent); + this.reqNodeID = actuatorId; + this.reqNodeVelocity = silent ? VeluxProductVelocity.SILENT.getVelocity() + : VeluxProductVelocity.FAST.getVelocity(); + return this; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SlipBridgeAPI.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SlipBridgeAPI.java new file mode 100644 index 0000000000000..8f1fc9d945ba8 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SlipBridgeAPI.java @@ -0,0 +1,207 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.bridge.VeluxBridgeInstance; +import org.openhab.binding.velux.bridge.common.BridgeAPI; +import org.openhab.binding.velux.bridge.common.GetDeviceStatus; +import org.openhab.binding.velux.bridge.common.GetFirmware; +import org.openhab.binding.velux.bridge.common.GetHouseStatus; +import org.openhab.binding.velux.bridge.common.GetLANConfig; +import org.openhab.binding.velux.bridge.common.GetProduct; +import org.openhab.binding.velux.bridge.common.GetProductLimitation; +import org.openhab.binding.velux.bridge.common.GetProducts; +import org.openhab.binding.velux.bridge.common.GetScenes; +import org.openhab.binding.velux.bridge.common.GetWLANConfig; +import org.openhab.binding.velux.bridge.common.Login; +import org.openhab.binding.velux.bridge.common.Logout; +import org.openhab.binding.velux.bridge.common.RunProductCommand; +import org.openhab.binding.velux.bridge.common.RunProductDiscovery; +import org.openhab.binding.velux.bridge.common.RunProductIdentification; +import org.openhab.binding.velux.bridge.common.RunProductSearch; +import org.openhab.binding.velux.bridge.common.RunScene; +import org.openhab.binding.velux.bridge.common.SetHouseStatusMonitor; +import org.openhab.binding.velux.bridge.common.SetSceneVelocity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * SLIP-based 3rd Level I/O interface towards the Velux bridge. + *

+ * It provides the one-and-only protocol specific 1st-level communication class. + * Additionally it provides all methods for different gateway interactions. + *

+ * The following class access methods exist: + *

    + *
  • {@link SlipBridgeAPI#getDeviceStatus} for retrieving the bridge state (i.e. IDLE, BUSY, a.s.o),
  • + *
  • {@link SlipBridgeAPI#getFirmware} for retrieving the firmware version of the bridge,
  • + *
  • {@link SlipBridgeAPI#getHouseStatus} for retrieving the information about device state changes recognized by the + * bridge,
  • + *
  • {@link SlipBridgeAPI#getLANConfig} for retrieving the complete LAN information of the bridge,
  • + *
  • {@link SlipBridgeAPI#getProduct} for retrieving the any information about a device behind the bridge,
  • + *
  • {@link SlipBridgeAPI#getProductLimitation} for retrieving the limitation information about a device behind the + * bridge,
  • + *
  • {@link SlipBridgeAPI#getProducts} for retrieving the any information for all devices behind the bridge,
  • + *
  • {@link SlipBridgeAPI#getScenes} for retrieving the any information for all scenes defined on the bridge,
  • + *
  • {@link SlipBridgeAPI#getWLANConfig} for retrieving the complete WLAN information of the bridge,
  • + *
  • {@link SlipBridgeAPI#login} for establishing a trusted connectivity by authentication,
  • + *
  • {@link SlipBridgeAPI#logout} for tearing down the trusted connectivity by deauthentication,
  • + *
  • {@link SlipBridgeAPI#runProductCommand} for manipulation of a device behind the bridge (i.e. instructing to + * modify a position),
  • + *
  • {@link SlipBridgeAPI#runProductDiscovery} for activation of learning mode of the bridge to discovery new + * products,
  • + *
  • {@link SlipBridgeAPI#runProductIdentification} for human-oriented identification a device behind the bridge (i.e. + * by winking or switching on-and-off),
  • + *
  • {@link SlipBridgeAPI#runProductSearch} for searching for lost products on the bridge,
  • + *
  • {@link SlipBridgeAPI#runScene} for manipulation of a set of devices behind the bridge which are tied together as + * scene,
  • + *
  • {@link SlipBridgeAPI#setHouseStatusMonitor} for activation or deactivation of the house monitoring mode to be + * informed about device state changes recognized by the bridge,
  • + *
  • {@link SlipBridgeAPI#setSceneVelocity} for changes the velocity of a scene defined on the bridge (i.e. silent or + * fast mode).
  • + *
+ *

+ * As most derived class of the several inheritance levels it defines an + * interfacing method which returns the SLIP-protocol-specific communication for gateway interaction. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SlipBridgeAPI implements BridgeAPI { + private final Logger logger = LoggerFactory.getLogger(SlipBridgeAPI.class); + + private static final GetDeviceStatus GETDEVICESTATUS = new SCgetDeviceStatus(); + private static final GetFirmware GETFIRMWARE = new SCgetFirmware(); + private static final GetHouseStatus GETHOUSESTATUS = new SCgetHouseStatus(); + private static final GetLANConfig GETLANCONFIG = new SCgetLANConfig(); + private static final GetProduct GETPRODUCT = new SCgetProduct(); + private static final GetProductLimitation GETPRODUCTLIMITATION = new SCgetLimitation(); + private static final GetProducts GETPRODUCTS = new SCgetProducts(); + private static final GetScenes GETSCENES = new SCgetScenes(); + private static final GetWLANConfig GETWLANCONFIG = new SCgetWLANConfig(); + private static final Login LOGIN = new SClogin(); + private static final Logout LOGOUT = new SClogout(); + private static final RunProductCommand RUNPRODUCTCOMMAND = new SCrunProductCommand(); + private static final RunProductDiscovery RUNPRODUCTDISCOVERY = new SCrunProductDiscovery(); + private static final RunProductIdentification RUNPRODUCTIDENTIFICATION = new SCrunProductIdentification(); + private static final RunProductSearch RUNPRODUCTSEARCH = new SCrunProductSearch(); + private static final RunScene RUNSCENE = new SCrunScene(); + private static final SetHouseStatusMonitor SETHOUSESTATUSMONITOR = new SCsetHouseStatusMonitor(); + private static final SetSceneVelocity SETSCENEVELOCITY = new SCsetSceneVelocity(); + + /** + * Constructor. + *

+ * Inherits the initialization of the binding-wide instance for dealing for common information and + * initializes the handler {@link SlipVeluxBridge#bridgeAPI} + * to pass the interface methods. + * + * @param bridgeInstance refers to the binding-wide instance for dealing for common informations. + */ + SlipBridgeAPI(VeluxBridgeInstance bridgeInstance) { + logger.trace("SlipBridgeAPI(constructor) called."); + } + + @Override + public GetDeviceStatus getDeviceStatus() { + return GETDEVICESTATUS; + } + + @Override + public GetFirmware getFirmware() { + return GETFIRMWARE; + } + + @Override + public @Nullable GetHouseStatus getHouseStatus() { + return GETHOUSESTATUS; + } + + @Override + public GetLANConfig getLANConfig() { + return GETLANCONFIG; + } + + @Override + public @Nullable GetProduct getProduct() { + return GETPRODUCT; + } + + @Override + public @Nullable GetProductLimitation getProductLimitation() { + return GETPRODUCTLIMITATION; + } + + @Override + public GetProducts getProducts() { + return GETPRODUCTS; + } + + @Override + public GetScenes getScenes() { + return GETSCENES; + } + + @Override + public GetWLANConfig getWLANConfig() { + return GETWLANCONFIG; + } + + @Override + public Login login() { + return LOGIN; + } + + @Override + public Logout logout() { + return LOGOUT; + } + + @Override + public @Nullable RunProductCommand runProductCommand() { + return RUNPRODUCTCOMMAND; + } + + @Override + public RunProductDiscovery runProductDiscovery() { + return RUNPRODUCTDISCOVERY; + } + + @Override + public RunProductIdentification runProductIdentification() { + return RUNPRODUCTIDENTIFICATION; + } + + @Override + public RunProductSearch runProductSearch() { + return RUNPRODUCTSEARCH; + } + + @Override + public RunScene runScene() { + return RUNSCENE; + } + + @Override + public @Nullable SetHouseStatusMonitor setHouseStatusMonitor() { + return SETHOUSESTATUSMONITOR; + } + + @Override + public SetSceneVelocity setSceneVelocity() { + return SETSCENEVELOCITY; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SlipBridgeCommunicationProtocol.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SlipBridgeCommunicationProtocol.java new file mode 100644 index 0000000000000..663b2240e0d1a --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SlipBridgeCommunicationProtocol.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.common.BridgeCommunicationProtocol; +import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Authentication + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 2nd level interface it defines the methods to help in sending a query and + * processing the received answer. + *

+ * (Additional) Methods in this interface for the appropriate interaction: + *

    + *
  • {@link getRequestCommand} to return the intended command to be sent.
  • + *
  • {@link getRequestDataAsArrayOfBytes} to return the intended data part to be sent.
  • + *
  • {@link setResponse} to store the response already separated into response command and data part.
  • + *
  • {@link isCommunicationFinished} to signal the completeness of the interaction (only available + * after storing the response).
  • + *
+ *

+ * Other mandatory methods are inherited from {@link BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +interface SlipBridgeCommunicationProtocol extends BridgeCommunicationProtocol { + + /** + * Provides an empty array of bytes. + * + */ + public static final byte[] EMPTYDATA = new byte[0]; + + /** + * Returning the command part of the request object for further SLIP serialization. + * + * @return commandNumber + * is of type {@link CommandNumber}. + */ + public CommandNumber getRequestCommand(); + + /** + * Returning the data part of the request object for further SLIP serialization. + * + * @return dataAsArrayOfBytes + * is an Array of byte. + */ + public byte[] getRequestDataAsArrayOfBytes(); + + /** + * Storing the command and the data part of the response object for further checks. + * + * @param thisResponseCommand of type short: command part of the response packet. + * @param thisResponseData of type Array of bytes: data part of response packet to be processed. + * @param isSequentialEnforced of type boolean: enforces the strict handshake sequence even for long duration + * interactions. + */ + public void setResponse(short thisResponseCommand, byte[] thisResponseData, boolean isSequentialEnforced); + + /** + * Returning the communication status included within the response message.. + * + * @return isFinished + * is a boolean signaling the end of this transaction. + */ + public boolean isCommunicationFinished(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SlipVeluxBridge.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SlipVeluxBridge.java new file mode 100644 index 0000000000000..52394c05942bd --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SlipVeluxBridge.java @@ -0,0 +1,346 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip; + +import java.text.ParseException; +import java.util.TreeSet; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.VeluxBridge; +import org.openhab.binding.velux.bridge.VeluxBridgeInstance; +import org.openhab.binding.velux.bridge.common.BridgeAPI; +import org.openhab.binding.velux.bridge.common.BridgeCommunicationProtocol; +import org.openhab.binding.velux.bridge.slip.io.Connection; +import org.openhab.binding.velux.bridge.slip.util.Packet; +import org.openhab.binding.velux.bridge.slip.util.SlipEncoding; +import org.openhab.binding.velux.bridge.slip.util.SlipRFC1055; +import org.openhab.binding.velux.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.things.VeluxProduct.ProductBridgeIndex; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * SLIP-based 2nd Level I/O interface towards the Velux bridge. + *

+ * It provides methods for pre- and postcommunication + * as well as a common method for the real communication. + *

+ * In addition to the generic {@link VeluxBridge} methods, i.e. + *

    + *
  • {@link VeluxBridge#bridgeLogin} for pre-communication,
  • + *
  • {@link VeluxBridge#bridgeLogout} for post-communication,
  • + *
  • {@link VeluxBridge#bridgeCommunicate} as method for the common communication,
  • + *
  • {@link VeluxBridge#bridgeAPI} as interfacing method to all interaction prototypes,
  • + *
+ * the following class access methods provides the protocol-specific implementation: + *
    + *
  • {@link #bridgeDirectCommunicate} for SLIP-based communication.
  • + *
  • {@link #bridgeAPI} for return all defined protocol-specific implementations which are provided by + * {@link org.openhab.binding.velux.bridge.slip.SlipBridgeAPI SlipBridgeAPI}.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public class SlipVeluxBridge extends VeluxBridge { + private final Logger logger = LoggerFactory.getLogger(SlipVeluxBridge.class); + + /* + * *************************** + * ***** Private Objects ***** + */ + + private final byte[] emptyPacket = new byte[0]; + private Connection connection = new Connection(); + + /** + * Handler passing the interface methods to other classes. + * Can be accessed via method {@link org.openhab.binding.velux.bridge.common.BridgeAPI BridgeAPI}. + */ + private BridgeAPI bridgeAPI; + + /** + * Constructor. + *

+ * Inherits the initialization of the binding-wide instance for dealing for common informations and + * initializes the Velux bridge connection settings. + * + * @param bridgeInstance refers to the binding-wide instance for dealing for common informations. + */ + public SlipVeluxBridge(VeluxBridgeInstance bridgeInstance) { + super(bridgeInstance); + logger.trace("SlipVeluxBridge(constructor) called."); + bridgeAPI = new SlipBridgeAPI(bridgeInstance); + supportedProtocols = new TreeSet(); + supportedProtocols.add("slip"); + logger.trace("SlipVeluxBridge(constructor) done."); + } + + /** + * Destructor. + *

+ * De-initializes the binding-wide instance. + */ + @Override + public void shutdown() { + logger.trace("shutdown() called."); + connection.resetConnection(); + logger.trace("shutdown() done."); + } + + /** + * Provides information about the base-level communication method and + * any kind of available gateway interactions. + *

+ * Note: the implementation within this class {@link SlipVeluxBridge} as inherited from {@link VeluxBridge} + * will return the protocol-specific class implementations. + *

+ * The information will be initialized by the corresponding API class {@link SlipBridgeAPI}. + * + * @return bridgeAPI of type {@link BridgeAPI} contains all possible methods. + */ + @Override + public BridgeAPI bridgeAPI() { + logger.trace("bridgeAPI() called."); + return bridgeAPI; + } + + /** + * Method as implementation of abstract superclass method. + *

+ * Initializes a client/server communication towards Velux veluxBridge + * based on the Basic I/O interface {@link Connection#io} and parameters + * passed as arguments (see below). + * + * @param communication Structure of interface type {@link SlipBridgeCommunicationProtocol} describing the intended + * communication, that is request and response interactions as well as appropriate URL definition. + * @param useAuthentication boolean flag to decide whether to use authenticated communication. + * @return success of type boolean which signals the success of the communication. + * + */ + @Override + protected boolean bridgeDirectCommunicate(BridgeCommunicationProtocol communication, boolean useAuthentication) { + logger.trace("bridgeDirectCommunicate(BCP: {},{}authenticated) called.", communication.name(), + useAuthentication ? "" : "un"); + return bridgeDirectCommunicate((SlipBridgeCommunicationProtocol) communication, useAuthentication); + } + + /** + * Returns the timestamp in milliseconds since Unix epoch + * of last (potentially faulty) communication. + * + * @return timestamp in milliseconds. + */ + @Override + public synchronized long lastCommunication() { + return connection.lastCommunication(); + } + + /** + * Returns the timestamp in milliseconds since Unix epoch + * of last successful communication. + * + * @return timestamp in milliseconds. + */ + @Override + public synchronized long lastSuccessfulCommunication() { + return connection.lastSuccessfulCommunication(); + } + + /** + * Initializes a client/server communication towards Velux veluxBridge + * based on the Basic I/O interface {@link Connection#io} and parameters + * passed as arguments (see below). + * + * @param communication Structure of interface type {@link SlipBridgeCommunicationProtocol} describing the + * intended communication, that is request and response interactions as well as appropriate URL + * definition. + * @param useAuthentication boolean flag to decide whether to use authenticated communication. + * @return success of type boolean which signals the success of the communication. + */ + private synchronized boolean bridgeDirectCommunicate(SlipBridgeCommunicationProtocol communication, + boolean useAuthentication) { + logger.trace("bridgeDirectCommunicate({},{}authenticated) called.", communication.name(), + useAuthentication ? "" : "un"); + + assert this.bridgeInstance.veluxBridgeConfiguration().protocol.contentEquals("slip"); + boolean isSequentialEnforced = this.bridgeInstance.veluxBridgeConfiguration().isSequentialEnforced; + boolean isProtocolTraceEnabled = this.bridgeInstance.veluxBridgeConfiguration().isProtocolTraceEnabled; + + // From parameters + short command = communication.getRequestCommand().toShort(); + byte[] data = communication.getRequestDataAsArrayOfBytes(); + // For further use at different logging statements + String commandString = Command.get(command).toString(); + + logger.debug("bridgeDirectCommunicate({},{}authenticated) called.", commandString, + useAuthentication ? "" : "un"); + boolean success = false; + + communication: do { + // Special handling + if (Command.get(command) == Command.GW_OPENHAB_CLOSE) { + logger.trace("bridgeDirectCommunicate(): special command: shutting down connection."); + connection.resetConnection(); + success = true; + continue; + } + + // Normal processing + logger.trace("bridgeDirectCommunicate(): working on request {} with {} bytes of data.", commandString, + data.length); + byte[] sendBytes = emptyPacket; + if (Command.get(command) == Command.GW_OPENHAB_RECEIVEONLY) { + logger.trace( + "bridgeDirectCommunicate(): special command: determine whether there is any message waiting."); + logger.trace("bridgeDirectCommunicate(): check for a waiting message."); + if (!connection.isMessageAvailable()) { + logger.trace("bridgeDirectCommunicate(): no message waiting, aborting."); + break communication; + } + logger.trace("bridgeDirectCommunicate(): there is a message waiting."); + } else { + SlipEncoding t = new SlipEncoding(command, data); + if (!t.isValid()) { + logger.warn("bridgeDirectCommunicate(): SlipEncoding() failed, aborting."); + break; + } + logger.trace("bridgeDirectCommunicate(): transportEncoding={}.", t.toString()); + sendBytes = new SlipRFC1055().encode(t.toMessage()); + } + + do { + byte[] receivedPacket; + try { + if (sendBytes.length > 0) { + logger.trace("bridgeDirectCommunicate(): sending {} bytes.", sendBytes.length); + if (isProtocolTraceEnabled) { + logger.info("Sending command {}.", commandString); + } + } else { + logger.trace("bridgeDirectCommunicate(): initiating receive-only."); + } + // (Optionally) Send and receive packet. + receivedPacket = connection.io(this.bridgeInstance, sendBytes); + // Once being sent, it should never be sent again + sendBytes = emptyPacket; + } catch (Exception e) { + logger.warn("bridgeDirectCommunicate(): connection.io returns {}", e.getMessage()); + break communication; + } + logger.trace("bridgeDirectCommunicate(): received packet {}.", new Packet(receivedPacket).toString()); + byte[] response; + try { + response = new SlipRFC1055().decode(receivedPacket); + } catch (ParseException e) { + logger.warn("bridgeDirectCommunicate(): method SlipRFC1055() raised a decoding error: {}.", + e.getMessage()); + break communication; + } + SlipEncoding tr = new SlipEncoding(response); + if (!tr.isValid()) { + logger.warn("bridgeDirectCommunicate(): method SlipEncoding() raised a decoding error."); + break communication; + } + short responseCommand = tr.getCommand(); + byte[] responseData = tr.getData(); + logger.debug("bridgeDirectCommunicate(): working on response {} with {} bytes of data.", + Command.get(responseCommand).toString(), responseData.length); + if (isProtocolTraceEnabled) { + logger.info("Received answer {}.", Command.get(responseCommand).toString()); + } + // Handle some common (unexpected) answers + switch (Command.get(responseCommand)) { + case GW_NODE_INFORMATION_CHANGED_NTF: + logger.trace("bridgeDirectCommunicate(): received GW_NODE_INFORMATION_CHANGED_NTF."); + logger.trace("bridgeDirectCommunicate(): continue with receiving."); + continue; + case GW_NODE_STATE_POSITION_CHANGED_NTF: + logger.trace( + "bridgeDirectCommunicate(): received GW_NODE_STATE_POSITION_CHANGED_NTF, special processing of this packet."); + SCgetHouseStatus receiver = new SCgetHouseStatus(); + receiver.setResponse(responseCommand, responseData, isSequentialEnforced); + if (receiver.isCommunicationSuccessful()) { + logger.trace("bridgeDirectCommunicate(): existingProducts().update() called."); + bridgeInstance.existingProducts().update(new ProductBridgeIndex(receiver.getNtfNodeID()), + receiver.getNtfState(), receiver.getNtfCurrentPosition(), receiver.getNtfTarget()); + } + logger.trace("bridgeDirectCommunicate(): continue with receiving."); + continue; + case GW_ERROR_NTF: + switch (responseData[0]) { + case 0: + logger.warn( + "bridgeDirectCommunicate(): received GW_ERROR_NTF on {} (Not further defined error), aborting.", + commandString); + break communication; + case 1: + logger.warn( + "bridgeDirectCommunicate(): received GW_ERROR_NTF (Unknown Command or command is not accepted at this state) on {}, aborting.", + commandString); + break communication; + case 2: + logger.warn( + "bridgeDirectCommunicate(): received GW_ERROR_NTF (ERROR on Frame Structure) on {}, aborting.", + commandString); + break communication; + case 7: + logger.trace( + "bridgeDirectCommunicate(): received GW_ERROR_NTF (Busy. Try again later) on {}, retrying.", + commandString); + sendBytes = emptyPacket; + continue; + case 8: + logger.warn( + "bridgeDirectCommunicate(): received GW_ERROR_NTF (Bad system table index) on {}, aborting.", + commandString); + break communication; + case 12: + logger.warn( + "bridgeDirectCommunicate(): received GW_ERROR_NTF (Not authenticated) on {}, aborting.", + commandString); + resetAuthentication(); + break communication; + default: + logger.warn("bridgeDirectCommunicate(): received GW_ERROR_NTF ({}) on {}, aborting.", + responseData[0], commandString); + break communication; + } + case GW_ACTIVATION_LOG_UPDATED_NTF: + logger.info("bridgeDirectCommunicate(): received GW_ACTIVATION_LOG_UPDATED_NTF."); + logger.trace("bridgeDirectCommunicate(): continue with receiving."); + continue; + + case GW_COMMAND_RUN_STATUS_NTF: + case GW_COMMAND_REMAINING_TIME_NTF: + case GW_SESSION_FINISHED_NTF: + if (!isSequentialEnforced) { + logger.trace("bridgeDirectCommunicate(): response ignored due to activated parallelism."); + logger.trace("bridgeDirectCommunicate(): continue with receiving."); + continue; + } + + default: + } + logger.trace("bridgeDirectCommunicate(): passes back command {} and data {}.", + new CommandNumber(responseCommand).toString(), new Packet(responseData).toString()); + communication.setResponse(responseCommand, responseData, isSequentialEnforced); + } while (!communication.isCommunicationFinished()); + success = communication.isCommunicationSuccessful(); + } while (false); // communication + logger.debug("bridgeDirectCommunicate({}) returns {}.", commandString, success ? "success" : "failure"); + return success; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/io/Connection.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/io/Connection.java new file mode 100644 index 0000000000000..6f2d14a468e9f --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/io/Connection.java @@ -0,0 +1,241 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip.io; + +import java.io.IOException; +import java.net.ConnectException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.bridge.VeluxBridgeInstance; +import org.openhab.binding.velux.bridge.slip.util.Packet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 2nd Level I/O interface towards the Velux bridge. + * It provides methods for a pure client/server communication. + *

+ * The following class access methods exist: + *

    + *
  • {@link Connection#io} for a complete pair of request and response messages,
  • + *
  • {@link Connection#isAlive} to check the presence of a connection,
  • + *
  • {@link Connection#isMessageAvailable} to check the presence of an incoming message,
  • + *
  • {@link Connection#lastSuccessfulCommunication} returns the timestamp of the last successful communication,
  • + *
  • {@link Connection#lastCommunication} returns the timestamp of the last communication,
  • + *
  • {@link Connection#resetConnection} for resetting the current connection.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public class Connection { + private final Logger logger = LoggerFactory.getLogger(Connection.class); + + /* + * *************************** + * ***** Private Objects ***** + */ + + /** + * Timestamp of last successful communication in milliseconds. + */ + private long lastSuccessfulCommunicationInMSecs = 0; + /** + * Timestamp of last communication in milliseconds. + */ + private long lastCommunicationInMSecs = 0; + /** + * SSL socket for communication. + */ + private SSLconnection connectivity = SSLconnection.UNKNOWN; + + /* + * ************************** + * ***** Public Methods ***** + */ + + /** + * Base level communication with the SlipVeluxBridg. + * + * @param bridgeInstance describing the Service Access Point location i.e. hostname and TCP port. + * @param request as Array of bytes representing the structure of the message to be send. + * @return response of type Array of byte containing all received informations. + * @throws java.net.ConnectException in case of unrecoverable communication failures. + * @throws java.io.IOException in case of continuous communication I/O failures. + */ + public synchronized byte[] io(VeluxBridgeInstance bridgeInstance, byte[] request) + throws ConnectException, IOException { + logger.trace("io() called."); + + /** Local handles */ + int retryCount = 0; + IOException lastIOE = new IOException("Unexpected I/O exception."); + + do { + try { + if (!connectivity.isReady()) { + try { + // From configuration + String host = bridgeInstance.veluxBridgeConfiguration().ipAddress; + int port = bridgeInstance.veluxBridgeConfiguration().tcpPort; + + logger.trace("io(): connecting to {}:{}.", host, port); + connectivity = new SSLconnection(host, port); + } catch (ConnectException ce) { + // ToDo: eliminate obsolete lines + // logger.warn("io(): raised a non-recoverable error during connection setup: + // {}.",ce.getMessage()); + // throw ce; + throw new ConnectException(String + .format("raised a non-recoverable error during connection setup: %s", ce.getMessage())); + } catch (Exception e) { + logger.warn("io(): raised an error during connection setup: {}.", e.getMessage()); + lastIOE = new IOException(String.format("error during connection setup: %s.", e.getMessage())); + continue; + } + } + if (request.length > 0) { + try { + if (logger.isTraceEnabled()) { + logger.trace("io(): sending packet with {} bytes: {}", request.length, new Packet(request)); + } else { + logger.debug("io(): sending packet of size {}.", request.length); + } + if (connectivity.isReady()) { + connectivity.send(request); + } + } catch (Exception e) { + logger.warn("io(): raised an error during sending: {}.", e.getMessage()); + break; + } + + // Give the bridge some time to breathe + if (bridgeInstance.veluxBridgeConfiguration().timeoutMsecs > 0) { + logger.trace("io(): wait time {} msecs.", + bridgeInstance.veluxBridgeConfiguration().timeoutMsecs); + try { + Thread.sleep(bridgeInstance.veluxBridgeConfiguration().timeoutMsecs); + } catch (InterruptedException ie) { + logger.trace("io() wait interrupted."); + } + } + } + byte[] packet = new byte[0]; + logger.trace("io(): receiving bytes."); + if (connectivity.isReady()) { + packet = connectivity.receive(); + } + if (logger.isTraceEnabled()) { + logger.trace("io(): received packet with {} bytes: {}", packet.length, new Packet(packet)); + } else { + logger.debug("io(): received packet with {} bytes.", packet.length); + } + lastSuccessfulCommunicationInMSecs = System.currentTimeMillis(); + lastCommunicationInMSecs = lastSuccessfulCommunicationInMSecs; + logger.trace("io() finished."); + return packet; + } catch (IOException ioe) { + logger.warn("io(): Exception occurred during I/O: {}.", ioe.getMessage()); + lastIOE = ioe; + // Error Retries with Exponential Backoff + long waitTime = ((long) Math.pow(2, retryCount) + * bridgeInstance.veluxBridgeConfiguration().timeoutMsecs); + logger.trace("io(): wait time {} msecs.", waitTime); + try { + Thread.sleep(waitTime); + } catch (InterruptedException ie) { + logger.trace("io(): wait interrupted."); + } + } + } while (retryCount++ < bridgeInstance.veluxBridgeConfiguration().retries); + if (retryCount >= bridgeInstance.veluxBridgeConfiguration().retries) { + logger.info("io(): socket I/O failed {} times.", bridgeInstance.veluxBridgeConfiguration().retries); + } + logger.trace("io(): shutting down connection."); + if (connectivity.isReady()) { + connectivity.close(); + } + logger.trace("io() finishes with failure by throwing exception."); + throw lastIOE; + } + + /** + * Returns the status of the current connection. + * + * @return state as boolean. + */ + public boolean isAlive() { + logger.trace("isAlive(): called."); + return connectivity.isReady(); + } + + /** + * Returns the availability of an incoming message. + * + * @return state as boolean. + */ + public synchronized boolean isMessageAvailable() { + logger.trace("isMessageAvailable(): called."); + try { + if ((connectivity.isReady()) && (connectivity.available())) { + logger.trace("isMessageAvailable(): there is a message waiting."); + return true; + } + } catch (IOException e) { + logger.trace("isMessageAvailable(): lost connection due to {}.", e.getMessage()); + resetConnection(); + } + logger.trace("isMessageAvailable(): no message waiting."); + return false; + } + + /** + * Returns the timestamp in milliseconds since Unix epoch + * of last successful communication. + * + * @return timestamp in milliseconds. + */ + public long lastSuccessfulCommunication() { + return lastSuccessfulCommunicationInMSecs; + } + + /** + * Returns the timestamp in milliseconds since Unix epoch + * of last communication. + * + * @return timestamp in milliseconds. + */ + public long lastCommunication() { + return lastCommunicationInMSecs; + } + + /** + * Resets an open connectivity by closing the socket and resetting the authentication information. + */ + public synchronized void resetConnection() { + logger.trace("resetConnection() called."); + if (connectivity.isReady()) { + logger.trace("resetConnection(): shutting down connection."); + try { + if (connectivity.isReady()) { + connectivity.close(); + } + } catch (IOException e) { + logger.warn("resetConnection(): raised an error during connection close: {}.", e.getMessage()); + } + logger.trace("resetConnection(): clearing authentication token."); + } + logger.trace("resetConnection() done."); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/io/SSLconnection.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/io/SSLconnection.java new file mode 100644 index 0000000000000..a0ac986266b22 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/io/SSLconnection.java @@ -0,0 +1,245 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip.io; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.ConnectException; +import java.net.UnknownHostException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Transport layer supported by the Velux bridge. + *

+ * SLIP-based 2nd Level I/O interface towards the Velux bridge. + *

+ * It provides methods for pre- and post-communication + * as well as a common method for the real communication. + *

    + *
  • {@link SSLconnection#SSLconnection} for establishing the connection,
  • + *
  • {@link SSLconnection#send} for sending a message to the bridge,
  • + *
  • {@link SSLconnection#available} for observation whether there are bytes available,
  • + *
  • {@link SSLconnection#receive} for receiving a message from the bridge,
  • + *
  • {@link SSLconnection#close} for tearing down the connection.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SSLconnection { + private final Logger logger = LoggerFactory.getLogger(SSLconnection.class); + + // Public definition + public static final SSLconnection UNKNOWN = new SSLconnection(); + + /* + * *************************** + * ***** Private Objects ***** + */ + + private static final int CONNECTION_BUFFER_SIZE = 4096; + + private boolean ready = false; + private @Nullable SSLSocket socket; + private @Nullable DataOutputStream dOut; + private @Nullable DataInputStream dIn; + + /** + * Fake trust manager to suppress any certificate errors, + * used within {@link #SSLconnection} for seamless operation + * even on self-signed certificates like provided by Velux. + */ + private final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { + @Override + public X509Certificate @Nullable [] getAcceptedIssuers() { + return null; + } + + @Override + public void checkClientTrusted(X509Certificate @Nullable [] arg0, @Nullable String arg1) + throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate @Nullable [] arg0, @Nullable String arg1) + throws CertificateException { + } + } }; + + /* + * ************************ + * ***** Constructors ***** + */ + + /** + * Constructor for initialization of an unfinished connectivity. + */ + SSLconnection() { + logger.debug("SSLconnection() called."); + ready = false; + logger.trace("SSLconnection() finished."); + } + + /** + * Constructor to setup and establish a connection. + * + * @param host as String describing the Service Access Point location i.e. hostname. + * @param port as String describing the Service Access Point location i.e. TCP port. + * @throws java.net.ConnectException in case of unrecoverable communication failures. + * @throws java.io.IOException in case of continuous communication I/O failures. + * @throws java.net.UnknownHostException in case of continuous communication I/O failures. + */ + SSLconnection(String host, int port) throws ConnectException, IOException, UnknownHostException { + logger.debug("SSLconnection({},{}) called.", host, port); + logger.info("Starting {} bridge connection.", VeluxBindingConstants.BINDING_ID); + SSLContext ctx = null; + try { + ctx = SSLContext.getInstance("SSL"); + ctx.init(null, trustAllCerts, null); + } catch (Exception e) { + throw new IOException("create of an empty trust store failed."); + } + logger.trace("SSLconnection(): creating socket..."); + // Just for avoidance of Potential null pointer access + SSLSocket socketX = (SSLSocket) ctx.getSocketFactory().createSocket(host, port); + logger.trace("SSLconnection(): starting SSL handshake..."); + if (socketX != null) { + socketX.startHandshake(); + dOut = new DataOutputStream(socketX.getOutputStream()); + dIn = new DataInputStream(socketX.getInputStream()); + ready = true; + socket = socketX; + } + logger.trace("SSLconnection() finished."); + } + + /* + * ************************** + * ***** Public Methods ***** + */ + + /** + * Method to query the readiness of the connection. + * + * @return ready as boolean for an established connection. + */ + synchronized boolean isReady() { + return ready; + } + + /** + * Method to pass a message towards the bridge. + * + * @param packet as Array of bytes to be transmitted towards the bridge via the established connection. + * @throws java.io.IOException in case of a communication I/O failure. + */ + @SuppressWarnings("null") + synchronized void send(byte[] packet) throws IOException { + logger.trace("send() called, writing {} bytes.", packet.length); + if (!ready || (dOut == null)) { + throw new IOException(); + } + dOut.write(packet, 0, packet.length); + if (logger.isTraceEnabled()) { + StringBuilder sb = new StringBuilder(); + for (byte b : packet) { + sb.append(String.format("%02X ", b)); + } + logger.trace("send() finished after having send {} bytes: {}", packet.length, sb.toString()); + } + } + + /** + * Method to verify that there is message from the bridge. + * + * @return true if there are any bytes ready to be queried using {@link SSLconnection#receive}. + * @throws java.io.IOException in case of a communication I/O failure. + */ + synchronized boolean available() throws IOException { + logger.trace("available() called."); + if (!ready || (dIn == null)) { + throw new IOException(); + } + @SuppressWarnings("null") + int availableBytes = dIn.available(); + logger.trace("available(): found {} bytes ready to be read (> 0 means true).", availableBytes); + return availableBytes > 0; + } + + /** + * Method to get a message from the bridge. + * + * @return packet as Array of bytes as received from the bridge via the established connection. + * @throws java.io.IOException in case of a communication I/O failure. + */ + synchronized byte[] receive() throws IOException { + logger.trace("receive() called."); + if (!ready || (dIn == null)) { + throw new IOException(); + } + byte[] message = new byte[CONNECTION_BUFFER_SIZE]; + @SuppressWarnings("null") + int messageLength = dIn.read(message, 0, message.length); + byte[] packet = new byte[messageLength]; + System.arraycopy(message, 0, packet, 0, messageLength); + if (logger.isTraceEnabled()) { + StringBuilder sb = new StringBuilder(); + for (byte b : packet) { + sb.append(String.format("%02X ", b)); + } + logger.trace("receive() finished after having read {} bytes: {}", messageLength, sb.toString()); + } + return packet; + } + + /** + * Destructor to tear down a connection. + * + * @throws java.io.IOException in case of a communication I/O failure. + */ + synchronized void close() throws IOException { + logger.debug("close() called."); + ready = false; + logger.info("Shutting down Velux bridge connection."); + // Just for avoidance of Potential null pointer access + DataInputStream dInX = dIn; + if (dInX != null) { + dInX.close(); + } + // Just for avoidance of Potential null pointer access + DataOutputStream dOutX = dOut; + if (dOutX != null) { + dOutX.close(); + } + // Just for avoidance of Potential null pointer access + SSLSocket socketX = socket; + if (socketX != null) { + socketX.close(); + } + logger.trace("close() finished."); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/io/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/io/package-info.java new file mode 100644 index 0000000000000..1a1994d1e7ad5 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/io/package-info.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2010-2019 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 + */ +/** + * SLIP-protocol I/O with the bridge. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.bridge.slip.io; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/package-info.java new file mode 100644 index 0000000000000..5c005546541ad --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/package-info.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2010-2019 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 + */ +/** + * SLIP-protocol specific implementations of the interactions to IO-homecontrolled devices. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.bridge.slip; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/KLF200Response.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/KLF200Response.java new file mode 100644 index 0000000000000..37a636dc3f651 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/KLF200Response.java @@ -0,0 +1,164 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip.util; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; + +/** + * Utility class for handling of KLF200 response packets. + * + *

+ * Methods available: + *

+ * Static methods are: + *

    + *
  • {@link #introLogging} for logging used at the beginning of the response handling.
  • + *
  • {@link #errorLogging} for error logging during processing.
  • + *
  • {@link #outroLogging} logging used at the end of the response handling.
  • + *
  • {@link #isLengthValid} for validation of length of the data part of the message.
  • + *
  • {@link #check4matchingNodeID} for validation of node identifier.
  • + *
  • {@link #check4matchingSessionID} for validation of session identifier.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public class KLF200Response { + + /** + * Provides user-oriented logging in two log levels for monitoring KLF behavior. + *

+ * Introduction logging used at the beginning of the response handling. + * + * @param logger Instantiated logging class to be used for output. + * @param responseCommand The command byte of the response packet. + * @param thisResponseData The array of bytes which are passed back within the response package. + */ + public static void introLogging(Logger logger, short responseCommand, byte[] thisResponseData) { + logger.debug("setResponse({} with {} bytes of data) called.", Command.get(responseCommand).toString(), + thisResponseData.length); + logger.trace("setResponse(): handling response {} ({}).", Command.get(responseCommand).toString(), + new CommandNumber(responseCommand).toString()); + } + + /** + * Provides user-oriented logging in two log levels for monitoring KLF behavior. + *

+ * Error logging used at the point where an unexpected or unrecognized command has been received. + * + * @param logger Instantiated logging class to be used for output. + * @param responseCommand The command byte of the response packet. + */ + public static void errorLogging(Logger logger, short responseCommand) { + logger.trace("setResponse(): cannot handle response {} ({}).", Command.get(responseCommand).toString(), + responseCommand); + logger.warn("Gateway response {} ({}) cannot be handled at this point of interaction.", + Command.get(responseCommand).toString(), responseCommand); + } + + /** + * Provides user-oriented logging in two log levels for monitoring KLF behavior. + *

+ * Conclusion logging used at the end of the response handling. + * + * @param logger Instantiated logging class to be used for output. + * @param success Describes the success of the response processing. + * @param finished Describes whether the response processing has come to an end. + */ + public static void outroLogging(Logger logger, boolean success, boolean finished) { + logger.trace("setResponse(): finished={},success={}.", finished, success); + } + + /** + * Provides user-oriented logging in two log levels for monitoring KLF behavior. + *

+ * Check the intended length of the response packet. + * + * @param logger Instantiated logging class to be used for output. + * @param responseCommand The command byte of the response packet. + * @param thisResponseData The array of bytes which are passed back within the response package. + * @param requiredResponseDataLength The expected size of the array of bytes which are passed back within the + * response package. + * @return isLengthValid of type boolean which signals the validity of the packet length. + */ + public static boolean isLengthValid(Logger logger, short responseCommand, byte[] thisResponseData, + int requiredResponseDataLength) { + logger.trace("isLengthValid() called for {} ({}) with {} bytes of data.", + Command.get(responseCommand).toString(), new CommandNumber(responseCommand).toString(), + thisResponseData.length); + if (thisResponseData.length != requiredResponseDataLength) { + logger.warn( + "Gateway response {} ({}) consists of a malformed packet (effective length is {}, required length is {}).", + Command.get(responseCommand).toString(), new CommandNumber(responseCommand).toString(), + thisResponseData.length, requiredResponseDataLength); + return false; + } + logger.trace("isLengthValid() returns {}.", true); + return true; + } + + /** + * Provides user-oriented logging in two log levels for monitoring KLF behavior. + *

+ * Internal support method to match two values for equality. + * + * @param logger Instantiated logging class to be used for output. + * @param idName String describing the type of values being compared. + * @param requestID Value of type int have been replaced within the request. + * @param responseID Value of type int being received within the response. + * @return check4matchingAnyID of type boolean which signals the equality. + */ + private static boolean check4matchingAnyID(Logger logger, String idName, int requestID, int responseID) { + logger.trace("check4matchingAnyID() called for request{} {} and response{} {}.", idName, requestID, idName, + responseID); + if (requestID != responseID) { + logger.warn("Gateway response with {} {} unexpected as query asked for {} {}.", idName, requestID, idName, + responseID); + return false; + } + logger.trace("check4matchingAnyID() returns {}.", true); + return true; + } + + /** + * Compare the node identifier of the request with the node identifier within the response + * with user-oriented logging in two log levels for monitoring KLF behavior. + * + * @param logger Instantiated logging class to be used for output. + * @param reqNodeID Node identifier of the request. + * @param cfmNodeID Node identifier of the response. + * @return success of type boolean which signals the success of the communication. + */ + public static boolean check4matchingNodeID(Logger logger, int reqNodeID, int cfmNodeID) { + logger.trace("check4matchingNodeID() called for requestNodeID {} and responseNodeID {}.", reqNodeID, cfmNodeID); + return check4matchingAnyID(logger, "NodeID", reqNodeID, cfmNodeID); + } + + /** + * Compare the session identifier of the request with the session identifier within the response + * with user-oriented logging in two log levels for monitoring KLF behavior. + * + * @param logger Instantiated logging class to be used for output. + * @param reqSessionID Session identifier of the request. + * @param cfmSessionID Session identifier of the response. + * @return success of type boolean which signals the success of the communication. + */ + public static boolean check4matchingSessionID(Logger logger, int reqSessionID, int cfmSessionID) { + logger.trace("check4matchingSessionID() called for requestNodeID {} and responseNodeID {}.", reqSessionID, + cfmSessionID); + return check4matchingAnyID(logger, "SessionID", reqSessionID, cfmSessionID); + } +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/Packet.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/Packet.java new file mode 100644 index 0000000000000..5de338248fd39 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/Packet.java @@ -0,0 +1,277 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip.util; + +import java.util.Arrays; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Utility class for handling of packets i.e. array of bytes. + * + *

+ * Methods available: + *

    + *
  • {@link #Packet(byte[])} creates a Packet object based on the array of bytes.
  • + *
  • {@link #length()} returns the number of bytes contained within this Packet.
  • + *
  • {@link #toByteArray} returns the complete Packet as array of bytes.
  • + *
  • {@link #getByteArray} returns the a range of bytes as array of bytes.
  • + *
  • {@link #toString} returns the complete packet as human-readable String.
  • + *
  • {@link #getOneByteValue} returns the value of one byte as int.
  • + *
  • {@link #setOneByteValue} sets the value of one byte within the Packet.
  • + *
  • {@link #getTwoByteValue} returns the value of two bytes as int.
  • + *
  • {@link #setTwoByteValue} sets the value of two bytes within the Packet.
  • + *
  • {@link #getFourByteValue(int)} returns the value of four bytes as int.
  • + *
  • {@link #setFourByteValue} sets the value of four bytes within the Packet.
  • + *
  • {@link #getString} returns the value of a range of bytes as String.
  • + *
  • {@link #setString} sets the value of a range of bytes within the Packet.
  • + *
+ * Static methods are: + *
    + *
  • {@link #shortToString} converts a byte into a human-readable hex-String.
  • + *
  • {@link #byteArrayToInt} converts a range of four bytes into an int.
  • + *
  • {@link #intToIPAddressString} converts an int into a String.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public class Packet { + + /* + * =========================================================== + * Internal Objects + */ + + private static final String BLANK = " "; + + private byte[] data; + + /* + * =========================================================== + * Constructor Method + */ + + /** + * Constructor: Create a {@link org.openhab.binding.velux.bridge.slip.util.Packet Packet} out of a sequence of + * bytes. + * + * @param thisData Packet as Array of bytes. + */ + public Packet(byte[] thisData) { + this.data = thisData; + } + + /* + * =========================================================== + * Access Methods + */ + + /** + * Returns the length of the {@link org.openhab.binding.velux.bridge.slip.util.Packet Packet}.. + * + * @return packetLength + * of type int. + */ + public int length() { // NO_UCD (unused code) + return data.length; + } + + /** + * Returns the complete {@link org.openhab.binding.velux.bridge.slip.util.Packet Packet} as sequence of bytes. + * + * @return packet + * of type Array-of-byte. + */ + public byte[] toByteArray() { + return data; + } + + /** + * Returns a part of the {@link org.openhab.binding.velux.bridge.slip.util.Packet Packet} as sequence of bytes + * starting at position (n) up to the position (n+length-1). + * + * @param position Position (n) within the packet. + * @param length Length of the intended slice as int. + * @return packet of type Array-of-byte. + */ + public byte[] getByteArray(int position, int length) { + return Arrays.copyOfRange(data, position, position + length); + } + + /** + * Returns the complete {@link org.openhab.binding.velux.bridge.slip.util.Packet Packet} + * as human-readable sequence of hex bytes each separated by the given separator. + * + * @param separator as of Type String. + * @return packetString of type String. + */ + public String toString(String separator) { // NO_UCD (use default) + StringBuilder sb = new StringBuilder(); + for (byte b : this.data) { + sb.append(String.format("%02X", b)); + sb.append(separator); + } + if (sb.lastIndexOf(separator) > 0) { + sb.deleteCharAt(sb.lastIndexOf(separator)); + } + return (sb.toString()); + } + + /** + * Returns the complete {@link org.openhab.binding.velux.bridge.slip.util.Packet Packet} + * as human-readable sequence of hex bytes each separated by a blank. + * + * @return packetString of type String. + */ + @Override + public String toString() { + return this.toString(BLANK); + } + + /** + * Returns the value of the byte at (n)th position as int value for convenience. + * + * @param position Position (n) within the packet. + * @return value of type int. + */ + public int getOneByteValue(int position) { + return (data[position] & 0xff); + } + + /** + * Modifies the value of the byte at (n)th position by setting it to the value passed as int. + * + * @param position Position (n) within the packet. + * @param value of type int. + */ + public void setOneByteValue(int position, int value) { + data[position] = (byte) value; + } + + /** + * Returns the value of the bytes at the positions (n)th and (n+1) as int value for convenience. + *

+ * Note: Big-endian LSB-0 encoding. + *

+ * + * @param position Position (n) within the packet. + * @return value of type int. + */ + public int getTwoByteValue(int position) { + return 0x00 << 24 | 0x00 << 16 | (data[position] & 0xff) << 8 | (data[position + 1] & 0xff); + } + + /** + * Modifies the value of the bytes at the positions (n) and (n+1) by setting it to the value passed as int. + *

+ * Note: Big-endian LSB-0 encoding. + *

+ * + * @param position Position (n) within the packet. + * @param value of type int. + */ + public void setTwoByteValue(int position, int value) { + data[position] = (byte) ((value >>> 8) & 0xFF); + data[position + 1] = (byte) (value & 0xFF); + } + + /** + * Returns the value of the bytes at the positions (n)th to (n+3) as int value for convenience. + *

+ * Note: Big-endian LSB-0 encoding. + *

+ * + * @param position Position (n) within the packet. + * @return value of type int. + */ + public int getFourByteValue(int position) { + return data[position] << 24 | (data[position + 1] & 0xff) << 16 | (data[position + 2] & 0xff) << 8 + | (data[position + 3] & 0xff); + } + + /** + * Modifies the value of the bytes at the positions (n) to (n+3) by setting it to the value passed as int. + *

+ * Note: Big-endian LSB-0 encoding. + *

+ * + * @param position Position (n) within the packet. + * @param value of type int. + */ + public void setFourByteValue(int position, int value) { // NO_UCD (unused code) + data[position] = (byte) ((value >>> 24) & 0xFF); + data[position + 1] = (byte) ((value >>> 16) & 0xFF); + data[position + 2] = (byte) ((value >>> 8) & 0xFF); + data[position + 3] = (byte) (value & 0xFF); + } + + /** + * Returns the char string converted byte-by-byte starting at the position (n) up to (n+length+1). + *

+ * Note: Any trailing null char will be eliminated. + *

+ * + * @param position Position (n) within the packet. + * @param length Length of the intended slice as int. + * @return value of type String. + */ + public String getString(int position, int length) { + return new String(Arrays.copyOfRange(data, position, position + length - 1)).replace("\0", ""); + } + + /** + * Modifies the value of the bytes starting at the position (n) by setting it to the character values passed as + * String. + *

+ * Note: The trailing null char will not be stored. + *

+ * + * @param position Position (n) within the packet. + * @param text of type String. + */ + public void setString(int position, String text) { // NO_UCD (unused code) + System.arraycopy(text, 0, data, 0, text.length()); + } + + /* + * =========================================================== + * Conversion Methods + */ + + /** + * Returns the hex char string representing the byte. + * + * @param oneByte of type byte to be converted. + * @return hexByteString of type String. + */ + static String shortToString(int oneByte) { + return String.format("%02X", oneByte); + } + + static int byteArrayToInt(byte[] data) { // NO_UCD (unused code) + return data[0] << 24 | (data[1] & 0xff) << 16 | (data[2] & 0xff) << 8 | (data[3] & 0xff); + } + + /** + * Returns the dotted string representing an IP address. + * + * @param ipAddress of type int to be converted. + * @return ipAddressString of type String. + */ + public static String intToIPAddressString(int ipAddress) { + return String.format("%d.%d.%d.%d", ((ipAddress >>> 24) & 0xFF), ((ipAddress >>> 16) & 0xFF), + ((ipAddress >>> 8) & 0xFF), (ipAddress & 0xFF)); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/SlipEncoding.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/SlipEncoding.java new file mode 100644 index 0000000000000..7c9f77170b7f2 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/SlipEncoding.java @@ -0,0 +1,170 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip.util; + +import java.nio.ByteBuffer; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Transport layer supported by the Velux bridge. + *

+ * Module semantic: encoding and decoding of frames according to RFC 1055. + *

+ * It defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.bridge.common.BridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + *

+ * Methods available: + *

    + *
  • {@link #SlipEncoding(short command, byte[] data) } builds a message based on command and data.
  • + *
  • {@link #SlipEncoding(byte[] thisPacket) } splits a message into command and data.
  • + *
  • {@link #isValid} returns the number of bytes contained within this Packet.
  • + *
  • {@link #getCommand} returns the Command part of the message.
  • + *
  • {@link #getData} returns the data part of the message.
  • + *
  • {@link #toMessage} returns the complete message.
  • + *
  • {@link #toString} returns the message in a human-readable way.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public class SlipEncoding { + + private final Logger logger = LoggerFactory.getLogger(SlipEncoding.class); + + private static final byte PROTOCOL_ID = 0; + private static boolean encodingValid = false; + private static byte[] message = new byte[0]; + + /** + * Builds a message based on command and parameters. + * + * @param command Message type as short. + * @param data Parameters as Array of bytes. + */ + public SlipEncoding(short command, byte[] data) { + logger.trace("SlipEncoding(constructor) for command 0x{} with data size {} called.", + Integer.toHexString(new Short(command).intValue()), data.length); + if (data.length > 250) { + logger.warn("SlipEncoding(constructor) called with data size {}: too big, aborting.", data.length); + encodingValid = false; + } else { + byte checksum = 0; + message = new byte[data.length + 5]; + message[0] = PROTOCOL_ID; + message[1] = (byte) (3 + data.length); + message[2] = (byte) (command >>> 8); + message[3] = (byte) command; + message[4 + data.length] = 0; + System.arraycopy(data, 0, message, 4, data.length); + for (byte b : message) { + checksum = (byte) (checksum ^ b); + } + message[4 + data.length] = checksum; + logger.trace("SlipEncoding(constructor) successfully initialized, storing bytes: {}.", this.toString()); + encodingValid = true; + } + } + + /** + * Validates a message based on transfer syntax as Array-of-bytes. + * + * @param thisPacket Message as Array of bytes. + */ + + public SlipEncoding(byte[] thisPacket) { + logger.trace("SlipEncoding(constructor) called for decoding a packet with size {}.", thisPacket.length); + message = thisPacket; + encodingValid = false; + do { + // ProtocolID:Length:Command:Data(0-250):Checksum + if (message.length < 5) { + logger.warn("SlipEncoding(constructor) called with data size {}: Packet too short.", message.length); + break; + } + if (message[0] != PROTOCOL_ID) { + logger.warn("SlipEncoding(constructor) called: Unexpected PROTOCOL_ID (got {}).", + Packet.shortToString(message[0])); + break; + } + byte checksum = 0; + for (int i = 0; i < message.length - 1; i++) { + checksum = (byte) (checksum ^ message[i]); + } + if (message[message.length - 1] != checksum) { + logger.warn("SlipEncoding(constructor) Invalid packet checksum (got {} != calculated {}).", + Packet.shortToString(message[message.length - 1]), Packet.shortToString(checksum)); + logger.debug("SlipEncoding(constructor) packet is {}.", new Packet(message).toString(":")); + break; + } + logger.trace("SlipEncoding(constructor) successfully initialized with command 0x{} and data {}.", + Packet.shortToString(this.getCommand()), new Packet(this.getData()).toString()); + encodingValid = true; + } while (false); + } + + /** + * Returns the validity of the message content. + * + * @return encodingValid + * of type boolean as status of the encoding or decoding. + */ + public boolean isValid() { + return encodingValid; + } + + /** + * Extracts the command. + * + * @return command + * of type short as encoded within the message. + */ + public short getCommand() { + short command = ByteBuffer.wrap(new byte[] { message[2], message[3] }).getShort(); + logger.trace("getCommand() returns 0x{}.", String.format("%02X ", command)); + return command; + } + + /** + * Extracts the data i.e. parameters to the command. + * + * @return data + * of type Array-of-byte as encoded within the message. + */ + public byte[] getData() { + byte[] data = new byte[message.length - 5]; + System.arraycopy(message, 4, data, 0, message.length - 5); + logger.trace("getData() returns {} bytes: {}.", data.length, new Packet(data).toString()); + return data; + } + + /** + * Returns the complete message. + * + * @return message + * of type Array-of-byte. + */ + public byte[] toMessage() { + return message; + } + + @Override + public String toString() { + return new Packet(message).toString(); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/SlipRFC1055.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/SlipRFC1055.java new file mode 100644 index 0000000000000..fe29672cce9bc --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/SlipRFC1055.java @@ -0,0 +1,128 @@ +/** + * Copyright (c) 2010-2019 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.velux.bridge.slip.util; + +import java.text.ParseException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Transport layer supported by the Velux bridge: + * SLIP wrapping supported by the Velux bridge. + *

+ * Methods available: + *

    + *
  • {@link #encode(byte[] payload) } converts a given payload into transfer byte encoding.
  • + *
  • {@link #decode(byte[] packet) } converts a given transfer byte encoding into a payload.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public class SlipRFC1055 { + + private final Logger logger = LoggerFactory.getLogger(SlipRFC1055.class); + + private static final byte SLIP_BYTE_END = (byte) 0xC0; + private static final byte SLIP_BYTE_ESC = (byte) 0xDB; + private static final byte SLIP_BYTE_ESC_END = (byte) 0xDC; + private static final byte SLIP_BYTE_ESC_ESC = (byte) 0xDD; + + /** + * Converts a given payload into transfer byte encoding. + * + * @param payload Array of bytes to be transmitted. + * @return packet + * of type Array-of-byte as encoded payload. + */ + public byte[] encode(byte[] payload) { + logger.trace("encode() for data size {} called.", payload.length); + + int additional = 2; + for (byte b : payload) { + if ((b == SLIP_BYTE_ESC) || (b == SLIP_BYTE_END)) { + additional++; + } + } + byte[] packet = new byte[payload.length + additional]; + int packetIndex = 0; + packet[packetIndex++] = SLIP_BYTE_END; + + for (byte b : payload) { + if (b == SLIP_BYTE_ESC) { + packet[packetIndex++] = SLIP_BYTE_ESC; + packet[packetIndex++] = SLIP_BYTE_ESC_ESC; + } else if (b == SLIP_BYTE_END) { + packet[packetIndex++] = SLIP_BYTE_ESC; + packet[packetIndex++] = SLIP_BYTE_ESC_END; + } else { + packet[packetIndex++] = b; + } + } + packet[packetIndex++] = SLIP_BYTE_END; + assert (packetIndex == packet.length); + logger.trace("encode() provides transfer encoding: {}.", new Packet(packet).toString()); + return packet; + } + + /** + * Converts a given transfer byte encoding into a payload. + * + * @param packet Array of bytes as being received. + * @return payload + * of type Array-of-byte as decoded payload. + * @throws ParseException in case of decoding errors. + */ + public byte[] decode(byte[] packet) throws ParseException { + logger.trace("decode() for packet size {} called.", packet.length); + if (packet.length < 3) { + throw new ParseException("Packet too short", 0); + } + if (packet[0] != SLIP_BYTE_END) { + throw new ParseException("Unexpected byte at 1st position", 0); + } + ; + if (packet[packet.length - 1] != SLIP_BYTE_END) { + throw new ParseException("Unexpected byte at last position", 0); + } + ; + int additional = -2; + for (int i = 0; i < packet.length; i++) { + if (packet[i] == SLIP_BYTE_ESC) { + additional--; + } + } + byte[] payload = new byte[packet.length + additional]; + + int packetIndex = 0; + for (int i = 0; i < packet.length; i++) { + if ((i == 0) || (i == packet.length - 1)) { + continue; + } + if ((packet[i] == SLIP_BYTE_ESC) && (packet[i + 1] == SLIP_BYTE_ESC_ESC)) { + payload[packetIndex++] = SLIP_BYTE_ESC; + i++; + } else if ((packet[i] == SLIP_BYTE_ESC) && (packet[i + 1] == SLIP_BYTE_ESC_END)) { + payload[packetIndex++] = SLIP_BYTE_END; + i++; + } else { + payload[packetIndex++] = packet[i]; + } + } + logger.trace("decode() provides payload: {}.", new Packet(payload).toString()); + return payload; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/package-info.java new file mode 100644 index 0000000000000..6932e73866d3c --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/package-info.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2010-2019 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 + */ +/** + * Utility classes for the SLIP-protocol. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.bridge.slip.util; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/discovery/VeluxDiscoveryService.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/discovery/VeluxDiscoveryService.java new file mode 100644 index 0000000000000..6042cfcbd8ac9 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/discovery/VeluxDiscoveryService.java @@ -0,0 +1,236 @@ +/** + * Copyright (c) 2010-2019 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.velux.discovery; + +import static org.openhab.binding.velux.VeluxBindingConstants.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService; +import org.eclipse.smarthome.config.discovery.DiscoveryResult; +import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; +import org.eclipse.smarthome.config.discovery.DiscoveryService; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.VeluxBindingProperties; +import org.openhab.binding.velux.handler.VeluxBridgeHandler; +import org.openhab.binding.velux.internal.utils.ManifestInformation; +import org.openhab.binding.velux.things.VeluxProduct; +import org.openhab.binding.velux.things.VeluxProductSerialNo; +import org.openhab.binding.velux.things.VeluxScene; +import org.osgi.service.component.annotations.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxDiscoveryService} is responsible for discovering scenes on + * the current Velux Bridge. + * + * @author Guenther Schreiner - Initial contribution. + */ +// ToDo: check whether an immediate activation is preferable. +// Might be activated by: +// @Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.velux") +// +@NonNullByDefault +@Component(service = DiscoveryService.class, configurationPid = "discovery.velux") +public class VeluxDiscoveryService extends AbstractDiscoveryService implements Runnable { + private final Logger logger = LoggerFactory.getLogger(VeluxDiscoveryService.class); + + private static final int DISCOVER_TIMEOUT_SECONDS = 300; + + private static @Nullable VeluxBridgeHandler bridgeHandler = null; + + /** + * Initializes the {@link VeluxDiscoveryService} without a {@link VeluxBridgeHandler}. + *

+ * This is the main entry point for the OSGI DiscoveryService. + * + */ + public VeluxDiscoveryService() { + super(VeluxBindingConstants.SUPPORTED_THINGS_ITEMS, DISCOVER_TIMEOUT_SECONDS); + logger.trace("VeluxDiscoveryService(without Bridge) just initialized."); + } + + /** + * Initializes the {@link VeluxDiscoveryService} with a reference to the well-prepared environment with a + * {@link VeluxBridgeHandler}. + * + * @param bridge Initialized Velux bridge handler. + */ + public VeluxDiscoveryService(VeluxBridgeHandler bridge) { + super(VeluxBindingConstants.SUPPORTED_THINGS_ITEMS, DISCOVER_TIMEOUT_SECONDS); + logger.trace("VeluxDiscoveryService(bridge={}) just initialized.", bridge); + + synchronized (this) { + if (bridgeHandler == null) { + logger.trace("VeluxDiscoveryService(): registering bridge {} for lateron use for Discovery.", bridge); + } else if (!bridge.equals(bridgeHandler)) { + logger.trace("VeluxDiscoveryService(): replacing already registered bridge {} by {}.", bridgeHandler, + bridge); + } + bridgeHandler = bridge; + } + } + + @Override + public void deactivate() { + logger.trace("deactivate() called."); + super.deactivate(); + } + + @Override + protected synchronized void startScan() { + logger.trace("startScan() called."); + + logger.debug("startScan(): creating a thing of type binding."); + ThingUID thingUID = new ThingUID(THING_TYPE_BINDING, "org_openhab_binding_velux"); + // @formatter:off + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID) + .withProperty(VeluxBindingProperties.PROPERTY_BINDING_BUNDLEVERSION, ManifestInformation.getBundleVersion()) + // TODO: should work with: + // .withLabel("@text/binding.velux.name") + .withLabel("Velux Binding") + .build(); + // @formatter:on + logger.debug("startScan(): registering new thing {}.", discoveryResult); + thingDiscovered(discoveryResult); + + if (bridgeHandler == null) { + logger.debug("startScan(): VeluxDiscoveryService cannot proceed due to missing Velux bridge."); + } else { + logger.debug("startScan(): Starting Velux discovery scan for scenes and actuators."); + discoverScenes(); + discoverProducts(); + } + logger.trace("startScan() done."); + } + + @Override + public synchronized void stopScan() { + logger.trace("stopScan() called."); + super.stopScan(); + removeOlderResults(getTimestampOfLastScan()); + logger.trace("stopScan() done."); + } + + @Override + public void run() { + logger.trace("run() called."); + } + + /** + * Discover the gateway-defined scenes. + */ + private void discoverScenes() { + logger.trace("discoverScenes() called."); + // Just for avoidance of Potential null pointer access + VeluxBridgeHandler bridgeHandlerX = bridgeHandler; + if (bridgeHandlerX == null) { + logger.debug("discoverScenes(): VeluxDiscoveryService.bridgeHandlerR not initialized, aborting discovery."); + return; + } + ThingUID bridgeUID = bridgeHandlerX.getThing().getUID(); + logger.debug("discoverScenes(): discovering all scenes."); + for (VeluxScene scene : bridgeHandlerX.existingScenes().values()) { + String sceneName = scene.getName().toString(); + logger.trace("discoverScenes(): found scene {}.", sceneName); + + String label = sceneName.replaceAll("\\P{Alnum}", "_"); + logger.trace("discoverScenes(): using label {}.", label); + + ThingUID thingUID = new ThingUID(THING_TYPE_VELUX_SCENE, bridgeUID, label); + @SuppressWarnings("deprecation") + ThingTypeUID thingTypeUID = thingUID.getThingTypeUID(); + + // @formatter:off + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID) + .withThingType(thingTypeUID) + .withProperty(VeluxBindingProperties.PROPERTY_SCENE_NAME, sceneName) + .withRepresentationProperty(VeluxBindingProperties.PROPERTY_SCENE_NAME) + .withBridge(bridgeUID) + .withLabel(label) + .build(); + // @formatter:on + logger.debug("discoverScenes(): registering new thing {}.", discoveryResult); + thingDiscovered(discoveryResult); + } + logger.trace("discoverScenes() finished."); + } + + /** + * Discover the gateway-defined products/actuators. + */ + private void discoverProducts() { + logger.trace("discoverProducts() called."); + // Just for avoidance of Potential null pointer access + VeluxBridgeHandler bridgeHandlerX = bridgeHandler; + if (bridgeHandlerX == null) { + logger.debug("discoverScenes() VeluxDiscoveryService.bridgeHandlerR not initialized, aborting discovery."); + return; + } + ThingUID bridgeUID = bridgeHandlerX.getThing().getUID(); + logger.debug("discoverProducts(): discovering all actuators."); + for (VeluxProduct product : bridgeHandlerX.existingProducts().values()) { + String serialNumber = product.getSerialNumber(); + String actuatorName = product.getProductName().toString(); + logger.trace("discoverProducts() found actuator {} (name {}).", serialNumber, actuatorName); + String identifier; + if (serialNumber.equals(VeluxProductSerialNo.UNKNOWN)) { + identifier = actuatorName; + } else { + identifier = serialNumber; + } + String label = actuatorName.replaceAll("\\P{Alnum}", "_"); + logger.trace("discoverProducts(): using label {}.", label); + ThingUID thingUID = null; + boolean isInverted = false; + logger.trace("discoverProducts() dealing with {} (type {}).", product, product.getProductType()); + switch (product.getProductType()) { + case SLIDER_WINDOW: + logger.trace("discoverProducts(): creating window."); + thingUID = new ThingUID(THING_TYPE_VELUX_WINDOW, bridgeUID, label); + isInverted = true; + break; + + case SLIDER_SHUTTER: + logger.trace("discoverProducts(): creating rollershutter."); + thingUID = new ThingUID(THING_TYPE_VELUX_ROLLERSHUTTER, bridgeUID, label); + break; + + default: + logger.trace("discoverProducts(): creating actuator."); + thingUID = new ThingUID(THING_TYPE_VELUX_ACTUATOR, bridgeUID, label); + } + @SuppressWarnings("deprecation") + ThingTypeUID thingTypeUID = thingUID.getThingTypeUID(); + + // @formatter:off + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID) + .withThingType(thingTypeUID) + .withProperty(VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER, identifier) + .withProperty(VeluxBindingProperties.PROPERTY_ACTUATOR_NAME, actuatorName) + .withProperty(VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED, isInverted) + .withRepresentationProperty(VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER) + .withBridge(bridgeUID) + .withLabel(actuatorName) + .build(); + // @formatter:on + logger.debug("discoverProducts(): registering new thing {}.", discoveryResult); + thingDiscovered(discoveryResult); + } + logger.trace("discoverProducts() finished."); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/discovery/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/discovery/package-info.java new file mode 100644 index 0000000000000..71912c430c98e --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/discovery/package-info.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2010-2019 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 + */ +/** + * Classes for handling discovery of Velux devices beyond the Velux gateway. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.discovery; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/BridgeChannels.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/BridgeChannels.java new file mode 100644 index 0000000000000..c90cafe912009 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/BridgeChannels.java @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2010-2019 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.velux.handler; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +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.binding.BaseBridgeHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/*** + * The class {@link BridgeChannels} provides methods for dealing with + * properties. + *

    + *
  • {@link #getAllChannelUIDs}
  • + *
+ *

+ * Noninstantiable utility class + *

+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +final class BridgeChannels { + private static final Logger LOGGER = LoggerFactory.getLogger(BridgeChannels.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private BridgeChannels() { + throw new AssertionError(); + } + + /* + * ************************** + * ***** Public Methods ***** + */ + + /** + * Return the Channel identifiers of all child things and the bridge things. + *

+ * + * @param bridge which will be scrutinized for things. + * @return channelUIDs of type {@link Set} of {@link ChannelUID}s. + */ + static Set getAllChannelUIDs(BaseBridgeHandler bridge) { + LOGGER.trace("getAllChannels() called."); + Set channelUIDs = new HashSet(); + Set things = new HashSet(bridge.getThing().getThings()); + things.add(bridge.getThing()); + for (Thing thing : things) { + for (Channel channel : thing.getChannels()) { + channelUIDs.add(channel.getUID()); + } + } + LOGGER.trace("getAllChannels() returns {}.", channelUIDs); + return channelUIDs; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelActuatorLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelActuatorLimitation.java new file mode 100644 index 0000000000000..6032f23a50f7d --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelActuatorLimitation.java @@ -0,0 +1,239 @@ +/** + * Copyright (c) 2010-2019 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.velux.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.VeluxBindingProperties; +import org.openhab.binding.velux.bridge.common.GetProductLimitation; +import org.openhab.binding.velux.things.VeluxProduct; +import org.openhab.binding.velux.things.VeluxProductPosition; +import org.openhab.binding.velux.things.VeluxProductSerialNo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel position of the Thing actuator: + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ *
    + *
  • OpenHAB Event Bus → Velux bridge + *

    + * Sending commands and value updates by method {@link #handleCommand}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelActuatorLimitation extends VeluxChannelHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelActuatorLimitation.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelActuatorLimitation() { + throw new AssertionError(); + } + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler); + State newState = null; + do { // just for common exit + if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { + LOGGER.trace("handleRefresh(): there are some existing products."); + } + if (!ThingProperty.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER)) { + LOGGER.trace("handleRefresh(): aborting processing as {} is not set.", + VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER); + break; + } + String actuatorSerial = (String) ThingProperty.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER); + LOGGER.trace("handleRefresh(): actuatorSerial={}", actuatorSerial); + + // Handle value inversion + boolean propertyInverted = false; + if (ThingProperty.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) { + propertyInverted = (boolean) ThingProperty.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED); + } + boolean isInverted = propertyInverted || VeluxProductSerialNo.indicatesRevertedValues(actuatorSerial); + LOGGER.trace("handleRefresh(): isInverted={}.", isInverted); + actuatorSerial = VeluxProductSerialNo.cleaned(actuatorSerial); + + if (!thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts + .isRegistered(actuatorSerial)) { + LOGGER.info("handleRefresh(): cannot work on unknown actuator with serial {}.", actuatorSerial); + break; + } + LOGGER.trace("handleRefresh(): fetching actuator for {}.", actuatorSerial); + VeluxProduct thisProduct = thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts + .get(actuatorSerial); + LOGGER.trace("handleRefresh(): found actuator {}.", thisProduct); + + GetProductLimitation bcp = thisBridgeHandler.thisBridge.bridgeAPI().getProductLimitation(); + if (bcp == null) { + LOGGER.trace("handleRefresh(): aborting processing as handler is null."); + break; + } + bcp.setActuatorIdAndLimitationType(thisProduct.getBridgeProductIndex().toInt(), true); + if (thisBridgeHandler.thisBridge.bridgeCommunicate(bcp)) { + int limitation = bcp.getLimitation(); + if (bcp.isCommunicationSuccessful()) { + try { + VeluxProductPosition position = new VeluxProductPosition(limitation); + if (position.isValid()) { + PercentType positionAsPercent = position.getPositionAsPercentType(isInverted); + LOGGER.trace("handleRefresh(): found limitation of actuator at level {}.", + positionAsPercent); + newState = positionAsPercent; + } else { + LOGGER.trace("handleRefresh(): limitation level of actuator is unknown."); + } + } catch (Exception e) { + LOGGER.warn("handleRefresh(): getProducts() exception: {}.", e.getMessage()); + } + } + } + } while (false); // common exit + LOGGER.trace("handleRefresh() returns {}.", newState); + return newState; + } + + /** + * Communication method to update the real world according to the passed channel value (or command). + * + * @param channelUID The item passed as type {@link ChannelUID} for which to following command is addressed to. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param command The command passed as type {@link Command} for the mentioned item. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newValue ... + */ + static @Nullable Command handleCommand(ChannelUID channelUID, String channelId, Command command, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleCommand({},{},{},{}) called.", channelUID, channelId, command, thisBridgeHandler); + LOGGER.warn("handleCommand(): not yet implemented."); + Command newValue = null; + // do { // just for common exit + // if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { + // LOGGER.trace("handleCommand(): there are some existing products."); + // } + // if (!ThingProperty.exists(thisBridgeHandler, channelUID, + // VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER)) { + // LOGGER.trace("handleCommand(): aborting processing as actuatorSerial is not set."); + // break; + // } + // String actuatorSerial = (String) ThingProperty.getValue(thisBridgeHandler, channelUID, + // VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER); + // LOGGER.trace("handleCommand(): actuatorSerial={}", actuatorSerial); + // + // // Handle value inversion + // boolean propertyInverted = false; + // if (ThingProperty.exists(thisBridgeHandler, channelUID, + // VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) { + // propertyInverted = (boolean) ThingProperty.getValue(thisBridgeHandler, channelUID, + // VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED); + // } + // boolean isInverted = propertyInverted || VeluxProductSerialNo.indicatesRevertedValues(actuatorSerial); + // LOGGER.trace("handleCommand(): isInverted={}.", isInverted); + // actuatorSerial = VeluxProductSerialNo.cleaned(actuatorSerial); + // + // if (!thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts + // .isRegistered(actuatorSerial)) { + // LOGGER.info("handleCommand({},{}): cannot work on unknown actuator: {}.", channelUID.getAsString(), + // command, actuatorSerial); + // break; + // } + // LOGGER.trace("handleCommand(): fetching product for {}.", actuatorSerial); + // VeluxProduct thisProduct = thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts + // .get(actuatorSerial); + // LOGGER.trace("handleCommand(): found product {}.", thisProduct); + // + // VeluxProductPosition targetLevel = VeluxProductPosition.UNKNOWN; + // if (channelId.equals(CHANNEL_ACTUATOR_POSITION)) { + // if ((command instanceof UpDownType) && ((UpDownType) command == UpDownType.UP)) { + // LOGGER.trace("handleCommand(): found UP command."); + // targetLevel = isInverted ? new VeluxProductPosition(PercentType.HUNDRED) + // : new VeluxProductPosition(PercentType.ZERO); + // } else if ((command instanceof UpDownType) && ((UpDownType) command == UpDownType.DOWN)) { + // LOGGER.trace("handleCommand(): found DOWN command."); + // targetLevel = isInverted ? new VeluxProductPosition(PercentType.ZERO) + // : new VeluxProductPosition(PercentType.HUNDRED); + // } else if ((command instanceof StopMoveType) && ((StopMoveType) command == StopMoveType.STOP)) { + // LOGGER.trace("handleCommand(): found STOP command."); + // targetLevel = new VeluxProductPosition(); + // } else if (command instanceof PercentType) { + // LOGGER.trace("handleCommand(): found command of type PercentType."); + // PercentType ptCommand = (PercentType) command; + // if (isInverted) { + // ptCommand = new PercentType(PercentType.HUNDRED.intValue() - ptCommand.intValue()); + // } + // LOGGER.trace("handleCommand(): found command to set level to {}.", ptCommand); + // targetLevel = new VeluxProductPosition(ptCommand); + // } else { + // LOGGER.info("handleCommand({},{}): ignoring command.", channelUID.getAsString(), command); + // break; + // } + // } else { + // if ((command instanceof OnOffType) && ((OnOffType) command == OnOffType.ON)) { + // LOGGER.trace("handleCommand(): found ON command."); + // targetLevel = isInverted ? new VeluxProductPosition(PercentType.HUNDRED) + // : new VeluxProductPosition(PercentType.ZERO); + // } else if ((command instanceof OnOffType) && ((OnOffType) command == OnOffType.OFF)) { + // LOGGER.trace("handleCommand(): found OFF command."); + // targetLevel = isInverted ? new VeluxProductPosition(PercentType.ZERO) + // : new VeluxProductPosition(PercentType.HUNDRED); + // } else { + // LOGGER.info("handleCommand({},{}): ignoring command.", channelUID.getAsString(), command); + // break; + // } + // } + // LOGGER.debug("handleCommand(): sending command with target level {}.", targetLevel); + // new VeluxBridgeRunProductCommand().sendCommand(thisBridgeHandler.thisBridge, + // thisProduct.getBridgeProductIndex().toInt(), targetLevel); + // LOGGER.trace("handleCommand(): The new shutter level will be send through the home monitoring events."); + // + // if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { + // LOGGER.trace("handleCommand(): position of actuators are updated."); + // } + // } while (false); // common exit + return newValue; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelActuatorPosition.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelActuatorPosition.java new file mode 100644 index 0000000000000..510183f61091a --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelActuatorPosition.java @@ -0,0 +1,244 @@ +/** + * Copyright (c) 2010-2019 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.velux.handler; + +import static org.openhab.binding.velux.VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.library.types.StopMoveType; +import org.eclipse.smarthome.core.library.types.UpDownType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.VeluxBindingProperties; +import org.openhab.binding.velux.bridge.VeluxBridgeRunProductCommand; +import org.openhab.binding.velux.bridge.common.GetProduct; +import org.openhab.binding.velux.things.VeluxProduct; +import org.openhab.binding.velux.things.VeluxProductPosition; +import org.openhab.binding.velux.things.VeluxProductSerialNo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel position of the Thing actuator: + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ *
    + *
  • OpenHAB Event Bus → Velux bridge + *

    + * Sending commands and value updates by method {@link #handleCommand}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelActuatorPosition extends VeluxChannelHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelActuatorPosition.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelActuatorPosition() { + throw new AssertionError(); + } + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler); + State newState = null; + do { // just for common exit + if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { + LOGGER.trace("handleRefresh(): there are some existing products."); + } + if (!ThingProperty.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER)) { + LOGGER.trace("handleRefresh(): aborting processing as {} is not set.", + VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER); + break; + } + String actuatorSerial = (String) ThingProperty.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER); + LOGGER.trace("handleRefresh(): actuatorSerial={}", actuatorSerial); + + // Handle value inversion + boolean propertyInverted = false; + if (ThingProperty.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) { + propertyInverted = (boolean) ThingProperty.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED); + } + boolean isInverted = propertyInverted || VeluxProductSerialNo.indicatesRevertedValues(actuatorSerial); + LOGGER.trace("handleRefresh(): isInverted={}.", isInverted); + actuatorSerial = VeluxProductSerialNo.cleaned(actuatorSerial); + + if (!thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts + .isRegistered(actuatorSerial)) { + LOGGER.info("handleRefresh(): cannot work on unknown actuator with serial {}.", actuatorSerial); + break; + } + LOGGER.trace("handleRefresh(): fetching actuator for {}.", actuatorSerial); + VeluxProduct thisProduct = thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts + .get(actuatorSerial); + LOGGER.trace("handleRefresh(): found actuator {}.", thisProduct); + + GetProduct bcp = thisBridgeHandler.thisBridge.bridgeAPI().getProduct(); + if (bcp == null) { + LOGGER.trace("handleRefresh(): aborting processing as handler is null."); + break; + } + bcp.setProductId(thisProduct.getBridgeProductIndex().toInt()); + if (thisBridgeHandler.thisBridge.bridgeCommunicate(bcp)) { + thisProduct = bcp.getProduct(); + if (bcp.isCommunicationSuccessful()) { + try { + VeluxProductPosition position = new VeluxProductPosition(thisProduct.getCurrentPosition()); + if (position.isValid()) { + PercentType positionAsPercent = position.getPositionAsPercentType(isInverted); + LOGGER.trace("handleRefresh(): found actuator at level {}.", positionAsPercent); + newState = positionAsPercent; + } else { + LOGGER.trace("handleRefresh(): level of actuator is unknown."); + } + } catch (Exception e) { + LOGGER.warn("handleRefresh(): getProducts() exception: {}.", e.getMessage()); + } + } + } + } while (false); // common exit + LOGGER.trace("handleRefresh() returns {}.", newState); + return newState; + } + + /** + * Communication method to update the real world according to the passed channel value (or command). + * + * @param channelUID The item passed as type {@link ChannelUID} for which to following command is addressed to. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param command The command passed as type {@link Command} for the mentioned item. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newValue ... + */ + static @Nullable Command handleCommand(ChannelUID channelUID, String channelId, Command command, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleCommand({},{},{},{}) called.", channelUID, channelId, command, thisBridgeHandler); + Command newValue = null; + do { // just for common exit + assert thisBridgeHandler.bridgeParameters.actuators != null : "VeluxBridgeHandler.bridgeParameters.actuators not initialized."; + if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { + LOGGER.trace("handleCommand(): there are some existing products."); + } + if (!ThingProperty.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER)) { + LOGGER.trace("handleCommand(): aborting processing as actuatorSerial is not set."); + break; + } + String actuatorSerial = (String) ThingProperty.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER); + LOGGER.trace("handleCommand(): actuatorSerial={}", actuatorSerial); + + // Handle value inversion + boolean propertyInverted = false; + if (ThingProperty.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) { + propertyInverted = (boolean) ThingProperty.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED); + } + boolean isInverted = propertyInverted || VeluxProductSerialNo.indicatesRevertedValues(actuatorSerial); + LOGGER.trace("handleCommand(): isInverted={}.", isInverted); + actuatorSerial = VeluxProductSerialNo.cleaned(actuatorSerial); + + if (!thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts + .isRegistered(actuatorSerial)) { + LOGGER.info("handleCommand({},{}): cannot work on unknown actuator: {}.", channelUID.getAsString(), + command, actuatorSerial); + break; + } + LOGGER.trace("handleCommand(): fetching product for {}.", actuatorSerial); + VeluxProduct thisProduct = thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts + .get(actuatorSerial); + LOGGER.trace("handleCommand(): found product {}.", thisProduct); + + VeluxProductPosition targetLevel = VeluxProductPosition.UNKNOWN; + if (channelId.equals(CHANNEL_ACTUATOR_POSITION)) { + if ((command instanceof UpDownType) && ((UpDownType) command == UpDownType.UP)) { + LOGGER.trace("handleCommand(): found UP command."); + targetLevel = isInverted ? new VeluxProductPosition(PercentType.HUNDRED) + : new VeluxProductPosition(PercentType.ZERO); + } else if ((command instanceof UpDownType) && ((UpDownType) command == UpDownType.DOWN)) { + LOGGER.trace("handleCommand(): found DOWN command."); + targetLevel = isInverted ? new VeluxProductPosition(PercentType.ZERO) + : new VeluxProductPosition(PercentType.HUNDRED); + } else if ((command instanceof StopMoveType) && ((StopMoveType) command == StopMoveType.STOP)) { + LOGGER.trace("handleCommand(): found STOP command."); + targetLevel = new VeluxProductPosition(); + } else if (command instanceof PercentType) { + LOGGER.trace("handleCommand(): found command of type PercentType."); + PercentType ptCommand = (PercentType) command; + if (isInverted) { + ptCommand = new PercentType(PercentType.HUNDRED.intValue() - ptCommand.intValue()); + } + LOGGER.trace("handleCommand(): found command to set level to {}.", ptCommand); + targetLevel = new VeluxProductPosition(ptCommand); + } else { + LOGGER.info("handleCommand({},{}): ignoring command.", channelUID.getAsString(), command); + break; + } + } else { + if ((command instanceof OnOffType) && ((OnOffType) command == OnOffType.ON)) { + LOGGER.trace("handleCommand(): found ON command."); + targetLevel = isInverted ? new VeluxProductPosition(PercentType.HUNDRED) + : new VeluxProductPosition(PercentType.ZERO); + } else if ((command instanceof OnOffType) && ((OnOffType) command == OnOffType.OFF)) { + LOGGER.trace("handleCommand(): found OFF command."); + targetLevel = isInverted ? new VeluxProductPosition(PercentType.ZERO) + : new VeluxProductPosition(PercentType.HUNDRED); + } else { + LOGGER.info("handleCommand({},{}): ignoring command.", channelUID.getAsString(), command); + break; + } + } + LOGGER.debug("handleCommand(): sending command with target level {}.", targetLevel); + new VeluxBridgeRunProductCommand().sendCommand(thisBridgeHandler.thisBridge, + thisProduct.getBridgeProductIndex().toInt(), targetLevel); + LOGGER.trace("handleCommand(): The new shutter level will be send through the home monitoring events."); + + if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { + LOGGER.trace("handleCommand(): position of actuators are updated."); + } + } while (false); // common exit + return newValue; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeCheck.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeCheck.java new file mode 100644 index 0000000000000..87b48b70ea713 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeCheck.java @@ -0,0 +1,112 @@ +/** + * Copyright (c) 2010-2019 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.velux.handler; + +import java.util.ArrayList; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.VeluxBindingProperties; +import org.openhab.binding.velux.internal.VeluxItemType; +import org.openhab.binding.velux.things.VeluxScene; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel check of the Thing klf200 : + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelBridgeCheck extends VeluxChannelHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeCheck.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelBridgeCheck() { + throw new AssertionError(); + } + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler); + State newState = null; + do { // just for common exit + LOGGER.trace("handleCommand(): loop through all existing scenes."); + ArrayList unusedScenes = new ArrayList(); + for (VeluxScene scene : thisBridgeHandler.bridgeParameters.scenes.getChannel().existingScenes.values()) { + boolean found = false; + LOGGER.trace("handleCommand(): .loop through all handled channels."); + for (ChannelUID thisChannelUID : BridgeChannels.getAllChannelUIDs(thisBridgeHandler)) { + LOGGER.trace("handleCommand(): evaluating ChannelUID {}.", thisChannelUID); + VeluxItemType thisItemType = VeluxItemType.getByThingAndChannel( + thisBridgeHandler.thingTypeUIDOf(thisChannelUID), thisChannelUID.getId()); + if (!thisItemType.equals(VeluxItemType.SCENE_ACTION)) { + LOGGER.trace("handleCommand(): ignoring non SCENE_ACTION."); + continue; + } + if (!ThingProperty.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_SCENE_NAME)) { + LOGGER.trace("handleCommand(): aborting processing as scene name is not set."); + break; + } + String sceneName = (String) ThingProperty.getValue(thisBridgeHandler, thisChannelUID, + VeluxBindingProperties.PROPERTY_SCENE_NAME); + LOGGER.trace("handleCommand(): comparing {} with {}.", scene.getName().toString(), sceneName); + if (scene.getName().toString().equals(sceneName)) { + LOGGER.trace("handleCommand(): scene {} used within item {}.", scene.getName(), + thisChannelUID.getAsString()); + found = true; + } + } + if (!found) { + unusedScenes.add(scene.getName().toString()); + LOGGER.trace("handleCommand(): scene {} is currently unused.", scene.getName()); + } + } + String result; + if (unusedScenes.size() > 0) { + result = "@text/channelValue.check-integrity-failed" + " " + unusedScenes.toString() + "."; + } else { + result = "@text/channelValue.check-integrity-ok"; + } + LOGGER.info("{}", result); + newState = StateUtils.createState(result); + } while (false); // common exit + return newState; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeDoDetection.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeDoDetection.java new file mode 100644 index 0000000000000..39f098dca452a --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeDoDetection.java @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2010-2019 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.velux.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.velux.bridge.VeluxBridgeDetectProducts; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel doDetection of the Thing klf200 : + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ *
    + *
  • OpenHAB Event Bus → Velux bridge + *

    + * Sending commands and value updates by method {@link #handleCommand}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelBridgeDoDetection extends VeluxChannelHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeDoDetection.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelBridgeDoDetection() { + throw new AssertionError(); + } + + /** + * Communication method to update the real world according to the passed channel value (or command). + * + * @param channelUID The item passed as type {@link ChannelUID} for which to following command is addressed to. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param command The command passed as type {@link Command} for the mentioned item. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newValue ... + */ + static @Nullable Command handleCommand(ChannelUID channelUID, String channelId, Command command, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleCommand({},{},{},{}) called.", channelUID, channelId, command, thisBridgeHandler); + if (command.equals(OnOffType.ON)) { + LOGGER.trace("handleCommand(): about to activate veluxBridge detection mode."); + new VeluxBridgeDetectProducts().detectProducts(thisBridgeHandler.thisBridge); + } else { + LOGGER.trace("handleCommand(): ignoring OFF command."); + } + return null; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeFirmware.java new file mode 100644 index 0000000000000..43aa28a4dcef4 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeFirmware.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2010-2019 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.velux.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.bridge.VeluxBridgeGetFirmware; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel firmware of the Thing klf200 : + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelBridgeFirmware extends VeluxChannelHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeFirmware.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelBridgeFirmware() { + throw new AssertionError(); + } + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler); + State newState = null; + thisBridgeHandler.bridgeParameters.firmware = new VeluxBridgeGetFirmware() + .retrieve(thisBridgeHandler.thisBridge); + if (thisBridgeHandler.bridgeParameters.firmware.isRetrieved) { + newState = thisBridgeHandler.bridgeParameters.firmware.firmwareVersion; + } + LOGGER.trace("handleRefresh() returns {}.", newState); + return newState; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeLANconfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeLANconfig.java new file mode 100644 index 0000000000000..71f1c8f6be502 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeLANconfig.java @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2010-2019 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.velux.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.bridge.VeluxBridgeLANConfig; +import org.openhab.binding.velux.internal.VeluxItemType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channels ipAddress, subnetMask, defaultGW and DHCP of the Thing + * klf200 : + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelBridgeLANconfig extends VeluxChannelHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeLANconfig.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelBridgeLANconfig() { + throw new AssertionError(); + } + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler); + State newState = null; + thisBridgeHandler.bridgeParameters.lanConfig = new VeluxBridgeLANConfig() + .retrieve(thisBridgeHandler.thisBridge); + if (thisBridgeHandler.bridgeParameters.lanConfig.isRetrieved) { + VeluxItemType itemType = VeluxItemType.getByThingAndChannel(thisBridgeHandler.thingTypeUIDOf(channelUID), + channelUID.getId()); + switch (itemType) { + case BRIDGE_IPADDRESS: + newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABipAddress); + ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.CHANNEL_BRIDGE_IPADDRESS, + thisBridgeHandler.bridgeParameters.lanConfig.openHABipAddress); + break; + case BRIDGE_SUBNETMASK: + newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABsubnetMask); + ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.CHANNEL_BRIDGE_SUBNETMASK, + thisBridgeHandler.bridgeParameters.lanConfig.openHABsubnetMask); + break; + case BRIDGE_DEFAULTGW: + newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABdefaultGW); + ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.CHANNEL_BRIDGE_DEFAULTGW, + thisBridgeHandler.bridgeParameters.lanConfig.openHABdefaultGW); + break; + case BRIDGE_DHCP: + newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABenabledDHCP); + ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.CHANNEL_BRIDGE_DHCP, + thisBridgeHandler.bridgeParameters.lanConfig.openHABenabledDHCP); + default: + } + } + LOGGER.trace("handleRefresh() returns {}.", newState); + return newState; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeProducts.java new file mode 100644 index 0000000000000..c28f0087a230b --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeProducts.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2010-2019 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.velux.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel products of the Thing klf200: + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelBridgeProducts extends VeluxChannelHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeProducts.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelBridgeProducts() { + throw new AssertionError(); + } + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler); + State newState = null; + if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { + LOGGER.trace("handleCommand(): there are some existing products."); + } + String products = thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts.toString(); + LOGGER.trace("handleCommand(): found products {}.", products); + products = products.replaceAll("[^\\p{Punct}\\w]", "_"); + newState = StateUtils.createState(products); + LOGGER.trace("handleRefresh() returns {}.", newState); + return newState; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeScenes.java new file mode 100644 index 0000000000000..4903f554a5e63 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeScenes.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2010-2019 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.velux.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel scenes of the Thing klf200: + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelBridgeScenes extends VeluxChannelHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeScenes.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelBridgeScenes() { + throw new AssertionError(); + } + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler); + State newState = null; + if (thisBridgeHandler.bridgeParameters.scenes.autoRefresh(thisBridgeHandler.thisBridge)) { + LOGGER.trace("handleCommand(): there are some existing scenes."); + } + String sceneInfo = thisBridgeHandler.bridgeParameters.scenes.getChannel().existingScenes.toString(); + LOGGER.trace("handleCommand(): found scenes {}.", sceneInfo); + sceneInfo = sceneInfo.replaceAll("[^\\p{Punct}\\w]", "_"); + newState = StateUtils.createState(sceneInfo); + LOGGER.trace("handleRefresh() returns {}.", newState); + return newState; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeStatus.java new file mode 100644 index 0000000000000..cac22d421c708 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeStatus.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2010-2019 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.velux.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.bridge.VeluxBridgeDeviceStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel status of the Thing klf200: + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelBridgeStatus extends VeluxChannelHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeStatus.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelBridgeStatus() { + throw new AssertionError(); + } + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler); + State newState = null; + thisBridgeHandler.bridgeParameters.gateway = new VeluxBridgeDeviceStatus() + .retrieve(thisBridgeHandler.thisBridge); + if (thisBridgeHandler.bridgeParameters.gateway.isRetrieved) { + newState = thisBridgeHandler.bridgeParameters.gateway.gwState; + } + LOGGER.trace("handleRefresh() returns {}.", newState); + return newState; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeWLANconfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeWLANconfig.java new file mode 100644 index 0000000000000..97d3986aca550 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeWLANconfig.java @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2010-2019 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.velux.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.bridge.VeluxBridgeWLANConfig; +import org.openhab.binding.velux.internal.VeluxItemType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channels WLANSSID and WLANPassword of the Thing klf200 : + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelBridgeWLANconfig extends VeluxChannelHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeWLANconfig.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelBridgeWLANconfig() { + throw new AssertionError(); + } + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler); + State newState = null; + thisBridgeHandler.bridgeParameters.wlanConfig = new VeluxBridgeWLANConfig() + .retrieve(thisBridgeHandler.thisBridge); + if (thisBridgeHandler.bridgeParameters.wlanConfig.isRetrieved) { + VeluxItemType itemType = VeluxItemType.getByThingAndChannel(thisBridgeHandler.thingTypeUIDOf(channelUID), + channelUID.getId()); + switch (itemType) { + case BRIDGE_WLANSSID: + newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABipAddress); + ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.CHANNEL_BRIDGE_WLANSSID, + thisBridgeHandler.bridgeParameters.wlanConfig.openHABwlanSSID); + break; + case BRIDGE_WLANPASSWORD: + newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABsubnetMask); + ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.CHANNEL_BRIDGE_WLANPASSWORD, + thisBridgeHandler.bridgeParameters.wlanConfig.openHABwlanPassword); + break; + default: + } + } + LOGGER.trace("handleRefresh() returns {}.", newState); + return newState; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelSceneAction.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelSceneAction.java new file mode 100644 index 0000000000000..fa34d27add326 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelSceneAction.java @@ -0,0 +1,121 @@ +/** + * Copyright (c) 2010-2019 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.velux.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.velux.VeluxBindingProperties; +import org.openhab.binding.velux.bridge.VeluxBridgeRunScene; +import org.openhab.binding.velux.things.VeluxProductVelocity; +import org.openhab.binding.velux.things.VeluxScene; +import org.openhab.binding.velux.things.VeluxScene.SceneName; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel action of the Thing scene : + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ *
    + *
  • OpenHAB Event Bus → Velux bridge + *

    + * Sending commands and value updates by method {@link #handleCommand}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelSceneAction extends VeluxChannelHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelSceneAction.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelSceneAction() { + throw new AssertionError(); + } + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + + /** + * Communication method to update the real world according to the passed channel value (or command). + * + * @param channelUID The item passed as type {@link ChannelUID} for which to following command is addressed to. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param command The command passed as type {@link Command} for the mentioned item. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newValue ... + */ + static @Nullable Command handleCommand(ChannelUID channelUID, String channelId, Command command, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleCommand({},{},{},{}) called.", channelUID, channelId, command, thisBridgeHandler); + Command newValue = null; + do { // just for common exit + if (command != OnOffType.ON) { + LOGGER.trace("handleCommand(): ignoring OFF command."); + break; + } + if (!ThingProperty.exists(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_SCENE_NAME)) { + LOGGER.trace("handleCommand(): aborting processing as scene name is not set."); + break; + } + String sceneName = (String) ThingProperty.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_SCENE_NAME); + if (!thisBridgeHandler.bridgeParameters.scenes.getChannel().existingScenes + .isRegistered(new SceneName(sceneName))) { + LOGGER.info("handleCommand({},{}): cannot activate unknown scene: {}.", channelUID.getAsString(), + command, sceneName); + break; + } + String velocityName = (String) ThingProperty.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_SCENE_VELOCITY); + LOGGER.debug("handleCommand(): activating known scene {}.", sceneName); + VeluxScene thisScene = thisBridgeHandler.bridgeParameters.scenes.getChannel().existingScenes + .get(new SceneName(sceneName)); + LOGGER.trace("handleCommand(): execution of scene {}.", thisScene); + if (VeluxProductVelocity.getByName(velocityName) == VeluxProductVelocity.UNDEFTYPE) { + new VeluxBridgeRunScene().execute(thisBridgeHandler.thisBridge, + thisScene.getBridgeSceneIndex().toInt()); + } else { + LOGGER.trace("handleCommand(): using velocity {}.", + VeluxProductVelocity.getByName(velocityName).toString()); + new VeluxBridgeRunScene().execute(thisBridgeHandler.thisBridge, thisScene.getBridgeSceneIndex().toInt(), + VeluxProductVelocity.getByName(velocityName).getVelocity()); + } + LOGGER.trace("handleCommand(): execution of scene finished."); + } while (false); // common exit + return newValue; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelSceneSilentmode.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelSceneSilentmode.java new file mode 100644 index 0000000000000..5a8843167f9a9 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelSceneSilentmode.java @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2010-2019 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.velux.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.velux.VeluxBindingProperties; +import org.openhab.binding.velux.bridge.VeluxBridgeSetSceneVelocity; +import org.openhab.binding.velux.things.VeluxScene; +import org.openhab.binding.velux.things.VeluxScene.SceneName; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel silentMode of the Thing scene : + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ *
    + *
  • OpenHAB Event Bus → Velux bridge + *

    + * Sending commands and value updates by method {@link #handleCommand}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelSceneSilentmode extends VeluxChannelHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelSceneSilentmode.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelSceneSilentmode() { + throw new AssertionError(); + } + + /** + * Communication method to update the real world according to the passed channel value (or command). + * + * @param channelUID The item passed as type {@link ChannelUID} for which to following command is addressed to. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param command The command passed as type {@link Command} for the mentioned item. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newValue ... + */ + static @Nullable Command handleCommand(ChannelUID channelUID, String channelId, Command command, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleCommand({},{},{},{}) called.", channelUID, channelId, command, thisBridgeHandler); + Command newValue = null; + do { // just for common exit + assert thisBridgeHandler.bridgeParameters.scenes + .getChannel().existingScenes != null : "VeluxBridgeHandler.existingScenes not initialized."; + if (!ThingProperty.exists(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_SCENE_NAME)) { + LOGGER.trace("handleCommand(): aborting processing as scene name is not set."); + break; + } + String sceneName = (String) ThingProperty.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_SCENE_NAME); + if (!thisBridgeHandler.bridgeParameters.scenes.getChannel().existingScenes + .isRegistered(new SceneName(sceneName))) { + LOGGER.info("handleCommand({},{}): cannot modify unknown scene: {}.", channelUID.getAsString(), command, + sceneName); + break; + } + boolean silentMode = command.equals(OnOffType.ON); + LOGGER.debug("handleCommand(): setting silent mode to {}.", silentMode); + + VeluxScene thisScene = thisBridgeHandler.bridgeParameters.scenes.getChannel().existingScenes + .get(new SceneName(sceneName)); + LOGGER.trace("handleCommand(): working on scene {}.", thisScene); + int sceneNumber = thisScene.getBridgeSceneIndex().toInt(); + new VeluxBridgeSetSceneVelocity().setSilentMode(thisBridgeHandler.thisBridge, sceneNumber, silentMode); + LOGGER.trace("handleCommand(): change of velocity done."); + } while (false); // common exit + return newValue; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelVShutterPosition.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelVShutterPosition.java new file mode 100644 index 0000000000000..f763a5b7f58f8 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelVShutterPosition.java @@ -0,0 +1,166 @@ +/** + * Copyright (c) 2010-2019 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.velux.handler; + +import static org.openhab.binding.velux.VeluxBindingConstants.CHANNEL_VSHUTTER_POSITION; + +import java.math.BigDecimal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.library.types.UpDownType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.VeluxBindingProperties; +import org.openhab.binding.velux.bridge.VeluxBridgeRunScene; +import org.openhab.binding.velux.internal.VeluxItemType; +import org.openhab.binding.velux.internal.VeluxRSBindingConfig; +import org.openhab.binding.velux.things.VeluxScene; +import org.openhab.binding.velux.things.VeluxScene.SceneName; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel position of the Thing vshutter : + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ *
    + *
  • OpenHAB Event Bus → Velux bridge + *

    + * Sending commands and value updates by method {@link #handleCommand}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelVShutterPosition extends VeluxChannelHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelVShutterPosition.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelVShutterPosition() { + throw new AssertionError(); + } + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler); + assert (channelId == CHANNEL_VSHUTTER_POSITION); + State newState = null; + do { // just for common exit + if (!ThingProperty.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_VSHUTTER_CURRENTLEVEL)) { + LOGGER.trace("handleRefresh(): aborting processing as current scene level is not set."); + break; + } + // Don't know why OH2 returns BigDecimal. + BigDecimal rollershutterLevelBC = (BigDecimal) ThingProperty.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_VSHUTTER_CURRENTLEVEL); + int rollershutterLevel = rollershutterLevelBC.intValue(); + LOGGER.trace("handleRefresh(): current level is {}.", rollershutterLevel); + newState = new PercentType(rollershutterLevel); + } while (false); // common exit + LOGGER.trace("handleRefresh() returns {}.", newState); + return newState; + } + + /** + * Communication method to update the real world according to the passed channel value (or command). + * + * @param channelUID The item passed as type {@link ChannelUID} for which to following command is addressed to. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param command The command passed as type {@link Command} for the mentioned item. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newValue ... + */ + static @Nullable Command handleCommand(ChannelUID channelUID, String channelId, Command command, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleCommand({},{},{},{}) called.", channelUID, channelId, command, thisBridgeHandler); + Command newValue = null; + // ThingProperty sceneLevels + if (!ThingProperty.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_VSHUTTER_SCENELEVELS)) { + LOGGER.trace("handleCommand(): aborting processing as scene levels are not set."); + return newValue; + } + String sceneLevels = (String) ThingProperty.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_VSHUTTER_SCENELEVELS); + // ThingProperty currentLevel + if (!ThingProperty.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_VSHUTTER_CURRENTLEVEL)) { + LOGGER.trace("handleCommand(): aborting processing as current scene level is not set."); + return newValue; + } + // Don't know why OH2 returns BigDecimal. + BigDecimal rollershutterLevelBC = (BigDecimal) ThingProperty.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_VSHUTTER_CURRENTLEVEL); + int currentLevel = rollershutterLevelBC.intValue(); + LOGGER.trace("handleCommand(): current level is {}.", currentLevel); + + VeluxRSBindingConfig thisRSBindingConfig = new VeluxRSBindingConfig(VeluxItemType.VSHUTTER_POSITION, + sceneLevels, currentLevel); + + if ((UpDownType) command == UpDownType.UP) { + currentLevel = thisRSBindingConfig.getNextDescendingLevel(); + } else if ((UpDownType) command == UpDownType.DOWN) { + currentLevel = thisRSBindingConfig.getNextAscendingLevel(); + } else { + LOGGER.info("handleCommand({},{}): ignoring command.", channelUID.getAsString(), command); + return newValue; + } + LOGGER.trace("handleCommand(): next level is {}.", currentLevel); + String sceneName = thisRSBindingConfig.getSceneName(); + LOGGER.trace("handleCommand(): scene name is {}.", sceneName); + VeluxScene thisScene2 = thisBridgeHandler.bridgeParameters.scenes.getChannel().existingScenes + .get(new SceneName(sceneName)); + if (thisScene2 == VeluxScene.UNKNOWN) { + LOGGER.warn( + "handleCommand(): aborting command as scene with name {} is not registered; please check your KLF scene definitions.", + sceneName); + return newValue; + } + newValue = new PercentType(currentLevel); + LOGGER.trace("handleCommand(): executing scene {} with index {}.", thisScene2, + thisScene2.getBridgeSceneIndex().toInt()); + new VeluxBridgeRunScene().execute(thisBridgeHandler.thisBridge, thisScene2.getBridgeSceneIndex().toInt()); + + LOGGER.trace("handleCommand(): updating level to {}.", currentLevel); + ThingProperty.setValue(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_VSHUTTER_CURRENTLEVEL, + thisRSBindingConfig.getLevel()); + LOGGER.trace("handleCommand() returns {}.", newValue); + return newValue; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ExtendedBaseBridgeHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ExtendedBaseBridgeHandler.java new file mode 100644 index 0000000000000..4a4ff031ccb11 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ExtendedBaseBridgeHandler.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2010-2019 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.velux.handler; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; + +/** + * The {@link ExtendedBaseBridgeHandler} extended the {@link BaseBridgeHandler} interface and adds publicly + * visible convenience methods for property handling. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +abstract class ExtendedBaseBridgeHandler extends BaseBridgeHandler { + + /* + * ************************ + * ***** Constructors ***** + */ + + /** + * @see BaseBridgeHandler + * @param bridge which will be created. + */ + ExtendedBaseBridgeHandler(Bridge bridge) { + super(bridge); + } + + /** + * Returns a copy of the properties map, that can be modified. The method {@link #updateProperties} must be called + * to persist the properties. + * + * @return copy of the thing properties (not null) + */ + @Override + public Map editProperties() { + return super.editProperties(); + } + + /** + * Informs the framework, that the given properties map of the thing was updated. This method performs a check, if + * the properties were updated. If the properties did not change, the framework is not informed about changes. + * + * @param properties properties map, that was updated and should be persisted + */ + @Override + public void updateProperties(Map properties) { + super.updateProperties(properties); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ExtendedBaseThingHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ExtendedBaseThingHandler.java new file mode 100644 index 0000000000000..8d79f70c06f07 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ExtendedBaseThingHandler.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2010-2019 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.velux.handler; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; + +/** + * The {@link ExtendedBaseThingHandler} extended the {@link BaseThingHandler} interface and adds publicly + * visible convenience methods for property handling. + *

+ * It is recommended to extend this abstract base class. + *

+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +abstract class ExtendedBaseThingHandler extends BaseThingHandler { + + /* + * ************************ + * ***** Constructors ***** + */ + + /** + * @see BaseThingHandler + * @param thing which will be created. + */ + ExtendedBaseThingHandler(Thing thing) { + super(thing); + } + + /** + * Returns a copy of the properties map, that can be modified. The method {@link #updateProperties} must be called + * to persist the properties. + * + * @return copy of the thing properties (not null) + */ + @Override + public Map editProperties() { + return super.editProperties(); + } + + /** + * Informs the framework, that the given properties map of the thing was updated. This method performs a check, if + * the properties were updated. If the properties did not change, the framework is not informed about changes. + * + * @param properties properties map, that was updated and should be persisted + */ + @Override + public void updateProperties(Map properties) { + super.updateProperties(properties); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/StateUtils.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/StateUtils.java new file mode 100644 index 0000000000000..fa7192d158cfe --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/StateUtils.java @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2010-2019 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.velux.handler; + +import java.math.BigDecimal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; + +/** + * This class support handling of openHAB type {@link State}. Therefore, it provides the methods: + *

    + *
  • {@link #createState} for creating an openHAB {@link State}.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +final class StateUtils { + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private StateUtils() { + throw new AssertionError(); + } + + /** + * Creates an openHAB {@link State} in accordance to the class of the given {@code propertyValue}. Currently + * {@link PercentType}, {@link DecimalType}, and {@link Boolean} are handled explicitly. All other + * {@code dataTypes} are mapped to {@link StringType}. + *

+ * If {@code propertyValue} is {@code null}, {@link UnDefType#NULL} will be returned. + *

+ * Copied/adapted from the org.openhab.binding.koubachi binding. + *

+ * + * @param propertyValue which should be converted, + * @return state of type {@link State} in accordance with {@code dataType}. Will never be {@code null}. + */ + protected static State createState(@Nullable Object propertyValue) { + if (propertyValue == null) { + return UnDefType.NULL; + } + + Class dataType = propertyValue.getClass(); + + if (PercentType.class.isAssignableFrom(dataType)) { + return new PercentType((Integer) propertyValue); + } else if (Integer.class.isAssignableFrom(dataType)) { + return new DecimalType((Integer) propertyValue); + } else if (BigDecimal.class.isAssignableFrom(dataType)) { + return new DecimalType((BigDecimal) propertyValue); + } else if (Boolean.class.isAssignableFrom(dataType)) { + if ((Boolean) propertyValue) { + return OnOffType.ON; + } else { + return OnOffType.OFF; + } + } else if (State.class.isAssignableFrom(dataType)) { + return (State) propertyValue; + } else { + return new StringType(propertyValue.toString()); + } + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ThingProperty.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ThingProperty.java new file mode 100644 index 0000000000000..e6bcaf450cda6 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ThingProperty.java @@ -0,0 +1,155 @@ +/** + * Copyright (c) 2010-2019 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.velux.handler; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/*** + * The class {@link ThingProperty} provides methods for dealing with + * properties. + *
    + *
  • {@link #exists} Check existence of a property,
  • + *
  • {@link #getValue} Returns a property value,
  • + *
  • {@link #setValue} Modifies a property value.
  • + *
+ *

+ * Noninstantiable utility class + *

+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +final class ThingProperty { + private static final Logger LOGGER = LoggerFactory.getLogger(ThingProperty.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-Instantiability + + private ThingProperty() { + throw new AssertionError(); + } + + /* + * ************************** + * ***** Public Methods ***** + */ + + /** + * Check existence of the property value for the given channel and + * desired propertyName which are defined within VeluxBindingProperties. + *

+ * + * @param bridge which handles the mentioned Things, + * @param channelUID describes the channel to by scrutinized, + * @param propertyName defines the property which is to be evaluated. + * @return exists of type boolean. + */ + static boolean exists(BaseBridgeHandler bridge, ChannelUID channelUID, String propertyName) { + LOGGER.trace("exists({},{}) called.", channelUID, propertyName); + + ThingUID channelTUID = channelUID.getThingUID(); + Thing thingOfChannel = bridge.getThingByUID(channelTUID); + boolean exists = false; + if (thingOfChannel == null) { + LOGGER.trace("exists(): Channel {} does not belong to a thing.", channelUID); + } else { + if (thingOfChannel.getConfiguration().get(propertyName) != null) { + exists = true; + } + } + LOGGER.trace("exists() returns {}.", exists); + return exists; + } + + /** + * Return the property value of type Object for the given channel and + * desired propertyName which are defined within VeluxBindingProperties. + *

+ * + * @param bridge which handles the mentioned Things, + * @param channelUID describes the channel to by scrutinized, + * @param propertyName defines the property which is to be evaluated. + * @return propertyValue of type {@link Object}. Will return {@code null}, if not found, or if value itself + * is {@code null}. + */ + static Object getValue(BaseBridgeHandler bridge, ChannelUID channelUID, String propertyName) { + LOGGER.trace("getValue({},{}) called.", channelUID, propertyName); + + ThingUID channelTUID = channelUID.getThingUID(); + Thing thingOfChannel = bridge.getThingByUID(channelTUID); + if (thingOfChannel == null) { + LOGGER.warn("getValue(): Channel {} does not belong to a thing.", channelUID); + return true; + } + Object propertyValue = thingOfChannel.getConfiguration().get(propertyName); + LOGGER.trace("getValue() returns {}.", propertyValue); + return propertyValue; + } + + /** + * Modifies the property value for the given bridge and desired propertyName which are defined within + * VeluxBindingProperties. + *

+ * + * @param bridgeHandler which contains the properties, + * @param channelUID describes the channel to by scrutinized, + * @param propertyName defines the property which is to be modified. + * @param propertyValue defines the new property value. + */ + static void setValue(ExtendedBaseBridgeHandler bridgeHandler, ChannelUID channelUID, String propertyName, + Object propertyValue) { + LOGGER.trace("setValue({},{}) called.", propertyName, propertyValue); + + ThingUID channelTUID = channelUID.getThingUID(); + Thing thingOfChannel = bridgeHandler.getThingByUID(channelTUID); + if (thingOfChannel == null) { + LOGGER.warn("getValue(): Channel {} does not belong to a thing.", channelUID); + return; + } + thingOfChannel.setProperty(propertyName, propertyName); + LOGGER.trace("setValue() done."); + return; + } + + /** + * Modifies the property value for the given bridge and desired propertyName which are defined within + * VeluxBindingProperties. + *

+ * + * @param bridgeHandler which contains the properties, + * @param propertyName defines the property which is to be modified. + * @param propertyValue defines the new property value. + */ + static void setValue(ExtendedBaseBridgeHandler bridgeHandler, String propertyName, Object propertyValue) { + LOGGER.trace("setValue({},{}) called.", propertyName, propertyValue); + + Map properties = bridgeHandler.editProperties(); + properties.put(propertyName, propertyValue.toString()); + bridgeHandler.updateProperties(properties); + LOGGER.trace("setValue() done."); + return; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxBindingHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxBindingHandler.java new file mode 100644 index 0000000000000..1cf51b38b9d84 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxBindingHandler.java @@ -0,0 +1,201 @@ +/** + * Copyright (c) 2010-2019 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.velux.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +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.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.RefreshType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.internal.VeluxItemType; +import org.openhab.binding.velux.internal.utils.Localization; +import org.openhab.binding.velux.internal.utils.LoggerFulltrace; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/*** + * The class is responsible for representing the overall status of the Velux binding. + *

+ * Beside the normal thing handling introduced by {@link BaseThingHandler}, it provides a method: + *

    + *
  • {@link #updateNoOfBridges} enable other classes to modify the number of activated Velux bridges.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBindingHandler extends BaseThingHandler { + private final Logger logger = LoggerFactory.getLogger(VeluxBindingHandler.class); + private final LoggerFulltrace log = new LoggerFulltrace(logger, false); + + /* + * *************************** + * ***** Private Objects ***** + */ + private Integer currentNumberOfBridges = 0; + + /* + * ************************ + * ***** Constructors ***** + */ + + public VeluxBindingHandler(Thing thing) { + super(thing); + logger.trace("VeluxHandler(constructor) called."); + } + + /* + * *************************** + * ***** Private Methods ***** + */ + + /** + * Provide the ThingType for a given Channel. + *

+ * Separated into this private method to deal with the deprecated method. + *

+ * + * @param channelUID for type {@link ChannelUID}. + * @return thingTypeUID of type {@link ThingTypeUID}. + */ + @SuppressWarnings("deprecation") + private ThingTypeUID thingTypeUIDOf(ChannelUID channelUID) { + log.fulltrace("thingTypeUIDOf({}) called.", channelUID); + return channelUID.getThingUID().getThingTypeUID(); + } + + /** + * Returns a human-readable representation of the binding state. This should help especially unexperienced user to + * blossom up the introduction of the Velux binding. + * + * @return bindingInformation of type {@link String}. + */ + private String bridgeCountToString() { + String information = Localization.getText("@text/runtime.multiple-bridges"); + switch (currentNumberOfBridges) { + case 0: + information = Localization.getText("@text/runtime.no-bridge"); + break; + case 1: + information = Localization.getText("@text/runtime.one-bridge"); + break; + } + return information; + } + + /* + * ******************************************************************* + * ***** Objects and Methods for abstract class BaseThingHandler ***** + */ + + @Override + public void initialize() { + logger.trace("initialize() called."); + updateNoOfBridges(0); + logger.trace("initialize() done."); + } + + @Override + public void dispose() { + logger.trace("dispose() called."); + super.dispose(); + } + + /** + * NOTE: It takes care by calling {@link #handleCommand} with the REFRESH command, that every used channel is + * initialized. + */ + @Override + public void channelLinked(ChannelUID channelUID) { + logger.trace("channelLinked({}) called.", channelUID.getAsString()); + handleCommand(channelUID, RefreshType.REFRESH); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + logger.trace("handleCommand({},{}) called.", channelUID.getAsString(), command); + /* + * =========================================================== + * Common part + */ + String channelId = channelUID.getId(); + State newState = null; + String itemName = channelUID.getAsString(); + VeluxItemType itemType = VeluxItemType.getByThingAndChannel(thingTypeUIDOf(channelUID), channelUID.getId()); + + if (command instanceof RefreshType) { + /* + * =========================================================== + * Refresh part + */ + logger.trace("handleCommand(): refreshing item {}.", itemName); + switch (itemType) { + case BINDING_INFORMATION: + newState = StateUtils.createState(bridgeCountToString()); + break; + default: + logger.trace("handleCommand(): cannot handle REFRESH on channel {} as it is of type {}.", itemName, + channelId); + } + if (newState != null) { + logger.debug("handleCommand(): updating {} ({}) to {}.", itemName, channelUID, newState); + updateState(channelUID, newState); + } else { + logger.info("handleCommand({},{}): updating of item {} failed.", channelUID.getAsString(), command, + itemName); + } + } else { + /* + * =========================================================== + * Modification part + */ + logger.trace("handleCommand(): found COMMAND {}.", command); + + switch (channelId) { + default: + logger.warn("handleCommand() cannot handle command {} on channel {} (type {}).", command, itemName, + itemType); + } + } + logger.trace("handleCommand() done."); + } + + /* + * ********************************** + * ***** (Other) Public Methods ***** + */ + + /** + * Modifies the number of activated Velux bridges, which is reflected in the Thing representing the overall status + * of this binding. + * + * @param bridgeCount as Integer. + */ + + public void updateNoOfBridges(Integer bridgeCount) { + logger.trace("updateNoOfBridges({}) called.", bridgeCount); + this.currentNumberOfBridges = bridgeCount; + if (bridgeCount < 1) { + updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING, bridgeCountToString()); + } else { + updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, bridgeCountToString()); + } + logger.trace("updateNoOfBridges() done."); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxBridgeHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxBridgeHandler.java new file mode 100644 index 0000000000000..48e65555c7afc --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxBridgeHandler.java @@ -0,0 +1,682 @@ +/** + * Copyright (c) 2010-2019 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.velux.handler; + +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.lang.exception.ExceptionUtils; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.thing.Bridge; +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.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.RefreshType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.VeluxBindingProperties; +import org.openhab.binding.velux.bridge.VeluxBridge; +import org.openhab.binding.velux.bridge.VeluxBridgeActuators; +import org.openhab.binding.velux.bridge.VeluxBridgeDeviceStatus; +import org.openhab.binding.velux.bridge.VeluxBridgeGetFirmware; +import org.openhab.binding.velux.bridge.VeluxBridgeGetHouseStatus; +import org.openhab.binding.velux.bridge.VeluxBridgeInstance; +import org.openhab.binding.velux.bridge.VeluxBridgeLANConfig; +import org.openhab.binding.velux.bridge.VeluxBridgeProvider; +import org.openhab.binding.velux.bridge.VeluxBridgeScenes; +import org.openhab.binding.velux.bridge.VeluxBridgeSetHouseStatusMonitor; +import org.openhab.binding.velux.bridge.VeluxBridgeWLANConfig; +import org.openhab.binding.velux.bridge.common.BridgeAPI; +import org.openhab.binding.velux.bridge.common.BridgeCommunicationProtocol; +import org.openhab.binding.velux.bridge.json.JsonVeluxBridge; +import org.openhab.binding.velux.bridge.slip.SlipVeluxBridge; +import org.openhab.binding.velux.internal.VeluxBinding; +import org.openhab.binding.velux.internal.VeluxItemType; +import org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration; +import org.openhab.binding.velux.internal.utils.LoggerFulltrace; +import org.openhab.binding.velux.things.VeluxExistingProducts; +import org.openhab.binding.velux.things.VeluxExistingScenes; +import org.openhab.binding.velux.things.VeluxProduct; +import org.openhab.binding.velux.things.VeluxProductPosition; +import org.openhab.binding.velux.things.VeluxProductSerialNo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Common interaction with the Velux bridge. + *

+ * It implements the communication between OpenHAB and the Velux Bridge: + *

    + *
  • OpenHAB Event Bus → Velux bridge + *

    + * Sending commands and value updates.

  • + *
+ *
    + *
  • Velux bridgeOpenHAB: + *

    + * Retrieving information by sending a Refresh command.

  • + *
+ *

+ * Entry point for this class is the method + * {@link VeluxBridgeHandler#handleCommand handleCommand}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public class VeluxBridgeHandler extends ExtendedBaseBridgeHandler implements VeluxBridgeInstance, VeluxBridgeProvider { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeHandler.class); + private final LoggerFulltrace log = new LoggerFulltrace(logger, false); + + /* + * *************************** + * ***** Private Objects ***** + */ + + private @Nullable ScheduledFuture refreshJob = null; + private int refreshCounter = 0; + + private VeluxBridge myJsonBridge; + private VeluxBridge mySlipBridge; + + /* + * ************************************** + * ***** Default visibility Objects ***** + */ + + BridgeParameters bridgeParameters; + VeluxBridge thisBridge; + + /** + * Information retrieved by {@link VeluxBinding#VeluxBinding}. + */ + private VeluxBridgeConfiguration veluxBridgeConfiguration = new VeluxBridgeConfiguration(); + + /* + * ************************ + * ***** Constructors ***** + */ + + public VeluxBridgeHandler(final Bridge bridge) { + super(bridge); + logger.trace("VeluxBridgeHandler(constructor with bridge={}) called.", bridge); + logger.debug("Creating a VeluxBridgeHandler for thing '{}'.", getThing().getUID()); + bridgeParameters = new BridgeParameters(); + logger.trace("VeluxBridgeHandler(): Initializing empty storage for existing products."); + bridgeParameters.actuators = new VeluxBridgeActuators(); + logger.trace("VeluxBridgeHandler(): Initializing empty storage for existing scenes."); + bridgeParameters.scenes = new VeluxBridgeScenes(); + bridgeParameters.gateway = new VeluxBridgeDeviceStatus().getChannel(); + bridgeParameters.firmware = new VeluxBridgeGetFirmware().getChannel(); + bridgeParameters.lanConfig = new VeluxBridgeLANConfig().getChannel(); + bridgeParameters.wlanConfig = new VeluxBridgeWLANConfig().getChannel(); + // + logger.trace("VeluxBridgeHandler(): Initializing the different bridge protocols."); + myJsonBridge = new JsonVeluxBridge(this); + mySlipBridge = new SlipVeluxBridge(this); + thisBridge = myJsonBridge; + logger.trace("VeluxBridgeHandler(constructor) done."); + } + + /* + * *************************** + * ***** Private Classes ***** + */ + + /** + *

+ * Set of information retrieved from the bridge/gateway: + *

+ *
    + *
  • {@link #actuators} - Already known actuators,
  • + *
  • {@link #scenes} - Already on the gateway defined scenes,
  • + *
  • {@link #gateway} - Current status of the gateway status,
  • + *
  • {@link #firmware} - Information about the gateway firmware revision,
  • + *
  • {@link #lanConfig} - Information about the gateway configuration,
  • + *
  • {@link #wlanConfig} - Information about the gateway configuration.
  • + *
+ */ + public class BridgeParameters { + /** Information retrieved by {@link VeluxBridgeActuators#getProducts} */ + VeluxBridgeActuators actuators = new VeluxBridgeActuators(); + + /** Information retrieved by {@link org.openhab.binding.velux.bridge.VeluxBridgeScenes#getScenes} */ + VeluxBridgeScenes scenes = new VeluxBridgeScenes(); + + /** Information retrieved by {@link VeluxBridgeDeviceStatus#retrieve} */ + VeluxBridgeDeviceStatus.Channel gateway = new VeluxBridgeDeviceStatus().getChannel(); + + /** Information retrieved by {@link VeluxBridgeGetFirmware#retrieve} */ + VeluxBridgeGetFirmware.Channel firmware = new VeluxBridgeGetFirmware().getChannel(); + + /** Information retrieved by {@link VeluxBridgeLANConfig#retrieve} */ + VeluxBridgeLANConfig.Channel lanConfig = new VeluxBridgeLANConfig().getChannel(); + + /** Information retrieved by {@link VeluxBridgeWLANConfig#retrieve} */ + VeluxBridgeWLANConfig.Channel wlanConfig = new VeluxBridgeWLANConfig().getChannel(); + } + + /* + * *************************** + * ***** Private Methods ***** + */ + + /** + * Provide the ThingType for a given Channel. + *

+ * Separated into this private method to deal with the deprecated method. + *

+ * + * @param channelUID for type {@link ChannelUID}. + * @return thingTypeUID of type {@link ThingTypeUID}. + */ + @SuppressWarnings("deprecation") + ThingTypeUID thingTypeUIDOf(ChannelUID channelUID) { + log.fulltrace("thingTypeUIDOf({}) called.", channelUID); + return channelUID.getThingUID().getThingTypeUID(); + } + + /* + * ***************************************************************** + * ***** Objects and Methods for interface VeluxBridgeInstance ***** + */ + + /** + * Information retrieved by ... + */ + @Override + public VeluxBridgeConfiguration veluxBridgeConfiguration() { + return veluxBridgeConfiguration; + }; + + /** + * Information retrieved by {@link VeluxBridgeActuators#getProducts} + */ + @Override + public VeluxExistingProducts existingProducts() { + return bridgeParameters.actuators.getChannel().existingProducts; + }; + + /** + * Information retrieved by {@link VeluxBridgeScenes#getScenes} + */ + @Override + public VeluxExistingScenes existingScenes() { + return bridgeParameters.scenes.getChannel().existingScenes; + } + + /* + * ***************************************************************** + * ***** Objects and Methods for interface VeluxBridgeProvider ***** + */ + + @Override + public boolean bridgeCommunicate(BridgeCommunicationProtocol communication) { + logger.warn("bridgeCommunicate() called. Auto-generated method stub. Should never be called."); + return false; + } + + @Override + public @Nullable BridgeAPI bridgeAPI() { + logger.warn("bridgeAPI() called. . Auto-generated method stub. Should never be called."); + return null; + } + + /*** + *** Continuous methods + ***/ + + private synchronized void execute() { + logger.debug("execute() called."); + logger.trace("execute(): processing of possible HSM messages."); + if (new VeluxBridgeGetHouseStatus().evaluateState(thisBridge)) { + logger.trace("execute(): successfully processed of GetHouseStatus()"); + } + logger.trace("execute(): looping through all (both child things and bridge) channels for a need of refresh."); + for (ChannelUID channelUID : BridgeChannels.getAllChannelUIDs(this)) { + log.fulltrace("execute(): evaluating ChannelUID {}.", channelUID); + if (VeluxItemType.isToBeRefreshedNow(this.refreshCounter, thingTypeUIDOf(channelUID), channelUID.getId())) { + logger.trace("execute(): refreshing item {}.", channelUID); + handleCommand(channelUID, RefreshType.REFRESH); + } + } + this.refreshCounter++; + logger.debug("execute() done."); + } + + @Override + public void initialize() { + logger.debug("initialize() called."); + logger.info("Initializing Velux veluxBridge handler for '{}'.", getThing().getUID()); + // The framework requires you to return from this method quickly. + // Setting the thing status to UNKNOWN temporarily and let the background task decide for the real status. + updateStatus(ThingStatus.UNKNOWN); + + logger.trace("initialize(): preparing background initialization task."); + + // Background initialization: + scheduler.execute(() -> { + logger.trace("initialize.scheduled(): Further work within scheduler.execute()."); + logger.trace("initialize.scheduled(): Initializing bridge configuration parameters."); + this.veluxBridgeConfiguration = new VeluxBinding(getConfigAs(VeluxBridgeConfiguration.class)).checked(); + logger.trace("initialize.scheduled(): work on updated bridge configuration parameters."); + bridgeParamsUpdated(); + + logger.debug("initialize.scheduled(): activated scheduler with {} milliseconds.", + this.veluxBridgeConfiguration.refreshMSecs); + refreshJob = scheduler.scheduleWithFixedDelay(() -> { + try { + execute(); + } catch (RuntimeException e) { + logger.warn("Exception occurred during activated refresh scheduler: {}, {}.", e.getMessage(), + ExceptionUtils.getStackTrace(e)); + } + }, this.veluxBridgeConfiguration.refreshMSecs, this.veluxBridgeConfiguration.refreshMSecs, + TimeUnit.MILLISECONDS); + logger.trace("initialize.scheduled(): done."); + }); + logger.trace("initialize() done."); + } + + /** + * NOTE: It takes care about shutting down the connections before removal of this binding. + */ + @Override + public synchronized void dispose() { + logger.trace("dispose({}) called.", this); + logger.debug("Shutting down Velux veluxBridge '{}'.", getThing().getUID()); + // Just for avoidance of Potential null pointer access + ScheduledFuture refreshJobX = refreshJob; + if (refreshJobX != null) { + logger.trace("dispose(): stopping the refresh."); + refreshJobX.cancel(true); + } + logger.trace("dispose(): initiating logout."); + thisBridge.bridgeLogout(); + logger.trace("dispose(): shutting down JSON bridge."); + myJsonBridge.shutdown(); + logger.trace("dispose(): shutting down SLIP bridge."); + mySlipBridge.shutdown(); + logger.trace("dispose(): calling super class."); + super.dispose(); + logger.trace("dispose() done."); + } + + /** + * NOTE: It takes care by calling {@link #handleCommand} with the REFRESH command, that every used channel is + * initialized. + */ + @Override + public void channelLinked(ChannelUID channelUID) { + logger.trace("channelLinked({}) called.", channelUID.getAsString()); + + if (thing.getStatus() == ThingStatus.ONLINE) { + logger.trace("channelLinked() refreshing channel value with help of handleCommand as Thing is online."); + handleCommand(channelUID, RefreshType.REFRESH); + } else { + logger.trace("channelLinked() doing nothing as Thing is not online."); + } + } + + @Override + public void channelUnlinked(ChannelUID channelUID) { + logger.trace("channelUnlinked({}) called.", channelUID.getAsString()); + } + + @Override + public void childHandlerInitialized(ThingHandler childHandler, Thing childThing) { + logger.trace("childHandlerInitialized({},{}) called.", childHandler, childThing); + super.childHandlerInitialized(childHandler, childThing); + } + + @Override + public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) { + logger.trace("childHandlerDisposed({},{}) called.", childHandler, childThing); + super.childHandlerDisposed(childHandler, childThing); + } + + /*** + *** Reconfiguration methods + ***/ + private synchronized void bridgeParamsUpdated() { + logger.debug("bridgeParamsUpdated() called."); + + // Determine the appropriate bridge communication channel + boolean validBridgeFound = false; + if (myJsonBridge.supportedProtocols.contains(veluxBridgeConfiguration.protocol)) { + logger.debug("bridgeParamsUpdated(): choosing JSON as communication method."); + thisBridge = myJsonBridge; + validBridgeFound = true; + } + if (mySlipBridge.supportedProtocols.contains(veluxBridgeConfiguration.protocol)) { + logger.debug("bridgeParamsUpdated(): choosing SLIP as communication method."); + thisBridge = mySlipBridge; + validBridgeFound = true; + } + if (!validBridgeFound) { + logger.debug("No valid protocol selected, aborting this {} binding.", VeluxBindingConstants.BINDING_ID); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/runtime.bridge-offline-no-valid-bridgeProtocol-selected"); + logger.trace("bridgeParamsUpdated() done."); + return; + } + + logger.trace("bridgeParamsUpdated(): Trying to authenticate towards bridge."); + + if (!thisBridge.bridgeLogin()) { + logger.warn("{} bridge login sequence failed; expecting bridge is OFFLINE.", + VeluxBindingConstants.BINDING_ID); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/runtime.bridge-offline-login-sequence-failed"); + logger.trace("bridgeParamsUpdated() done."); + return; + } + + logger.trace("bridgeParamsUpdated(): Querying bridge state."); + bridgeParameters.gateway = new VeluxBridgeDeviceStatus().retrieve(thisBridge); + + logger.trace("bridgeParamsUpdated(): Fetching existing scenes."); + bridgeParameters.scenes.getScenes(thisBridge); + String scenes = bridgeParameters.scenes.getChannel().existingScenes.toString(false, "\n\t"); + logger.info("Found {} scenes:\n\t{}", VeluxBindingConstants.BINDING_ID, scenes); + + logger.trace("bridgeParamsUpdated(): Fetching existing actuators/products."); + bridgeParameters.actuators.getProducts(thisBridge); + String products = bridgeParameters.actuators.getChannel().existingProducts.toString(false, "\n\t"); + logger.info("Found {} actuators:\n\t{}", VeluxBindingConstants.BINDING_ID, products); + + if (thisBridge.bridgeAPI().setHouseStatusMonitor() != null) { + logger.trace("bridgeParamsUpdated(): Activating HouseStatusMonitor."); + if (new VeluxBridgeSetHouseStatusMonitor().modifyHSM(thisBridge, true)) { + logger.trace("bridgeParamsUpdated(): HSM activated."); + } else { + logger.warn("Activation of House-Status-Monitoring failed (might lead to a lack of status updates)."); + } + } + + veluxBridgeConfiguration.hasChanged = false; + logger.info("{} Bridge is online with {} scenes and {} actuators, now.", VeluxBindingConstants.BINDING_ID, + bridgeParameters.scenes.getChannel().existingScenes.getNoMembers(), + bridgeParameters.actuators.getChannel().existingProducts.getNoMembers()); + logger.debug("Velux veluxBridge is online, now."); + updateStatus(ThingStatus.ONLINE); + logger.trace("bridgeParamsUpdated() successfully finished."); + } + + /** + * In case of recognized changes in the real world, the method will + * update the corresponding states via openHAB event bus. + */ + private void syncChannelsWithProducts() { + logger.trace("syncChannelsWithProducts() called."); + if (!bridgeParameters.actuators.getChannel().existingProducts.isDirty()) { + logger.trace("syncChannelsWithProducts() done."); + return; + } + logger.trace("syncChannelsWithProducts(): existingProducts have changed."); + outer: for (VeluxProduct product : bridgeParameters.actuators.getChannel().existingProducts + .valuesOfModified()) { + logger.trace("syncChannelsWithProducts(): actuator {} has changed values.", + product.getProductName().toString()); + for (ChannelUID channelUID : BridgeChannels.getAllChannelUIDs(this)) { + log.fulltrace("syncChannelsWithProducts(): evaluating ChannelUID {}.", channelUID); + String itemName = channelUID.getAsString(); + VeluxItemType itemType = VeluxItemType.getByThingAndChannel(thingTypeUIDOf(channelUID), + channelUID.getId()); + log.fulltrace("syncChannelsWithProducts(): evaluating VeluxItemType {}.", itemType); + if (itemType == VeluxItemType.UNKNOWN) { + continue; + } + switch (itemType) { + case ACTUATOR_POSITION: + case ACTUATOR_STATE: + case ROLLERSHUTTER_POSITION: + case WINDOW_POSITION: + log.fulltrace("syncChannelsWithProducts(): found on suitable entry of VeluxItemType."); + break; + default: + continue; + } + if (!ThingProperty.exists(this, channelUID, VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER)) { + logger.trace("syncChannelsWithProducts(): aborting processing as actuatorSerial is not set."); + break; + } + String actuatorSerial = (String) ThingProperty.getValue(this, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER); + + // Handle value inversion + boolean propertyInverted = false; + if (ThingProperty.exists(this, channelUID, VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) { + propertyInverted = (boolean) ThingProperty.getValue(this, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED); + } + boolean isInverted = propertyInverted || VeluxProductSerialNo.indicatesRevertedValues(actuatorSerial); + logger.trace("syncChannelsWithProducts(): isInverted={}.", isInverted); + actuatorSerial = VeluxProductSerialNo.cleaned(actuatorSerial); + + logger.trace("syncChannelsWithProducts(): working on actuatorSerial {}.", actuatorSerial); + if (product.getSerialNumber().equals(actuatorSerial)) { + logger.trace("syncChannelsWithProducts(): product {}/{} used within item {}.", + product.getProductName(), product.getSerialNumber(), itemName); + try { + VeluxProductPosition position = new VeluxProductPosition(product.getCurrentPosition()); + if (position.isValid()) { + PercentType positionAsPercent = position.getPositionAsPercentType(isInverted); + logger.debug("syncChannelsWithProducts(): updating item {} to position {}%.", itemName, + positionAsPercent); + updateState(channelUID, positionAsPercent); + } else { + logger.trace("syncChannelsWithProducts(): update of item {} to position {} skipped.", + itemName, position.toString()); + } + } catch (RuntimeException e) { + logger.warn("syncChannelsWithProducts(): getProducts() exception: {}.", e.getMessage()); + } + break outer; + } + } + } + logger.trace("syncChannelsWithProducts(): resetting dirty flag."); + bridgeParameters.actuators.getChannel().existingProducts.resetDirtyFlag(); + logger.trace("syncChannelsWithProducts() done."); + } + + /** + * General two-way communication method. + * + * It provides either information retrieval or information update according to the passed command. + * + * @param channelUID The item passed as type {@link ChannelUID} for which to following command is addressed to. + * @param command The command passed as type {@link Command} for the mentioned item. If + * {@code command} is {@code null}, an information retrieval via a Refresh command + * is initiated for this item. + */ + @Override + public synchronized void handleCommand(ChannelUID channelUID, Command command) { + logger.debug("handleCommand({},{}) called.", channelUID.getAsString(), command); + + /* + * =========================================================== + * Common part + */ + + String channelId = channelUID.getId(); + State newState = null; + String itemName = channelUID.getAsString(); + VeluxItemType itemType = VeluxItemType.getByThingAndChannel(thingTypeUIDOf(channelUID), channelUID.getId()); + + if (itemType == VeluxItemType.UNKNOWN) { + logger.warn("handleCommand(): cannot determine type of Channel {}, ignoring command {}.", channelUID, + command); + logger.trace("handleCommand() aborting."); + return; + } + + if (veluxBridgeConfiguration.hasChanged) { + logger.trace("handleCommand(): work on updated bridge configuration parameters."); + bridgeParamsUpdated(); + } + + syncChannelsWithProducts(); + + if (command instanceof RefreshType) { + /* + * =========================================================== + * Refresh part + */ + logger.trace("handleCommand(): work on refresh."); + if (!itemType.isReadable()) { + logger.debug("handleCommand(): received a Refresh command for a non-readable item."); + } else { + logger.trace("handleCommand(): refreshing item {} (type {}).", itemName, itemType); + switch (itemType) { + // Bridge channels + case BRIDGE_STATUS: + newState = ChannelBridgeStatus.handleRefresh(channelUID, channelId, this); + break; + case BRIDGE_TIMESTAMP: + newState = new DecimalType(thisBridge.lastSuccessfulCommunication()); + break; + case BRIDGE_FIRMWARE: + newState = ChannelBridgeFirmware.handleRefresh(channelUID, channelId, this); + break; + case BRIDGE_IPADDRESS: + case BRIDGE_SUBNETMASK: + case BRIDGE_DEFAULTGW: + case BRIDGE_DHCP: + newState = ChannelBridgeLANconfig.handleRefresh(channelUID, channelId, this); + break; + case BRIDGE_WLANSSID: + case BRIDGE_WLANPASSWORD: + newState = ChannelBridgeWLANconfig.handleRefresh(channelUID, channelId, this); + break; + case BRIDGE_SCENES: + newState = ChannelBridgeScenes.handleRefresh(channelUID, channelId, this); + break; + case BRIDGE_PRODUCTS: + newState = ChannelBridgeProducts.handleRefresh(channelUID, channelId, this); + break; + case BRIDGE_CHECK: + newState = ChannelBridgeCheck.handleRefresh(channelUID, channelId, this); + break; + // Actuator channels + case ACTUATOR_POSITION: + case ACTUATOR_STATE: + case ROLLERSHUTTER_POSITION: + case WINDOW_POSITION: + newState = ChannelActuatorPosition.handleRefresh(channelUID, channelId, this); + break; + case ACTUATOR_LIMITATION: + case ROLLERSHUTTER_LIMITATION: + case WINDOW_LIMITATION: + // TODO: waiting for feedback from Velux engineering + if (false) { + ChannelActuatorLimitation.handleRefresh(channelUID, channelId, this); + } + break; + + // VirtualShutter channels + case VSHUTTER_POSITION: + newState = ChannelVShutterPosition.handleRefresh(channelUID, channelId, this); + break; + + default: + logger.trace("handleCommand(): cannot handle REFRESH on channel {} as it is of type {}.", + itemName, channelId); + } + } + if (newState != null) { + logger.debug("handleCommand(): updating {} ({}) to {}.", itemName, channelUID, newState); + updateState(channelUID, newState); + } else { + logger.info("handleCommand({},{}): updating of item {} (type {}) failed.", channelUID.getAsString(), + command, itemName, itemType); + } + } else { + /* + * =========================================================== + * Modification part + */ + logger.trace("handleCommand(): working on item {} (type {}) with COMMAND {}.", itemName, itemType, command); + switch (itemType) { + // Bridge channels + case BRIDGE_RELOAD: + if (command.equals(OnOffType.ON)) { + logger.trace("handleCommand(): about to reload informations from veluxBridge."); + bridgeParamsUpdated(); + } else { + logger.trace("handleCommand(): ignoring OFF command."); + } + break; + case BRIDGE_DO_DETECTION: + ChannelBridgeDoDetection.handleCommand(channelUID, channelId, command, this); + break; + + // Scene channels + case SCENE_ACTION: + ChannelSceneAction.handleCommand(channelUID, channelId, command, this); + break; + case SCENE_SILENTMODE: + ChannelSceneSilentmode.handleCommand(channelUID, channelId, command, this); + break; + + // Actuator channels + case ACTUATOR_POSITION: + case ACTUATOR_STATE: + case ROLLERSHUTTER_POSITION: + case WINDOW_POSITION: + ChannelActuatorPosition.handleCommand(channelUID, channelId, command, this); + break; + case ACTUATOR_LIMITATION: + case ROLLERSHUTTER_LIMITATION: + case WINDOW_LIMITATION: + // TODO: waiting for feedback from Velux engineering + if (false) { + ChannelActuatorLimitation.handleCommand(channelUID, channelId, command, this); + } + break; + // ToDo: wait for Velux to correct implementation + // case ACTUATOR_SILENTMODE: + // logger.warn( + // "handleCommand() sorry, but yet not implemented: cannot handle command {} on channel {} (type {}).", + // command, itemName, itemType); + // break; + + // VirtualShutter channels + case VSHUTTER_POSITION: + Command newValue = ChannelVShutterPosition.handleCommand(channelUID, channelId, command, this); + if (newValue != null) { + postCommand(channelUID, newValue); + } + break; + + default: + logger.warn("handleCommand() cannot handle command {} on channel {} (type {}).", command, itemName, + itemType); + } + + } + ThingProperty.setValue(this, VeluxBindingConstants.CHANNEL_BRIDGE_IOTIMESTAMP, thisBridge.lastCommunication()); + ThingProperty.setValue(this, VeluxBindingConstants.CHANNEL_BRIDGE_TIMESTAMP, + thisBridge.lastSuccessfulCommunication()); + logger.trace("handleCommand() done."); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxChannelHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxChannelHandler.java new file mode 100644 index 0000000000000..567931703a3ef --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxChannelHandler.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2010-2019 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.velux.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel VShutter : + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ *
    + *
  • OpenHAB Event Bus → Velux bridge + *

    + * Sending commands and value updates by method {@link #handleCommand}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +abstract class VeluxChannelHandler { + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, // NO_UCD (unused code) + VeluxBridgeHandler thisBridgeHandler) { + throw new IllegalStateException("handleRefresh hasn't been set up in the subclass"); + } + + /** + * Communication method to update the real world according to the passed channel value (or command). + * + * @param channelUID The item passed as type {@link ChannelUID} for which to following command is addressed to. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param command The command passed as type {@link Command} for the mentioned item. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newValue ... + */ + static @Nullable Command handleCommand(ChannelUID channelUID, String channelId, Command command, // NO_UCD (unused + // code) + VeluxBridgeHandler thisBridgeHandler) { + throw new IllegalStateException("handleRefresh hasn't been set up in the subclass"); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxHandler.java new file mode 100644 index 0000000000000..2ab8e923acd97 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxHandler.java @@ -0,0 +1,126 @@ +/** + * Copyright (c) 2010-2019 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.velux.handler; + +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.core.thing.Bridge; +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.ThingStatusDetail; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.RefreshType; +import org.openhab.binding.velux.internal.config.VeluxThingConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/*** + * The{@link VeluxHandler} is responsible for handling commands, which are + * sent via {@link VeluxBridgeHandler} to one of the channels. + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxHandler extends ExtendedBaseThingHandler { + private final Logger logger = LoggerFactory.getLogger(VeluxHandler.class); + + @SuppressWarnings("unused") + private @Nullable VeluxThingConfiguration configuration = null; + + public VeluxHandler(Thing thing) { + super(thing); + logger.trace("VeluxHandler(constructor) called."); + } + + @Override + public void initialize() { + logger.trace("initialize() called."); + Bridge thisBridge = getBridge(); + logger.debug("initialize(): Initializing thing {} in combination with bridge {}.", getThing().getUID(), + thisBridge); + if (thisBridge == null) { + logger.trace("initialize() updating ThingStatus to OFFLINE/CONFIGURATION_PENDING."); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING); + + } else if (thisBridge.getStatus() == ThingStatus.ONLINE) { + logger.trace("initialize() updating ThingStatus to ONLINE."); + updateStatus(ThingStatus.ONLINE); + initializeProperties(); + + } else { + logger.trace("initialize() updating ThingStatus to OFFLINE/BRIDGE_OFFLINE."); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); + + } + logger.trace("initialize() done."); + } + + private synchronized void initializeProperties() { + logger.trace("initializeProperties() called."); + + configuration = getConfigAs(VeluxThingConfiguration.class); + logger.trace("initializeProperties() done."); + } + + @Override + public void dispose() { + logger.trace("dispose() called."); + super.dispose(); + } + + @Override + public void channelLinked(ChannelUID channelUID) { + logger.trace("channelLinked({}) called.", channelUID.getAsString()); + + if (thing.getStatus() == ThingStatus.ONLINE) { + handleCommand(channelUID, RefreshType.REFRESH); + } + } + + @SuppressWarnings("null") + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + logger.trace("handleCommand({},{}) called.", channelUID.getAsString(), command); + + if (getBridge() == null) { + logger.trace("handleCommand() nothing yet to do as there is no bridge available."); + } else { + getBridge().getHandler().handleCommand(channelUID, command); + } + logger.trace("handleCommand() done."); + } + + @Override + public void handleConfigurationUpdate(Map configurationParameters) { + if (isInitialized()) { // prevents change of address + validateConfigurationParameters(configurationParameters); + Configuration configuration = editConfiguration(); + for (Entry configurationParameter : configurationParameters.entrySet()) { + logger.trace("handleConfigurationUpdate(): found modified config entry {}.", + configurationParameter.getKey()); + } + // persist new configuration and reinitialize handler + dispose(); + updateConfiguration(configuration); + initialize(); + } else { + super.handleConfigurationUpdate(configurationParameters); + } + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/package-info.java new file mode 100644 index 0000000000000..ae1993935f7b7 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/package-info.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2010-2019 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 + */ +/** + * Class for handling any kind of openHAB events and requests. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.handler; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java new file mode 100644 index 0000000000000..7292afc5cfea4 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java @@ -0,0 +1,123 @@ +/** + * Copyright (c) 2010-2019 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.velux.internal; + +import static org.apache.commons.lang.StringUtils.isNotBlank; + +import java.lang.reflect.Field; + +import org.apache.commons.lang.StringUtils; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/*** + * Class for Velux binding which validates the bridge configuration parameters. + * + *
    + *
  • {@link #VeluxBinding constructor}
  • + *
  • {@link #checked }
  • + *
+ * + * @author Guenther Schreiner - Initial contribution + * @author Joachim Sauer (@Saua) - fix for isBulkRetrievalEnabled, isSequentialEnforced + */ +@NonNullByDefault +public class VeluxBinding extends VeluxBridgeConfiguration { + private final Logger logger = LoggerFactory.getLogger(getClass()); + + /*** + *** Startup methods + ***/ + + /** + * Constructor + * + * initializes the interface towards the Velux bridge. Furthermore, the checked configuration can be retrieved by + * the method {@link #checked checked}. + * + * @param uncheckedConfiguration + * The configuration of type {@link VeluxBridgeConfiguration} + * which shall be checked. + */ + public VeluxBinding(@Nullable VeluxBridgeConfiguration uncheckedConfiguration) { + logger.trace("VeluxBinding(constructor) called."); + for (Field field : VeluxBridgeConfiguration.class.getFields()) { + if (!StringUtils.capitalize(field.getName()).contentEquals(field.getName())) { + logger.trace("VeluxBinding(): FYI: a potential configuration string is '{}'.", field.getName()); + } + } + if (uncheckedConfiguration == null) { + logger.debug("No configuration found, using default values."); + } else { + logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_PROTOCOL); + if (isNotBlank(uncheckedConfiguration.protocol)) { + this.protocol = uncheckedConfiguration.protocol; + } + logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_IPADDRESS); + if (isNotBlank(uncheckedConfiguration.ipAddress)) { + this.ipAddress = uncheckedConfiguration.ipAddress; + } + logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_TCPPORT); + if ((uncheckedConfiguration.tcpPort > 0) && (uncheckedConfiguration.tcpPort <= 65535)) { + this.tcpPort = uncheckedConfiguration.tcpPort; + } + logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_PASSWORD); + if (isNotBlank(uncheckedConfiguration.password)) { + this.password = uncheckedConfiguration.password; + } + logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_TIMEOUT_MSECS); + if ((uncheckedConfiguration.timeoutMsecs > 0) && (uncheckedConfiguration.timeoutMsecs <= 10000)) { + this.timeoutMsecs = uncheckedConfiguration.timeoutMsecs; + } + logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_RETRIES); + if ((uncheckedConfiguration.retries >= 0) && (uncheckedConfiguration.retries <= 10)) { + this.retries = uncheckedConfiguration.retries; + } + logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_REFRESH_MSECS); + if ((uncheckedConfiguration.refreshMSecs > 0) && (uncheckedConfiguration.refreshMSecs <= 10000)) { + this.refreshMSecs = uncheckedConfiguration.refreshMSecs; + } + this.isBulkRetrievalEnabled = uncheckedConfiguration.isBulkRetrievalEnabled; + this.isSequentialEnforced = uncheckedConfiguration.isSequentialEnforced; + this.isProtocolTraceEnabled = uncheckedConfiguration.isProtocolTraceEnabled; + + } + logger.trace("VeluxBinding(constructor) done."); + } + + /** + * Access method returning a validated configuration. + * + * @return bridgeConfiguration of type {@link VeluxBridgeConfiguration + * VeluxBridgeConfiguration}. + */ + public VeluxBridgeConfiguration checked() { + logger.trace("checked() called."); + logger.info("{}Config[{}={},{}={},{}={},{}={},{}={},{}={},{}={},{}={},{}={},{}={}]", + VeluxBindingConstants.BINDING_ID, VeluxBridgeConfiguration.BRIDGE_PROTOCOL, protocol, + VeluxBridgeConfiguration.BRIDGE_IPADDRESS, this.ipAddress, VeluxBridgeConfiguration.BRIDGE_TCPPORT, + tcpPort, VeluxBridgeConfiguration.BRIDGE_PASSWORD, password.replaceAll(".", "*"), + VeluxBridgeConfiguration.BRIDGE_TIMEOUT_MSECS, timeoutMsecs, VeluxBridgeConfiguration.BRIDGE_RETRIES, + retries, VeluxBridgeConfiguration.BRIDGE_REFRESH_MSECS, refreshMSecs, + VeluxBridgeConfiguration.BRIDGE_IS_BULK_RETRIEVAL_ENABLED, isBulkRetrievalEnabled, + VeluxBridgeConfiguration.BRIDGE_IS_SEQUENTIAL_ENFORCED, isSequentialEnforced, + VeluxBridgeConfiguration.BRIDGE_PROTOCOL_TRACE_ENABLED, isProtocolTraceEnabled); + logger.trace("checked() done."); + return this; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConfig.java new file mode 100644 index 0000000000000..c216ce46b0059 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConfig.java @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2010-2019 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.velux.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This represents the configuration of a openHAB item that is binded to a Velux + * KLF200 Gateway. It contains the following information: + * + *
    + *
  • bindingItemType + *

    + * Accessible via + * {@link org.openhab.binding.velux.internal.VeluxBindingConfig#getBindingItemType + * getBindingItemType} as representation of the Velux device is filed in the Velux bridge.

  • + *
  • bindingConfig + *

    + * Accessible via + * {@link org.openhab.binding.velux.internal.VeluxBindingConfig#getBindingConfig getBindingConfig} containing the + * device-specific binding configuration + * as declared in the binding configuration (possibly adapted by preprocessing).

  • + *
+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +class VeluxBindingConfig { + + private final Logger logger = LoggerFactory.getLogger(VeluxBindingConfig.class); + + /** + * The binding type of the velux item described by type {@link org.openhab.binding.velux.internal.VeluxItemType + * VeluxItemType}. + */ + private VeluxItemType bindingItemType; + + /** + * Device-specific binding configuration as declared in the binding configuration (possibly adapted by + * preprocessing). + */ + private String bindingConfig; + + /** + * Constructor of the VeluxBindingConfig. + * + * @param bindingItemType + * The Velux item type {@link org.openhab.binding.velux.internal.VeluxItemType + * VeluxItemType} + * which the Velux device is filed in the Velux bridge. + * @param bindingConfig + * The optional configuration type of the Velux binding. + */ + public VeluxBindingConfig(VeluxItemType bindingItemType, String bindingConfig) { // NO_UCD (use default) + logger.trace("VeluxBindingConfig(constructor:{},{}) called.", bindingItemType, bindingConfig); + + this.bindingItemType = bindingItemType; + this.bindingConfig = bindingConfig; + } + + /** + * @return bindingTypeItem of type {@link org.openhab.binding.velux.internal.VeluxItemType + * VeluxItemType}. + */ + public VeluxItemType getBindingItemType() { + return this.bindingItemType; + } + + /** + * @return bindingConfig of type {@link String} that + * has been declared in the binding configuration, + * possibly adapted by preprocessing. + */ + public String getBindingConfig() { + return this.bindingConfig; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxHandlerFactory.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxHandlerFactory.java new file mode 100644 index 0000000000000..dd633ce6992ba --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxHandlerFactory.java @@ -0,0 +1,136 @@ +/** + * Copyright (c) 2010-2019 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.velux.internal; + +import java.util.Hashtable; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.config.discovery.DiscoveryService; +import org.eclipse.smarthome.core.thing.Bridge; +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.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.discovery.VeluxDiscoveryService; +import org.openhab.binding.velux.handler.VeluxBindingHandler; +import org.openhab.binding.velux.handler.VeluxBridgeHandler; +import org.openhab.binding.velux.handler.VeluxHandler; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.component.annotations.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +@Component(service = ThingHandlerFactory.class, name = "binding.velux") +public class VeluxHandlerFactory extends BaseThingHandlerFactory { + private final Logger logger = LoggerFactory.getLogger(VeluxHandlerFactory.class); + + // Class internal + + private @Nullable ServiceRegistration discoveryServiceReg; + private @Nullable VeluxBindingHandler veluxBindingHandler = null; + private Integer veluxBridgeCount = 0; + + // Private + + private void registerDeviceDiscoveryService(VeluxBridgeHandler bridgeHandler) { + VeluxDiscoveryService discoveryService = new VeluxDiscoveryService(bridgeHandler); + discoveryServiceReg = bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, + new Hashtable()); + } + + private synchronized void unregisterDeviceDiscoveryService() { + logger.trace("unregisterDeviceDiscoveryService({}) called."); + + if (discoveryServiceReg != null) { + discoveryServiceReg.unregister(); + discoveryServiceReg = null; + } + } + + // Utility methods + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + boolean result = VeluxBindingConstants.SUPPORTED_THINGS_BRIDGE.contains(thingTypeUID) + || VeluxBindingConstants.SUPPORTED_THINGS_ITEMS.contains(thingTypeUID) + || VeluxBindingConstants.SUPPORTED_THINGS_BINDING.contains(thingTypeUID); + logger.trace("supportsThingType({}) called and returns {}.", thingTypeUID, result); + return result; + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + logger.trace("createHandler({}) called.", thing.getLabel()); + + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + // Handle Binding creation as 1st choice + if (VeluxBindingConstants.SUPPORTED_THINGS_BINDING.contains(thingTypeUID)) { + logger.trace("createHandler(): Creating a Handler for thing '{}'.", thing.getUID()); + veluxBindingHandler = new VeluxBindingHandler(thing); + return veluxBindingHandler; + } + + else + // Handle Bridge creation as 2nd choice + if (VeluxBindingConstants.SUPPORTED_THINGS_BRIDGE.contains(thingTypeUID)) { + logger.trace("createHandler(): Creating a VeluxBridgeHandler for thing '{}'.", thing.getUID()); + VeluxBridgeHandler handler = new VeluxBridgeHandler((Bridge) thing); + veluxBridgeCount++; + synchronized (this) { + if (veluxBindingHandler != null) { + veluxBindingHandler.updateNoOfBridges(veluxBridgeCount); + } + } + registerDeviceDiscoveryService(handler); + return handler; + } + + else + // Handle creation of Things behind the Bridge + if (VeluxBindingConstants.SUPPORTED_THINGS_ITEMS.contains(thingTypeUID)) { + logger.trace("createHandler(): Creating a VeluxHandler for thing '{}'.", thing.getUID()); + return new VeluxHandler(thing); + } else { + logger.warn("ThingHandler not found for {}.", thingTypeUID); + return null; + } + } + + @Override + protected void removeHandler(ThingHandler thingHandler) { + logger.trace("removeHandler({}) called.", thingHandler.toString()); + + if (thingHandler.getThing().getThingTypeUID().equals(VeluxBindingConstants.THING_TYPE_BRIDGE)) { + veluxBridgeCount--; + unregisterDeviceDiscoveryService(); + synchronized (this) { + if (veluxBindingHandler != null) { + veluxBindingHandler.updateNoOfBridges(veluxBridgeCount); + } + } + } + super.removeHandler(thingHandler); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxItemType.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxItemType.java new file mode 100644 index 0000000000000..474f89fd4548b --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxItemType.java @@ -0,0 +1,477 @@ +/** + * Copyright (c) 2010-2019 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.velux.internal; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.items.GenericItem; +import org.eclipse.smarthome.core.library.items.NumberItem; +import org.eclipse.smarthome.core.library.items.RollershutterItem; +import org.eclipse.smarthome.core.library.items.StringItem; +import org.eclipse.smarthome.core.library.items.SwitchItem; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Enumeration of Types of a Velux item. + *
+ * Provides information about: + *
    + *
  • associated thing identified by String
  • + *
  • defined channel identified by String
  • + *
  • {@link VeluxItemType#getItemClass getItemClass} item class
  • + *
  • {@link VeluxItemType#isReadable isReadable} about a read possibility
  • + *
  • {@link VeluxItemType#isWritable isWritable} about a write possibility
  • + *
  • {@link VeluxItemType#isExecutable isExecutable} about an execute possibility
  • + *
  • {@link VeluxItemType#isToBeRefreshed isToBeRefreshed} about necessarily to be refreshed.
  • + *
  • {@link VeluxItemType#isToBeRefreshedNow isToBeRefreshedNow} about necessarily to be refreshed at this time.
  • + *
+ * + * In addition there are helper methods providing information about: + * + *
    + *
  • {@link VeluxItemType#getByString getByString} the enum by identifier string,
  • + *
  • {@link VeluxItemType#getByThingAndChannel getByThingAndChannel} to retrieve an enum instance selected by Thing + * and Channel identifier,
  • + *
  • {@link VeluxItemType#getThingIdentifiers getThingIdentifiers} to retrieve any Thing identifiers as array of + * String,
  • + *
  • {@link VeluxItemType#getChannelIdentifiers getChannelIdentifiers} to retrieve any Channel identifiers as array of + * String.
  • + *
+ *

+ * Within this enumeration, the expected behaviour of the OpenHAB item (resp. Channel) + * is set. For each kind of Channel (i.e. bridge or device) parameter a + * set of information is defined: + *

    + *
  • + * Unique identification by: + *
      + *
    • Thing name as string,
    • + *
    • Channel name as string,
    • + *
    + *
  • + *
  • Channel type as OpenHAB type,
  • + *
  • ability flag whether this item is to be read,
  • + *
  • ability flag whether this item is able to be modified,
  • + *
  • ability flag whether this item is to be used as execution trigger.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution + * + */ +@NonNullByDefault +public enum VeluxItemType { + // @formatter:off + UNKNOWN(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.UNKNOWN, TypeFlavor.UNUSABLE), + // + BINDING_INFORMATION(VeluxBindingConstants.THING_TYPE_BINDING, VeluxBindingConstants.CHANNEL_BINDING_INFORMATION, TypeFlavor.READONLY_VOLATILE_STRING), + // + BRIDGE_STATUS(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_STATUS, TypeFlavor.READONLY_VOLATILE_STRING), + BRIDGE_TIMESTAMP(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_TIMESTAMP, TypeFlavor.READONLY_VOLATILE_NUMBER), + BRIDGE_RELOAD(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_RELOAD, TypeFlavor.INITIATOR), + BRIDGE_DO_DETECTION(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_DO_DETECTION, TypeFlavor.INITIATOR), + BRIDGE_FIRMWARE(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_FIRMWARE, TypeFlavor.READONLY_STATIC_STRING), + BRIDGE_IPADDRESS(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_IPADDRESS, TypeFlavor.READONLY_STATIC_STRING), + BRIDGE_SUBNETMASK(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_SUBNETMASK, TypeFlavor.READONLY_STATIC_STRING), + BRIDGE_DEFAULTGW(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_DEFAULTGW, TypeFlavor.READONLY_STATIC_STRING), + BRIDGE_DHCP(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_DHCP, TypeFlavor.READONLY_STATIC_SWITCH), + BRIDGE_WLANSSID(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_WLANSSID, TypeFlavor.READONLY_STATIC_STRING), + BRIDGE_WLANPASSWORD(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_WLANPASSWORD, TypeFlavor.READONLY_STATIC_STRING), + BRIDGE_PRODUCTS(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_PRODUCTS, TypeFlavor.READONLY_STATIC_STRING), + BRIDGE_SCENES(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_SCENES, TypeFlavor.READONLY_STATIC_STRING), + BRIDGE_CHECK(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_CHECK, TypeFlavor.READONLY_STATIC_STRING), + // + ACTUATOR_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION, TypeFlavor.MANIPULATOR_SHUTTER), + ACTUATOR_STATE(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_STATE, TypeFlavor.MANIPULATOR_SWITCH), +//TODO: waiting for feedback from Velux: API does not work properly. +// ACTUATOR_SILENTMODE(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_SILENTMODE, TypeFlavor.WRITEONLY_VOLATILE_SWITCH), + ACTUATOR_LIMITATION(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_LIMITATION, TypeFlavor.MANIPULATOR_SHUTTER), + // + ROLLERSHUTTER_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_ROLLERSHUTTER,VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION, TypeFlavor.MANIPULATOR_SHUTTER), + ROLLERSHUTTER_LIMITATION(VeluxBindingConstants.THING_TYPE_VELUX_ROLLERSHUTTER,VeluxBindingConstants.CHANNEL_ACTUATOR_LIMITATION,TypeFlavor.MANIPULATOR_SHUTTER), + // + WINDOW_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_WINDOW, VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION, TypeFlavor.MANIPULATOR_SHUTTER), + WINDOW_LIMITATION(VeluxBindingConstants.THING_TYPE_VELUX_WINDOW, VeluxBindingConstants.CHANNEL_ACTUATOR_LIMITATION, TypeFlavor.MANIPULATOR_SHUTTER), + // + SCENE_ACTION(VeluxBindingConstants.THING_TYPE_VELUX_SCENE, VeluxBindingConstants.CHANNEL_SCENE_ACTION, TypeFlavor.INITIATOR), + SCENE_SILENTMODE(VeluxBindingConstants.THING_TYPE_VELUX_SCENE, VeluxBindingConstants.CHANNEL_SCENE_SILENTMODE, TypeFlavor.WRITEONLY_VOLATILE_SWITCH), + // + VSHUTTER_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_VSHUTTER, VeluxBindingConstants.CHANNEL_VSHUTTER_POSITION, TypeFlavor.MANIPULATOR_SHUTTER), + ; + // @formatter:on + + private enum TypeFlavor { + /** + * Used to present read-only non-volatile configuration parameters as StringItem. + */ + READONLY_STATIC_STRING, + /** + * Used to present read-only non-volatile configuration parameters as SwitchItem. + */ + READONLY_STATIC_SWITCH, + /** + * Used to present volatile configuration parameters as StringItem. + */ + READONLY_VOLATILE_STRING, + /** + * Used to present volatile configuration parameters as NumberItem. + */ + READONLY_VOLATILE_NUMBER, + /** + * Used to present volatile configuration parameters as NumberItem. + */ + WRITEONLY_VOLATILE_SWITCH, + /** + * Used to present volatile configuration parameters as SwitchItem. + */ + READWRITE_VOLATILE_SWITCH, + /** + * Used to initiate an action. + */ + INITIATOR, + /** + * Used to manipulate an actuator. + */ + MANIPULATOR_SHUTTER, + /** + * Used to manipulate an actuator. + */ + MANIPULATOR_SWITCH, + /** + * Used to define an UNUSABLE entry. + */ + UNUSABLE, + + } + + private ThingTypeUID thingIdentifier; + private String channelIdentifier; + private Class itemClass; + private boolean itemIsReadable; + private boolean itemIsWritable; + private boolean itemIsExecutable; + private boolean itemIsToBeRefreshed; + private int itemsRefreshDivider; + + private static final Logger LOGGER = LoggerFactory.getLogger(VeluxItemType.class); + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private static final int REFRESH_CYCLE_FIRST_TIME = 0; + private static final int REFRESH_ONCE_A_DAY = 8640; + private static final int REFRESH_EACH_MINUTE = 6; + private static final int REFRESH_EVERY_CYCLE = 1; + + VeluxItemType(ThingTypeUID thingIdentifier, String channelIdentifier, TypeFlavor typeFlavor) { + this.thingIdentifier = thingIdentifier; + this.channelIdentifier = channelIdentifier; + switch (typeFlavor) { + case READONLY_STATIC_STRING: + this.itemClass = StringItem.class; + this.itemIsReadable = true; + this.itemIsWritable = false; + this.itemIsExecutable = false; + this.itemIsToBeRefreshed = true; + this.itemsRefreshDivider = REFRESH_ONCE_A_DAY; + break; + case READONLY_STATIC_SWITCH: + this.itemClass = SwitchItem.class; + this.itemIsReadable = true; + this.itemIsWritable = false; + this.itemIsExecutable = false; + this.itemIsToBeRefreshed = true; + this.itemsRefreshDivider = REFRESH_ONCE_A_DAY; + break; + + case READONLY_VOLATILE_STRING: + this.itemClass = StringItem.class; + this.itemIsReadable = true; + this.itemIsWritable = false; + this.itemIsExecutable = false; + this.itemIsToBeRefreshed = true; + this.itemsRefreshDivider = REFRESH_EACH_MINUTE; + break; + case READONLY_VOLATILE_NUMBER: + this.itemClass = NumberItem.class; + this.itemIsReadable = true; + this.itemIsWritable = false; + this.itemIsExecutable = false; + this.itemIsToBeRefreshed = true; + this.itemsRefreshDivider = REFRESH_EACH_MINUTE; + break; + case WRITEONLY_VOLATILE_SWITCH: + this.itemClass = SwitchItem.class; + this.itemIsReadable = false; + this.itemIsWritable = true; + this.itemIsExecutable = false; + this.itemIsToBeRefreshed = false; + this.itemsRefreshDivider = REFRESH_EACH_MINUTE; + break; + case READWRITE_VOLATILE_SWITCH: + this.itemClass = SwitchItem.class; + this.itemIsReadable = true; + this.itemIsWritable = true; + this.itemIsExecutable = false; + this.itemIsToBeRefreshed = true; + this.itemsRefreshDivider = REFRESH_EVERY_CYCLE; + break; + + case INITIATOR: + this.itemClass = SwitchItem.class; + this.itemIsReadable = false; + this.itemIsWritable = false; + this.itemIsExecutable = true; + this.itemIsToBeRefreshed = false; + this.itemsRefreshDivider = 1; + break; + + case MANIPULATOR_SHUTTER: + this.itemClass = RollershutterItem.class; + this.itemIsReadable = true; + this.itemIsWritable = true; + this.itemIsExecutable = false; + this.itemIsToBeRefreshed = true; + this.itemsRefreshDivider = REFRESH_EACH_MINUTE; + break; + + case MANIPULATOR_SWITCH: + this.itemClass = SwitchItem.class; + this.itemIsReadable = true; + this.itemIsWritable = true; + this.itemIsExecutable = false; + this.itemIsToBeRefreshed = true; + this.itemsRefreshDivider = REFRESH_EACH_MINUTE; + break; + + case UNUSABLE: + default: + this.itemClass = StringItem.class; + this.itemIsReadable = false; + this.itemIsWritable = false; + this.itemIsExecutable = false; + this.itemIsToBeRefreshed = false; + this.itemsRefreshDivider = 1; + } + } + + private VeluxItemType(ThingTypeUID thingIdentifier, String channelIdentifier, + Class itemClass, boolean isReadable, boolean isWritable, boolean isExecutable, + boolean isToBeRefreshed, int refreshDivider) { + this.thingIdentifier = thingIdentifier; + this.channelIdentifier = channelIdentifier; + this.itemClass = itemClass; + this.itemIsReadable = isReadable; + this.itemIsWritable = isWritable; + this.itemIsExecutable = isExecutable; + this.itemIsToBeRefreshed = isToBeRefreshed; + this.itemsRefreshDivider = refreshDivider; + } + + @Override + public String toString() { + return this.thingIdentifier + "/" + this.channelIdentifier; + } + + /** + * {@link VeluxItemType} access method to query Identifier on this type of item. + * + * @return thingIdentifier of type String describing the value of the enum {@link VeluxItemType} + * return + */ + public ThingTypeUID getIdentifier() { + return this.thingIdentifier; + } + + /** + * {@link VeluxItemType} access method to query the appropriate type of item. + * + * @return itemClass of type Item describing the possible type of this item. + */ + public Class getItemClass() { + return this.itemClass; + } + + /** + * {@link VeluxItemType} access method to query Read possibility on this type of item. + * + * @return itemIsReadable of type boolean describing the ability to perform a write operation. + */ + public boolean isReadable() { + logger.trace("isReadable() returns {}.", this.itemIsReadable); + return this.itemIsReadable; + } + + /** + * {@link VeluxItemType} access method to query Write possibility on this type of item. + * + * @return itemIsWritable of type boolean describing the ability to perform a write operation. + */ + public boolean isWritable() { + logger.trace("isWritable() returns {}.", this.itemIsWritable); + return this.itemIsWritable; + } + + /** + * {@link VeluxItemType} access method to query Execute possibility on this type of item. + * + * @return isExecute of type boolean describing the ability to perform a write operation. + */ + public boolean isExecutable() { + logger.trace("isExecutable() returns {}.", this.itemIsExecutable); + return this.itemIsExecutable; + } + + /** + * {@link VeluxItemType} access method to query the need of refresh on this type of item. + * + * @return isExecute of type boolean describing the ability to perform a write operation. + */ + public boolean isToBeRefreshed() { + logger.trace("isToBeRefreshed() returns {}.", this.itemIsToBeRefreshed); + return this.itemIsToBeRefreshed; + } + + /** + * {@link VeluxItemType} access method to query the refreshMSecs interval on this type of item. + * + * @return refreshDivider of type int describing the factor. + */ + public int getRefreshDivider() { + logger.trace("getRefreshDivider() returns {}.", this.itemsRefreshDivider); + return this.itemsRefreshDivider; + } + + /** + * {@link VeluxItemType} access method to find an enum by itemTypeName. + * + * @param itemTypeName as name of requested Thing of type String. + * + * @return veluxItemType of type VeluxItemType describing the appropriate enum. + */ + public VeluxItemType getByString(String itemTypeName) { // NO_UCD (unused code) + logger.trace("getByString({}) called.", itemTypeName); + try { + return VeluxItemType.valueOf(itemTypeName); + } catch (IllegalArgumentException e) { + return UNKNOWN; + } + } + + /** + * {@link VeluxItemType} access method to find an enum by name. + * + * @param thingIdentifier as name of requested Thing of type String. + * @param channelIdentifier as name of requested Channel of type String. + * + * @return veluxItemType of type VeluxItemType describing the appropriate enum. + */ + public static VeluxItemType getByThingAndChannel(ThingTypeUID thingIdentifier, String channelIdentifier) { + LOGGER.trace("getByThingAndChannel({},{}) called.", thingIdentifier, channelIdentifier); + for (VeluxItemType v : VeluxItemType.values()) { + if (((thingIdentifier.equals(v.thingIdentifier)) || (thingIdentifier.equals(v.thingIdentifier))) + && (channelIdentifier.equals(v.channelIdentifier))) { + LOGGER.trace("getByThingAndChannel() returns enum {}.", v); + return v; + } + } + LOGGER.trace("getByThingAndChannel() returns UNKNOWN."); + return UNKNOWN; + } + + /** + * {@link VeluxItemType} access method to find an enum by name. + * + * @return veluxItemType of type VeluxItemType describing the appropriate enum. + */ + public static String[] getThingIdentifiers() { // NO_UCD (unused code) + LOGGER.trace("getThingIdentifiers() called."); + Set> uniqueSet = new HashSet>(); + for (VeluxItemType v : VeluxItemType.values()) { + LOGGER.trace("getThingIdentifiers() adding {}.", v.thingIdentifier); + uniqueSet.add(Arrays.asList(v.thingIdentifier)); + } + return uniqueSet.toArray(new String[uniqueSet.size()]); + } + + /** + * {@link VeluxItemType} access method to find an enum by name. + * + * @param thingIdentifier as name of requested Thing of type String. + * + * @return veluxItemType of type VeluxItemType describing the appropriate enum. + */ + public static String[] getChannelIdentifiers(ThingTypeUID thingIdentifier) { // NO_UCD (unused code) + LOGGER.trace("getChannelIdentifiers() called."); + Set> uniqueSet = new HashSet>(); + for (VeluxItemType v : VeluxItemType.values()) { + if (thingIdentifier.equals(v.thingIdentifier)) { + uniqueSet.add(Arrays.asList(v.channelIdentifier)); + } + } + return uniqueSet.toArray(new String[uniqueSet.size()]); + } + + /** + * Helper function: Calculate modulo. + * + * @param a as dividend. + * @param b as divisor. + * + * @return true if zero is remainder after division. + */ + private static boolean isModulo(int a, int b) { + return (a % b) == 0; + } + + /** + * {@link VeluxItemType} access method to determine the necessity of being refreshed + * within the current refresh cycle. + * + * @param refreshCycleCounter as identification of the refresh round. + * @param thingIdentifier as name of requested Thing. + * @param channelIdentifier as name of requested Channel. + * + * @return boolean value which expresses the need. + */ + public static boolean isToBeRefreshedNow(int refreshCycleCounter, ThingTypeUID thingIdentifier, + String channelIdentifier) { + LOGGER.trace("isToBeRefreshedNow({},{},{}) called.", refreshCycleCounter, thingIdentifier, channelIdentifier); + + VeluxItemType itemType = getByThingAndChannel(thingIdentifier, channelIdentifier); + + if (itemType == VeluxItemType.UNKNOWN) { + LOGGER.trace("isToBeRefreshedNow(): returning false, as item is not found."); + return false; + } + + if (((refreshCycleCounter == REFRESH_CYCLE_FIRST_TIME) && (itemType.isReadable())) + || (itemType.isToBeRefreshed())) { + if ((refreshCycleCounter == REFRESH_CYCLE_FIRST_TIME) + || (isModulo(refreshCycleCounter, itemType.getRefreshDivider()))) { + LOGGER.trace("isToBeRefreshedNow(): returning false, as item is to be refreshed, now."); + return true; + } else { + LOGGER.trace("isToBeRefreshedNow(): returning false, as refresh cycle has not yet come for this item."); + } + } else { + LOGGER.trace("isToBeRefreshedNow(): returning false, as item is not refreshable."); + } + return false; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxRSBindingConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxRSBindingConfig.java new file mode 100644 index 0000000000000..a4ca1e2812f2e --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxRSBindingConfig.java @@ -0,0 +1,213 @@ +/** + * Copyright (c) 2010-2019 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.velux.internal; + +import java.util.Comparator; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.VeluxBindingConstants; +//import org.openhab.model.item.binding.BindingConfigParseException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This represents the configuration of a openHAB item that is binded to a Velux + * KLF200 Gateway. It contains the following information: + * + *
    + *
  • bindingItemType + *

    + * accessable via + * {@link org.openhab.binding.velux.internal.VeluxRSBindingConfig#getBindingItemType + * getBindingItemType} as representation of the Velux device is filed in the Velux bridge.

  • + *
  • bindingConfig + *

    + * accessable via + * {@link org.openhab.binding.velux.internal.VeluxRSBindingConfig#getBindingConfig getBindingConfig} containing the + * device-specific binding configuration + * as declared in the binding configuration (possibly adapted by preprocessing).

  • + *
+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxRSBindingConfig extends VeluxBindingConfig { + + private final Logger logger = LoggerFactory.getLogger(VeluxRSBindingConfig.class); + + /** + * The ascending sorted list of generic Objects indexed by an Integer + */ + private SortedMap mapAscending = new TreeMap(new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o1.compareTo(o2); + } + }); + /** + * The descending sorted list of generic Objects indexed by an Integer + */ + private SortedMap mapDescending = new TreeMap(new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2.compareTo(o1); + } + }); + + /** + * The sorted list of generic Objects indexed by an Integer + */ + private Integer rollershutterLevel = 0; + + private void veluxRollershutterBindingParser(final String channelValue) { + logger.debug("VeluxRollershutterBindingParser({}) called.", channelValue); + + String[] channelValueParts = channelValue.trim().split(VeluxBindingConstants.BINDING_VALUES_SEPARATOR); + if ((channelValueParts.length % 2) != 0) { + throw new IllegalArgumentException( + "Velux Rollershutter binding must contain an even number of configuration parts separated by '" + + VeluxBindingConstants.BINDING_VALUES_SEPARATOR + "' (ignoring binding '" + channelValue + + "')."); + } + + for (int idx = 0; idx < channelValueParts.length; idx++) { + logger.trace("VeluxRollershutterBindingParser() processing {}.", channelValueParts[idx]); + + int degree; + try { + degree = Integer.parseInt(channelValueParts[idx]); + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + "Velux Rollershutter binding must contain an even number of configuration parts separated by '" + + VeluxBindingConstants.BINDING_VALUES_SEPARATOR + + "' each consisting of a shutter level followed by a scene name (ignoring binding '" + + channelValue + "')."); + } + idx++; + mapAscending.put(degree, channelValueParts[idx]); + mapDescending.put(degree, channelValueParts[idx]); + } + for (Map.Entry nextEntry : mapAscending.entrySet()) { + logger.trace("VeluxRollershutterBindingParser({},{}) processed.", nextEntry.getKey(), nextEntry.getValue()); + } + } + + /** + * Constructor of the VeluxBindingConfig. + * + * @param bindingItemType + * The Velux item type {@link org.openhab.binding.velux.internal.VeluxItemType + * VeluxItemType} which the Velux device is filed in the Velux bridge. + * @param channelValue + * The optional configuration type of the Velux binding. + * @param rollershutterLevel of type Integer with current position. + */ + public VeluxRSBindingConfig(VeluxItemType bindingItemType, String channelValue, Integer rollershutterLevel) { + super(bindingItemType, channelValue); + logger.trace("VeluxRSBindingConfig(constructor:{},{},{}) called.", bindingItemType, channelValue, + rollershutterLevel); + this.rollershutterLevel = rollershutterLevel; + veluxRollershutterBindingParser(channelValue); + } + + /** + * Constructor of the VeluxBindingConfig. + * + * @param bindingItemType + * The Velux item type {@link org.openhab.binding.velux.internal.VeluxItemType + * VeluxItemType} which the Velux device is filed in the Velux bridge. + * @param channelValue + * The optional configuration type of the Velux binding. + */ + public VeluxRSBindingConfig(VeluxItemType bindingItemType, String channelValue) { // NO_UCD (unused code) + super(bindingItemType, channelValue); + logger.trace("VeluxRSBindingConfig(constructor:{},{}) called.", bindingItemType, channelValue); + veluxRollershutterBindingParser(channelValue); + } + + /** + * Returns the next shutter level for an DOWN command w/ adjusting the actual position. + * + * @return rollershutterLevel of type Integer with next position after DOWN command. + */ + public Integer getNextAscendingLevel() { + logger.trace("getNextAscendingLevel() called."); + + for (Map.Entry nextEntry : mapAscending.entrySet()) { + if (nextEntry.getKey() > this.rollershutterLevel) { + this.rollershutterLevel = nextEntry.getKey(); + break; + } + } + logger.trace("getNextAscendingLevel() returning {}.", this.rollershutterLevel); + return this.rollershutterLevel; + + } + + /** + * Returns the next shutter level for an UP command w/ adjusting the actual position. + * + * @return rollershutterLevel of type Integer with next position after UP command. + */ + public Integer getNextDescendingLevel() { + logger.trace("getNextDescendingLevel() called."); + + for (Map.Entry nextEntry : mapDescending.entrySet()) { + if (nextEntry.getKey() < this.rollershutterLevel) { + this.rollershutterLevel = nextEntry.getKey(); + break; + } + } + logger.trace("getNextDescendingLevel() returning {}.", this.rollershutterLevel); + return this.rollershutterLevel; + + } + + /** + * Returns the current shutter level w/o adjusting the actual positioning. + * + * @return rollershutterLevel of type Integer with current position. + * + */ + public Integer getLevel() { + logger.trace("getLevel() returning {}.", this.rollershutterLevel); + return this.rollershutterLevel; + } + + /** + * Returns the scene name of the current shutter level w/o adjusting the actual positioning. + * + * @return sceneName + * A String describing the next scene. + */ + public String getSceneName() { + return getSceneName(this.rollershutterLevel); + } + + /** + * Returns the scene name w/o adjusting the actual positioning. + * + * @param level + * The shutter level is be queried. + * @return sceneName + * A String describing the next scene. + */ + public String getSceneName(Integer level) { // NO_UCD (use private) + logger.trace("getSceneName({}) called.", level); + logger.trace("getSceneName() returning {}.", mapDescending.get(level)); + return mapDescending.get(level); + } +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxBridgeConfiguration.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxBridgeConfiguration.java new file mode 100644 index 0000000000000..0d5cf1c0e1f62 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxBridgeConfiguration.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2010-2019 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.velux.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link VeluxBridgeConfiguration} is a wrapper for + * configuration settings needed to access the + * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider} + * device. + *

+ * It contains the factory default values as well. + *

    + *
  • {@link VeluxBridgeConfiguration#protocol protocol} protocol type + * (one of http or https or slip),
  • + *
  • {@link VeluxBridgeConfiguration#ipAddress ipAddress} bridge IP address,
  • + *
  • {@link VeluxBridgeConfiguration#tcpPort tcpPort} bridge TCP port,
  • + *
  • {@link VeluxBridgeConfiguration#password password} bridge password,
  • + *
  • {@link VeluxBridgeConfiguration#timeoutMsecs timeoutMsecs} communication timeout in milliseconds,
  • + *
  • {@link VeluxBridgeConfiguration#retries retries} number of retries (with exponential backoff algorithm),
  • + *
  • {@link VeluxBridgeConfiguration#refreshMSecs refreshMSecs} refreshMSecs interval for retrieval of bridge + * information.
  • + *
  • {@link VeluxBridgeConfiguration#isBulkRetrievalEnabled isBulkRetrievalEnabled} flag to use bulk product
  • + *
  • {@link VeluxBridgeConfiguration#isSequentialEnforced isSequentialEnforced} flag to enforce sequential control on + * actuators.
  • + *
  • {@link VeluxBridgeConfiguration#isProtocolTraceEnabled isProtocolTraceEnabled} flag to enable protocol logging + * (via loglevel INFO).
  • + *
+ *

+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeConfiguration { + public static final String BRIDGE_PROTOCOL = "protocol"; + public static final String BRIDGE_IPADDRESS = "ipAddress"; + public static final String BRIDGE_TCPPORT = "tcpPort"; + public static final String BRIDGE_PASSWORD = "password"; + public static final String BRIDGE_TIMEOUT_MSECS = "timeoutMsecs"; + public static final String BRIDGE_RETRIES = "retries"; + public static final String BRIDGE_REFRESH_MSECS = "refreshMsecs"; + public static final String BRIDGE_IS_BULK_RETRIEVAL_ENABLED = "isBulkRetrievalEnabled"; + public static final String BRIDGE_IS_SEQUENTIAL_ENFORCED = "isSequentialEnforced"; + public static final String BRIDGE_PROTOCOL_TRACE_ENABLED = "isProtocolTraceEnabled"; + + /* + * Value to flag any changes towards the getter. + */ + public boolean hasChanged = true; + + /* + * Default values - should not be modified + */ + public String protocol = "slip"; + public String ipAddress = "192.168.1.1"; + public int tcpPort = 51200; + public String password = "velux123"; + public int timeoutMsecs = 1000; // one second + public int retries = 5; + public long refreshMSecs = 10000L; // 10 seconds + public boolean isBulkRetrievalEnabled = true; + public boolean isSequentialEnforced = false; + public boolean isProtocolTraceEnabled = false; +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxThingConfiguration.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxThingConfiguration.java new file mode 100644 index 0000000000000..b0750481cca56 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxThingConfiguration.java @@ -0,0 +1,140 @@ +/** + * Copyright (c) 2010-2019 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.velux.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.config.core.Configuration; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.things.VeluxProductSerialNo; + +/** + * The {@link VeluxThingConfiguration} is a wrapper for + * configuration settings needed to access the Velux device. + *

+ * It contains the factory default values as well. + *

+ * There are three parts. Information for: + *

    + *
  • {@link #sceneName Channel of type scene},
  • + *
  • {@link #serial Channels of type actuator, rollershutter and window},
  • + *
  • {@link #sceneLevels Channel of type virtualshutter}.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + * @author Andrew Fiddian-Green - adapted. + */ +@NonNullByDefault +public class VeluxThingConfiguration extends Configuration { + + /** + * {@link #sceneName} of type {@link String}, identifying a Velux scene by human-readable name. + *

+ * Configuration for the channel scene: + *

+ *
    + *
  • {@link #sceneName} for identification of a set of settings, so called scene.
  • + *
+ */ + @SuppressWarnings("unused") + private String sceneName; + + /** + * {@link #serial} of type {@link String}, identifying a io-homecontrol device by its serial number (i.e. + * 43:12:14:5A:12:1C:05:5F). + *

+ * Configuration for the channels actuator, rollershutter and window: + *

+ *
    + *
  • {@link #serial} for identification of a io-homecontrol device,
  • + *
  • {@link #name} for alternate identification of a io-homecontrol device,
  • + *
  • {@link #inverted} for modified value behavior.
  • + *
  • {@link #velocity} for modified action speed.
  • + *
+ */ + @SuppressWarnings("unused") + private String serial; + /** + * {@link #name} of type {@link String}, identifying a io-homecontrol device by its registration name especially + * for somfy as they do not provide a valid serial number. + *

+ * Part of the {@link #serial Configuration for the channels actuator, rollershutter and window}. + *

+ */ + @SuppressWarnings("unused") + private String name; + /** + * {@link #inverted} of type {@link Boolean}, inverts each Channel value. This means 0% will be handled as 100%, + * and vice versa, 100% will be handled as 0%. + *

+ * Part of the {@link #serial Configuration for the channels actuator, rollershutter and window}. + *

+ */ + @SuppressWarnings("unused") + private Boolean inverted; + /** + * {@link #velocity} of type {@link String}, describes the intended speed of action. + * Possible values are defined within VeluxProductVelocity. + *

+ * Part of the {@link #serial Configuration for the channels actuator, rollershutter and window}. + *

+ */ + @SuppressWarnings("unused") + private String velocity; + + /** + * {@link #sceneLevels} of type {@link String}, identifying a number of Velux scenes which act together as a virtual + * shutter. Each scene is defined to a corresponding shutter level. + *

+ * Configuration for the channel virtualshutter: + *

+ *
    + *
  • {@link #sceneLevels} for identification of a set of settings, so called scene.
  • + *
+ *

+ * Additionally it contains an internal variable for keeping the actual virtual shutter level. + *

+ *
    + *
  • {@link #currentLevel} for identification of a set of settings, so called scene.
  • + *
+ */ + @SuppressWarnings("unused") + private String sceneLevels; + /** + * {@link #currentLevel} of type {@link int}, which represents the current shutter level. + *

+ * Private part of the {@link #sceneLevels Configuration for the channel virtualshutter}. + *

+ */ + @SuppressWarnings("unused") + private int currentLevel; + + /** + * Default values - should not be modified + */ + public VeluxThingConfiguration() { + // + sceneName = VeluxBindingConstants.UNKNOWN; + // + serial = VeluxProductSerialNo.UNKNOWN; + name = VeluxBindingConstants.UNKNOWN; + inverted = false; + velocity = VeluxBindingConstants.UNKNOWN; + // + sceneLevels = VeluxBindingConstants.UNKNOWN; + currentLevel = 0; + } + +} +/* + * end-of-internal/config/VeluxThingConfiguration.java + */ diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/package-info.java new file mode 100644 index 0000000000000..d7bd49ceeaf4e --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/package-info.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2010-2019 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 + */ +/** + * Class with default openHAB configuration definitions. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.internal.config; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/package-info.java new file mode 100644 index 0000000000000..7a2c3c5275fee --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/package-info.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2010-2019 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 + */ +/** + * Classes for handling of openHAB bindings and configurations. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.internal; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/Localization.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/Localization.java new file mode 100644 index 0000000000000..252cf680e90e1 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/Localization.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2010-2019 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.velux.internal.utils; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * This is a workaround class for dealing with localization. + * + * It provides the following methods: + *
    + *
  • {@link #getText} returns the localized message.
  • + *
+ *

+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class Localization { + + /* + * ************************ + * ***** Constructors ***** + */ + + /** + * Suppress default constructor for creating a non-instantiable class. + */ + private Localization() { + throw new AssertionError(); + } + + /** + * Converts a given message into an equivalent localized message. + * + * @param key the message of type {@link String} to be converted, + * @param arguments (optional) arguments being referenced within the messageString. + * @return localizedMessageString the resulted message of type {@link String}. + */ + public static String getText(String key, Object... arguments) { + // ToDo: a well-working solution still to be found + String text = String.format(key, arguments); + return text; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/LoggerFulltrace.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/LoggerFulltrace.java new file mode 100644 index 0000000000000..0d197c661f034 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/LoggerFulltrace.java @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2010-2019 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.velux.internal.utils; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.slf4j.Logger; + +/** + * This is an extension of org.slf4j.Logger interface to provide an intensive logging + * for framework debugging. + *

Typical usage pattern:

+ * + *
+ * public class Wombat {
+ *
+ *   private final Logger logger = LoggerFactory.getLogger(Demo.class);
+ *   
+ *   final static LoggerFulltrace log = new LoggerFulltrace(logger,false);
+ *   Integer t;
+ *   Integer oldT;
+ *
+ *   public void setTemperature(Integer temperature) {
+ *     oldT = t;
+ *     t = temperature;
+ *     log.fulltrace("Temperature set to {}. Old temperature was {}.", t, oldT);
+ *     if(temperature.intValue() > 50) {
+ *       log.fulltrace("Temperature has risen above 50 degrees.");
+ *     }
+ *   }
+ * }
+ * 
+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class LoggerFulltrace { + private boolean enabled; + private Logger logger; + + /* Constructor */ + public LoggerFulltrace(Logger logger, boolean enabled) { + this.logger = logger; + this.enabled = enabled; + } + + /** + * Log a message at the TRACE level. + * + * @param msg the message string to be logged + */ + public void fulltrace(String msg) { + if (enabled) { + logger.trace("{}", msg); + } + }; + + /** + * Log a message at the TRACE level according to the specified format + * and argument. + *

+ * This form avoids superfluous object creation when the logger + * is disabled for the TRACE level. + *

+ * + * @param format the format string + * @param arg the argument + */ + public void fulltrace(String format, Object arg) { + if (enabled) { + // [ERROR] Format should be constant. Use placeholder to reduce the needless cost of parameter + // construction. see http://www.slf4j.org/faq.html#logging_performance + // SOLVED by bad hack. WAIT for Jenkis check being fixed + // logger.trace(format, arg); + logger.trace("{}/{}", format, arg); + } + }; + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/ManifestInformation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/ManifestInformation.java new file mode 100644 index 0000000000000..a884c0c50b182 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/ManifestInformation.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2010-2019 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.velux.internal.utils; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This is a helper class for dealing with information from MANIFEST file. + * + * It provides the following methods: + *
    + *
  • {@link #getBundleVersion} returns the bundle version as specified within the MANIFEST.
  • + *
+ *

+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class ManifestInformation { + private static final Logger LOGGER = LoggerFactory.getLogger(ManifestInformation.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + /** + * Suppress default constructor for creating a non-instantiable class. + */ + private ManifestInformation() { + throw new AssertionError(); + } + + /** + * Returns the bundle version as specified within the MANIFEST file. + * + * @return bundleVersion the resulted bundle version as {@link String}. + */ + public static String getBundleVersion() { + String osgiBundleVersion = org.osgi.framework.FrameworkUtil.getBundle(ManifestInformation.class) + .getBundleContext().getBundle().toString(); + LOGGER.trace("getBundleVersion() has found {}.", osgiBundleVersion); + return osgiBundleVersion; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/package-info.java new file mode 100644 index 0000000000000..eac5caa511d87 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/package-info.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2010-2019 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 + */ +/** + * Utility classes. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.internal.utils; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/package-info.java new file mode 100644 index 0000000000000..2a298b9284910 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/package-info.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2010-2019 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 + */ +/** + * Classes for common definitions for the binding, i.e. constants like String patterns. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxExistingProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxExistingProducts.java new file mode 100644 index 0000000000000..e4cc4f71db87c --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxExistingProducts.java @@ -0,0 +1,205 @@ +/** + * Copyright (c) 2010-2019 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.velux.things; + +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.things.VeluxProduct.ProductBridgeIndex; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Combined set of product informations provided by the Velux bridge, + * which can be used for later interactions. + *

+ * The following class access methods exist: + *

    + *
  • {@link VeluxExistingProducts#isRegistered} for querying existence of a {@link VeluxProduct}, + *
  • {@link VeluxExistingProducts#register} for storing a {@link VeluxProduct}, + *
  • {@link VeluxExistingProducts#update} for updating/storing of a {@link VeluxProduct}, + *
  • {@link VeluxExistingProducts#get} for retrieval of a {@link VeluxProduct}, + *
  • {@link VeluxExistingProducts#values} for retrieval of all {@link VeluxProduct}s, + *
  • {@link VeluxExistingProducts#getNoMembers} for retrieval of the number of all {@link VeluxProduct}s, + *
  • {@link VeluxExistingProducts#toString} for a descriptive string representation. + *
+ * + * @see VeluxProduct + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxExistingProducts { + private final Logger logger = LoggerFactory.getLogger(VeluxExistingProducts.class); + + // Type definitions, class-internal variables + + private ConcurrentHashMap existingProductsByUniqueIndex; + private ConcurrentHashMap bridgeIndexToSerialNumber; + private ConcurrentHashMap modifiedProductsByUniqueIndex; + private int memberCount; + + /* + * Value to flag any changes towards the getter. + */ + private boolean dirty; + + // Constructor methods + + public VeluxExistingProducts() { + logger.trace("VeluxExistingProducts(constructor) called."); + existingProductsByUniqueIndex = new ConcurrentHashMap(); + bridgeIndexToSerialNumber = new ConcurrentHashMap(); + modifiedProductsByUniqueIndex = new ConcurrentHashMap(); + memberCount = 0; + dirty = true; + logger.trace("VeluxExistingProducts(constructor) done."); + } + + // Class access methods + + public boolean isRegistered(String productUniqueIndexOrSerialNumber) { + logger.trace("isRegistered(String {}) returns {}.", productUniqueIndexOrSerialNumber, + existingProductsByUniqueIndex.containsKey(productUniqueIndexOrSerialNumber) ? "true" : "false"); + return existingProductsByUniqueIndex.containsKey(productUniqueIndexOrSerialNumber); + } + + public boolean isRegistered(VeluxProduct product) { + logger.trace("isRegistered(VeluxProduct {}) called.", product.toString()); + if (product.isV2()) { + return isRegistered(product.getSerialNumber()); + } + return isRegistered(product.getProductUniqueIndex()); + } + + public boolean isRegistered(ProductBridgeIndex bridgeProductIndex) { // NO_UCD (use private) + logger.trace("isRegisteredProductBridgeIndex {}) called.", bridgeProductIndex.toString()); + if (!bridgeIndexToSerialNumber.containsKey(bridgeProductIndex.toInt())) { + return false; + } + return isRegistered(bridgeIndexToSerialNumber.get(bridgeProductIndex.toInt())); + } + + public boolean register(VeluxProduct newProduct) { + logger.trace("register({}) called.", newProduct); + if (isRegistered(newProduct)) { + return false; + } + logger.trace("register() registering new product {}.", newProduct); + + String uniqueIndex = newProduct.isV2() ? newProduct.getSerialNumber() : newProduct.getProductUniqueIndex(); + logger.trace("register() registering by UniqueIndex {}", uniqueIndex); + existingProductsByUniqueIndex.put(uniqueIndex, newProduct); + + logger.trace("register() registering by ProductBridgeIndex {}", newProduct.getBridgeProductIndex().toInt()); + bridgeIndexToSerialNumber.put(newProduct.getBridgeProductIndex().toInt(), newProduct.getSerialNumber()); + + logger.trace("register() registering set of modifications by UniqueIndex {}", uniqueIndex); + modifiedProductsByUniqueIndex.put(uniqueIndex, newProduct); + + memberCount++; + dirty = true; + return true; + } + + public boolean update(ProductBridgeIndex bridgeProductIndex, int productState, int productPosition, + int productTarget) { + logger.debug("update(bridgeProductIndex={},productState={},productPosition={},productTarget={}) called.", + bridgeProductIndex.toInt(), productState, productPosition, productTarget); + if (!isRegistered(bridgeProductIndex)) { + logger.warn("update() failed as actuator (with index {}) is not registered.", bridgeProductIndex.toInt()); + return false; + } + VeluxProduct thisProduct = this.get(bridgeProductIndex); + if (thisProduct.setState(productState) || thisProduct.setCurrentPosition(productPosition) + || thisProduct.setTarget(productTarget)) { + dirty = true; + + String uniqueIndex = thisProduct.isV2() ? thisProduct.getSerialNumber() + : thisProduct.getProductUniqueIndex(); + logger.trace("update(): updating by UniqueIndex {}.", uniqueIndex); + existingProductsByUniqueIndex.replace(uniqueIndex, thisProduct); + modifiedProductsByUniqueIndex.put(uniqueIndex, thisProduct); + } + logger.trace("update() successfully finished (dirty={}).", dirty); + return true; + } + + public boolean update(VeluxProduct currentProduct) { + logger.trace("update(currentProduct={}) called.", currentProduct); + return update(currentProduct.getBridgeProductIndex(), currentProduct.getState(), + currentProduct.getCurrentPosition(), currentProduct.getTarget()); + } + + public VeluxProduct get(String productUniqueIndexOrSerialNumber) { + logger.trace("get({}) called.", productUniqueIndexOrSerialNumber); + if (!isRegistered(productUniqueIndexOrSerialNumber)) { + return VeluxProduct.UNKNOWN; + } + return existingProductsByUniqueIndex.get(productUniqueIndexOrSerialNumber); + } + + public VeluxProduct get(ProductBridgeIndex bridgeProductIndex) { // NO_UCD (use private) + logger.trace("get({}) called.", bridgeProductIndex); + if (!isRegistered(bridgeProductIndex)) { + return VeluxProduct.UNKNOWN; + } + return existingProductsByUniqueIndex.get(bridgeIndexToSerialNumber.get(bridgeProductIndex.toInt())); + } + + public VeluxProduct[] values() { + return existingProductsByUniqueIndex.values().toArray(new VeluxProduct[0]); + } + + public VeluxProduct[] valuesOfModified() { + return modifiedProductsByUniqueIndex.values().toArray(new VeluxProduct[0]); + } + + public int getNoMembers() { + logger.trace("getNoMembers() returns {}.", memberCount); + return memberCount; + } + + public boolean isDirty() { + logger.trace("isDirty() returns {}.", dirty); + return dirty; + } + + public void resetDirtyFlag() { + logger.trace("resetDirtyFlag() called."); + modifiedProductsByUniqueIndex = new ConcurrentHashMap(); + dirty = false; + } + + public String toString(boolean showSummary, String delimiter) { + StringBuilder sb = new StringBuilder(); + + if (showSummary) { + sb.append(memberCount).append(" members: "); + } + for (VeluxProduct product : this.values()) { + sb.append(product).append(delimiter); + } + if (sb.lastIndexOf(delimiter) > 0) { + sb.deleteCharAt(sb.lastIndexOf(delimiter)); + } + return sb.toString(); + } + + @Override + public String toString() { + return toString(true, VeluxBindingConstants.OUTPUT_VALUE_SEPARATOR); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxExistingScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxExistingScenes.java new file mode 100644 index 0000000000000..7960a00a99ccb --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxExistingScenes.java @@ -0,0 +1,118 @@ +/** + * Copyright (c) 2010-2019 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.velux.things; + +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.things.VeluxScene.SceneName; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Combined set of scene informations provided by the Velux bridge, + * which can be used for later interactions. + *

+ * The following class access methods exist: + *

    + *
  • {@link VeluxExistingScenes#isRegistered} for querying existence of a {@link VeluxScene}, + *
  • {@link VeluxExistingScenes#register} for storing a {@link VeluxScene}, + *
  • {@link VeluxExistingScenes#get} for retrieval of a {@link VeluxScene}, + *
  • {@link VeluxExistingScenes#values} for retrieval of all {@link VeluxScene}s, + *
  • {@link VeluxExistingScenes#getNoMembers} for retrieval of the number of all {@link VeluxScene}s, + *
  • {@link VeluxExistingScenes#toString} for a descriptive string representation. + *
+ * + * @see VeluxScene + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxExistingScenes { + private final Logger logger = LoggerFactory.getLogger(VeluxExistingScenes.class); + + // Type definitions, class-internal variables + + private ConcurrentHashMap existingScenesBySceneName; + private int memberCount; + + // Constructor methods + + public VeluxExistingScenes() { + logger.trace("VeluxExistingScenes(constructor) called."); + existingScenesBySceneName = new ConcurrentHashMap(); + memberCount = 0; + logger.trace("VeluxExistingScenes(constructor) done."); + } + + // Class access methods + + public boolean isRegistered(SceneName sceneName) { + logger.trace("isRegistered({}) returns {}.", sceneName, + existingScenesBySceneName.containsKey(sceneName.toString()) ? "true" : "false"); + return existingScenesBySceneName.containsKey(sceneName.toString()); + } + + public boolean isRegistered(VeluxScene scene) { + return isRegistered(scene.getName()); + } + + public boolean register(VeluxScene newScene) { + logger.trace("register({}) called.", newScene); + if (isRegistered(newScene)) { + return false; + } + logger.trace("register() registering new scene {}.", newScene); + existingScenesBySceneName.put(newScene.getName().toString(), newScene); + memberCount++; + return true; + } + + public VeluxScene get(SceneName sceneName) { + logger.trace("get({}) called.", sceneName); + if (!isRegistered(sceneName)) { + return VeluxScene.UNKNOWN; + } + return existingScenesBySceneName.get(sceneName.toString()); + } + + public VeluxScene[] values() { + return existingScenesBySceneName.values().toArray(new VeluxScene[0]); + } + + public int getNoMembers() { + logger.trace("getNoMembers() returns {}.", memberCount); + return memberCount; + } + + public String toString(boolean showSummary, String delimiter) { + StringBuilder sb = new StringBuilder(); + + if (showSummary) { + sb.append(memberCount).append(" members: "); + } + for (VeluxScene scene : this.values()) { + sb.append(scene.toString()).append(delimiter); + } + if (sb.lastIndexOf(delimiter) > 0) { + sb.deleteCharAt(sb.lastIndexOf(delimiter)); + } + return sb.toString(); + } + + @Override + public String toString() { + return toString(true, VeluxBindingConstants.OUTPUT_VALUE_SEPARATOR); + } +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwFirmware.java new file mode 100644 index 0000000000000..0d0727f6b1881 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwFirmware.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2010-2019 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.velux.things; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Velux product representation. + *

+ * Combined set of information describing a single Velux product. + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxGwFirmware { + private final Logger logger = LoggerFactory.getLogger(VeluxGwFirmware.class); + + // Class internal + + private String firmwareVersion = VeluxBindingConstants.UNKNOWN; + + // Constructor + + public VeluxGwFirmware(String firmwareVersion) { + logger.trace("VeluxGwFirmware() created."); + + this.firmwareVersion = firmwareVersion; + } + + public VeluxGwFirmware() { + logger.trace("VeluxGwFirmware(dummy) created."); + } + + // Class access methods + + public String getfirmwareVersion() { + return this.firmwareVersion; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwLAN.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwLAN.java new file mode 100644 index 0000000000000..b28124ec273da --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwLAN.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2010-2019 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.velux.things; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Velux product representation. + *

+ * Combined set of information describing a single Velux product. + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxGwLAN { + private final Logger logger = LoggerFactory.getLogger(VeluxGwLAN.class); + + // Class internal + + private String ipAddress = VeluxBindingConstants.UNKNOWN; + private String subnetMask = VeluxBindingConstants.UNKNOWN; + private String defaultGW = VeluxBindingConstants.UNKNOWN; + private boolean enabledDHCP = false; + + // Constructor + + public VeluxGwLAN(String ipAddress, String subnetMask, String defaultGW, boolean enabledDHCP) { + logger.trace("VeluxGwLAN() created."); + + this.ipAddress = ipAddress; + this.subnetMask = subnetMask; + this.defaultGW = defaultGW; + this.enabledDHCP = enabledDHCP; + } + + // Class access methods + + public String getIpAddress() { + logger.trace("getIpAddress() returns {}.", this.ipAddress); + return this.ipAddress; + } + + public String getSubnetMask() { + logger.trace("getSubnetMask() returns {}.", this.subnetMask); + return this.subnetMask; + } + + public String getDefaultGW() { + logger.trace("getDefaultGW() returns {}.", this.defaultGW); + return this.defaultGW; + } + + public boolean getDHCP() { + logger.trace("getDHCP() returns {}.", this.enabledDHCP ? "enabled" : "disabled"); + return this.enabledDHCP; + } + + @Override + public String toString() { + return String.format("ip %s, nm %s, gw %s, DHCP %s", this.ipAddress, this.subnetMask, this.defaultGW, + this.enabledDHCP ? "enabled" : "disabled"); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwState.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwState.java new file mode 100644 index 0000000000000..823733660aba4 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwState.java @@ -0,0 +1,175 @@ +/** + * Copyright (c) 2010-2019 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.velux.things; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Velux product characteristics: GatewayState Description. + *

+ * Methods in handle this type of information: + * * + *

    + *
  • {@link #VeluxGwState(byte, byte)} to convert a value into the complete characteristic.
  • + *
  • {@link #toString()} to retrieve the human-readable description of the complete characteristic.
  • + *
  • {@link #toDescription()} to retrieve the verbose human-readable description of the complete characteristic..
  • + *
  • {@link #getSubState()} to retrieve a Velux value as part of the complete characteristic.
  • + *
+ * In addition, the subtype are accessible via: + *
    + *
  • {@link VeluxGatewayState#getStateValue()} to retrieve the Velux value of the characteristic.
  • + *
  • {@link VeluxGatewayState#getStateDescription()} to retrieve the human-readable description of the + * characteristic.
  • + *
  • {@link VeluxGatewayState#get(int)} to convert a value into the characteristic.
  • + *
  • {@link VeluxGatewaySubState#getStateValue()} to retrieve the Velux value of the characteristic.
  • + *
  • {@link VeluxGatewaySubState#getStateDescription()} to retrieve the human-readable description of the + * characteristic.
  • + *
  • {@link VeluxGatewaySubState#get(int)} to convert a value into the characteristic.
  • + *
+ * + * @see VeluxKLFAPI + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxGwState { + private final Logger logger = LoggerFactory.getLogger(VeluxGwState.class); + + // Type definition + + public enum VeluxGatewayState { + UNDEFTYPE(-1, "Unkwown state."), + GW_S_TEST(0, "Test mode."), + GW_S_GWM_EMPTY(1, "Gateway mode, no actuator nodes in the system table."), + GW_S_GWM(2, "Gateway mode, with one or more actuator nodes in the system table."), + GW_S_BM_UNCONFIG(3, "Beacon mode, not configured by a remote controller."), + GW_S_BM(4, "Beacon mode, has been configured by a remote controller."), + GW_STATE_RESERVED(255, "Reserved"); + + // Class internal + + private int stateValue; + private String stateDescription; + + // Reverse-lookup map for getting a VeluxProductVelocity from a value. + private static final Map LOOKUPTYPEID2ENUM = Stream.of(VeluxGatewayState.values()) + .collect(Collectors.toMap(VeluxGatewayState::getStateValue, Function.identity())); + + // Constructor + + private VeluxGatewayState(int stateValue, String stateDescription) { + this.stateValue = stateValue; + this.stateDescription = stateDescription; + } + + // Class access methods + + public int getStateValue() { + return stateValue; + } + + public String getStateDescription() { + return stateDescription; + } + + public static VeluxGatewayState get(int stateValue) { + return LOOKUPTYPEID2ENUM.getOrDefault(stateValue, VeluxGatewayState.UNDEFTYPE); + } + } + + public enum VeluxGatewaySubState { + UNDEFTYPE(-1, "Unknown state."), + GW_SS_IDLE(0, "Idle state."), + GW_SS_P1(1, "Performing task in Configuration Service handler."), + GW_SS_P2(2, "Performing Scene Configuration."), + GW_SS_P3(3, "Performing Information Service Configuration."), + GW_SS_P4(4, "Performing Contact input Configuration."), + GW_SS_PFF(88, "Reserved"); + + // Class internal + + private int stateValue; + private String stateDescription; + + // Reverse-lookup map for getting a VeluxGatewayState from an TypeId + private static final Map LOOKUPTYPEID2ENUM = Stream + .of(VeluxGatewaySubState.values()) + .collect(Collectors.toMap(VeluxGatewaySubState::getStateValue, Function.identity())); + + // Constructor + + private VeluxGatewaySubState(int stateValue, String stateDescription) { + this.stateValue = stateValue; + this.stateDescription = stateDescription; + } + + // Class access methods + + public int getStateValue() { + return stateValue; + } + + public String getStateDescription() { + return stateDescription; + } + + public static VeluxGatewaySubState get(int stateValue) { + if (LOOKUPTYPEID2ENUM.containsKey(stateValue)) { + return LOOKUPTYPEID2ENUM.get(stateValue); + } else { + return VeluxGatewaySubState.UNDEFTYPE; + } + } + } + + // Class internal + + private VeluxGatewayState gwState; + private VeluxGatewaySubState gwSubState; + + // Constructor + + public VeluxGwState(byte stateValue, byte subStateValue) { + logger.trace("VeluxGwState() created."); + + this.gwState = VeluxGatewayState.get(stateValue); + this.gwSubState = VeluxGatewaySubState.get(subStateValue); + } + + // Class access methods + + @Override + public String toString() { + return this.gwState.name().concat("/").concat(this.gwSubState.name()); + } + + public String toDescription() { + return this.gwState.getStateDescription().concat(", ").concat(this.gwSubState.getStateDescription()); + } + + public byte getSubState() { + return (byte) this.gwSubState.getStateValue(); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwWLAN.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwWLAN.java new file mode 100644 index 0000000000000..d379bb7583f71 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwWLAN.java @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2010-2019 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.velux.things; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Velux product representation. + *

+ * Combined set of information describing a single Velux product. + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxGwWLAN { + private final Logger logger = LoggerFactory.getLogger(VeluxGwWLAN.class); + + // Class internal + + private String serviceSetID = VeluxBindingConstants.UNKNOWN; + private String password = VeluxBindingConstants.UNKNOWN; + + // Constructor + + public VeluxGwWLAN(String serviceSetID, String password) { + logger.trace("VeluxGwWLAN() created."); + + this.serviceSetID = serviceSetID; + this.password = password; + } + + public VeluxGwWLAN() { + logger.trace("VeluxGwWLAN(dummy) created."); + } + + // Class access methods + + public String getSSID() { + return this.serviceSetID; + } + + public String getPassword() { + return this.password; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxKLFAPI.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxKLFAPI.java new file mode 100644 index 0000000000000..e1ddc7278dec0 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxKLFAPI.java @@ -0,0 +1,356 @@ +/** + * Copyright (c) 2010-2019 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.velux.things; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * + * The {@link VeluxKLFAPI} class defines common KLF200 API constants, which are + * used across the whole binding. + *

+ * It provides the Enumeration of available API message identifiers as well as + * constants which describe the KLF200 API restrictions. + *

+ * Classes/Enumeration available: + *

    + *
  • Enumeration {@link Command} provides command name, coding and description.
  • + *
  • Class {@link CommandName} to handle symbolic API names.
  • + *
  • Class {@link CommandNumber} to handle API code.
  • + *
+ * Constants available: + *
    + *
  • {@link #KLF_SYSTEMTABLE_MAX} provides limits of the System table.
  • + *
+ *

+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public class VeluxKLFAPI { + + // Constants + + /** + * System table index parameter - an be a number from 0 to 203. + * + * See KLF200 + * System table + */ + public static final int KLF_SYSTEMTABLE_MAX = 203; + + // Type definitions + + /** + * Handle symbolic names of the {@link VeluxKLFAPI}. + *

+ * Methods available: + *

    + *
  • Constructor {@link CommandName} by String.
  • + *
  • Method {@link toString} to return a String.
  • + *
+ */ + public static class CommandName { + private String name; + + CommandName(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + + } + + /** + * Handle API codings of the {@link VeluxKLFAPI}. + *

+ * Methods available: + *

    + *
  • CommandNumber {@link CommandName} by short.
  • + *
  • Method {@link toShort} to return a short.
  • + *
  • Method {@link toString} to return a well-formatted String.
  • + *
+ */ + public static class CommandNumber { + private short commandNumber; + + public CommandNumber(short commandNumber) { + this.commandNumber = commandNumber; + } + + public short toShort() { + return commandNumber; + } + + @Override + public String toString() { + return "0x" + Integer.toHexString(new Short(commandNumber).intValue()); + } + } + + /** + * Enumeration of complete API as definition of a + * List of Gateway commands. + *

+ * See Appendix + * 3: List of Gateway commands + *

+ * Methods available: + *

    + *
  • Constructor {@link Command} by String.
  • + *
  • Method {@link getCommand} to return a {@link CommandNumber}.
  • + *
  • Method {@link getDescription} to return a description as String.
  • + *
  • Method {@link get} to return a {@link Command} based on the given int.
  • + *
+ */ + public enum Command { + // Special item: unrecognized command + UNDEFTYPE((short) -1, "Unknown command."), + // Special item: Shutdown of the connection + GW_OPENHAB_CLOSE((short) -2, "openHAB connection shutdown command."), + // Special item: Shutdown of the connection + GW_OPENHAB_RECEIVEONLY((short) -3, "openHAB receive command."), + // Velux specific commands + GW_ERROR_NTF((short) 0x0000, "Provides information on what triggered the error."), + GW_REBOOT_REQ((short) 0x0001, "Request gateway to reboot."), + GW_REBOOT_CFM((short) 0x0002, "Acknowledge to GW_REBOOT_REQ command."), + GW_SET_FACTORY_DEFAULT_REQ((short) 0x0003, + "Request gateway to clear system table, scene table and set Ethernet settings to factory default. Gateway will reboot."), + GW_SET_FACTORY_DEFAULT_CFM((short) 0x0004, "Acknowledge to GW_SET_FACTORY_DEFAULT_REQ command."), + GW_GET_VERSION_REQ((short) 0x0008, "Request version information."), + GW_GET_VERSION_CFM((short) 0x0009, "Acknowledge to GW_GET_VERSION_REQ command."), + GW_GET_PROTOCOL_VERSION_REQ((short) 0x000A, "Request KLF 200 API protocol version."), + GW_GET_PROTOCOL_VERSION_CFM((short) 0x000B, "Acknowledge to GW_GET_PROTOCOL_VERSION_REQ command."), + GW_GET_STATE_REQ((short) 0x000C, "Request the state of the gateway"), + GW_GET_STATE_CFM((short) 0x000D, "Acknowledge to GW_GET_STATE_REQ command."), + + GW_LEAVE_LEARN_STATE_REQ((short) 0x000E, "Request gateway to leave learn state."), + GW_LEAVE_LEARN_STATE_CFM((short) 0x000F, "Acknowledge to GW_LEAVE_LEARN_STATE_REQ command."), + GW_GET_NETWORK_SETUP_REQ((short) 0x00E0, "Request network parameters."), + GW_GET_NETWORK_SETUP_CFM((short) 0x00E1, "Acknowledge to GW_GET_NETWORK_SETUP_REQ."), + GW_SET_NETWORK_SETUP_REQ((short) 0x00E2, "Set network parameters."), + GW_SET_NETWORK_SETUP_CFM((short) 0x00E3, "Acknowledge to GW_SET_NETWORK_SETUP_REQ."), + + GW_CS_GET_SYSTEMTABLE_DATA_REQ((short) 0x0100, "Request a list of nodes in the gateways system table."), + GW_CS_GET_SYSTEMTABLE_DATA_CFM((short) 0x0101, "Acknowledge to GW_CS_GET_SYSTEMTABLE_DATA_REQ"), + GW_CS_GET_SYSTEMTABLE_DATA_NTF((short) 0x0102, + "Acknowledge to GW_CS_GET_SYSTEM_TABLE_DATA_REQList of nodes in the gateways systemtable."), + GW_CS_DISCOVER_NODES_REQ((short) 0x0103, "Start CS DiscoverNodes macro in KLF200."), + GW_CS_DISCOVER_NODES_CFM((short) 0x0104, "Acknowledge to GW_CS_DISCOVER_NODES_REQ command."), + GW_CS_DISCOVER_NODES_NTF((short) 0x0105, "Acknowledge to GW_CS_DISCOVER_NODES_REQ command."), + GW_CS_REMOVE_NODES_REQ((short) 0x0106, "Remove one or more nodes in the systemtable."), + GW_CS_REMOVE_NODES_CFM((short) 0x0107, "Acknowledge to GW_CS_REMOVE_NODES_REQ."), + GW_CS_VIRGIN_STATE_REQ((short) 0x0108, "Clear systemtable and delete system key."), + GW_CS_VIRGIN_STATE_CFM((short) 0x0109, "Acknowledge to GW_CS_VIRGIN_STATE_REQ."), + GW_CS_CONTROLLER_COPY_REQ((short) 0x010A, + "Setup KLF200 to get or give a system to or from another io-homecontrol® remote control. By a system means all nodes in the systemtable and the system key."), + GW_CS_CONTROLLER_COPY_CFM((short) 0x010B, "Acknowledge to GW_CS_CONTROLLER_COPY_REQ."), + GW_CS_CONTROLLER_COPY_NTF((short) 0x010C, "Acknowledge to GW_CS_CONTROLLER_COPY_REQ."), + GW_CS_CONTROLLER_COPY_CANCEL_NTF((short) 0x010D, "Cancellation of system copy to other controllers."), + GW_CS_RECEIVE_KEY_REQ((short) 0x010E, "Receive system key from another controller."), + GW_CS_RECEIVE_KEY_CFM((short) 0x010F, "Acknowledge to GW_CS_RECEIVE_KEY_REQ."), + GW_CS_RECEIVE_KEY_NTF((short) 0x0110, "Acknowledge to GW_CS_RECEIVE_KEY_REQ with status."), + GW_CS_PGC_JOB_NTF((short) 0x0111, + "Information on Product Generic Configuration job initiated by press on PGC button."), + GW_CS_SYSTEM_TABLE_UPDATE_NTF((short) 0x0112, + "Broadcasted to all clients and gives information about added and removed actuator nodes in system table."), + GW_CS_GENERATE_NEW_KEY_REQ((short) 0x0113, "Generate new system key and update actuators in systemtable."), + GW_CS_GENERATE_NEW_KEY_CFM((short) 0x0114, "Acknowledge to GW_CS_GENERATE_NEW_KEY_REQ."), + GW_CS_GENERATE_NEW_KEY_NTF((short) 0x0115, "Acknowledge to GW_CS_GENERATE_NEW_KEY_REQ with status."), + GW_CS_REPAIR_KEY_REQ((short) 0x0116, "Update key in actuators holding an old key."), + GW_CS_REPAIR_KEY_CFM((short) 0x0117, "Acknowledge to GW_CS_REPAIR_KEY_REQ."), + GW_CS_REPAIR_KEY_NTF((short) 0x0118, "Acknowledge to GW_CS_REPAIR_KEY_REQ with status."), + GW_CS_ACTIVATE_CONFIGURATION_MODE_REQ((short) 0x0119, + "Request one or more actuator to open for configuration."), + GW_CS_ACTIVATE_CONFIGURATION_MODE_CFM((short) 0x011A, "Acknowledge to GW_CS_ACTIVATE_CONFIGURATION_MODE_REQ."), + + GW_GET_NODE_INFORMATION_REQ((short) 0x0200, "Request extended information of one specific actuator node."), + GW_GET_NODE_INFORMATION_CFM((short) 0x0201, "Acknowledge to GW_GET_NODE_INFORMATION_REQ."), + GW_GET_NODE_INFORMATION_NTF((short) 0x0210, "Acknowledge to GW_GET_NODE_INFORMATION_REQ."), + GW_GET_ALL_NODES_INFORMATION_REQ((short) 0x0202, "Request extended information of all nodes."), + GW_GET_ALL_NODES_INFORMATION_CFM((short) 0x0203, "Acknowledge to GW_GET_ALL_NODES_INFORMATION_REQ"), + GW_GET_ALL_NODES_INFORMATION_NTF((short) 0x0204, + "Acknowledge to GW_GET_ALL_NODES_INFORMATION_REQ. Holds node information"), + GW_GET_ALL_NODES_INFORMATION_FINISHED_NTF((short) 0x0205, + "Acknowledge to GW_GET_ALL_NODES_INFORMATION_REQ. No more nodes."), + GW_SET_NODE_VARIATION_REQ((short) 0x0206, "Set node variation."), + GW_SET_NODE_VARIATION_CFM((short) 0x0207, "Acknowledge to GW_SET_NODE_VARIATION_REQ."), + GW_SET_NODE_NAME_REQ((short) 0x0208, "Set node name."), + GW_SET_NODE_NAME_CFM((short) 0x0209, "Acknowledge to GW_SET_NODE_NAME_REQ."), + GW_SET_NODE_VELOCITY_REQ((short) 0x020A, "Set node velocity."), + GW_SET_NODE_VELOCITY_CFM((short) 0x020B, "Acknowledge to GW_SET_NODE_VELOCITY_REQ."), + GW_NODE_INFORMATION_CHANGED_NTF((short) 0x020C, "Information has been updated."), + GW_NODE_STATE_POSITION_CHANGED_NTF((short) 0x0211, "Information has been updated."), + GW_SET_NODE_ORDER_AND_PLACEMENT_REQ((short) 0x020D, "Set search order and room placement."), + GW_SET_NODE_ORDER_AND_PLACEMENT_CFM((short) 0x020E, "Acknowledge to GW_SET_NODE_ORDER_AND_PLACEMENT_REQ."), + + GW_GET_GROUP_INFORMATION_REQ((short) 0x0220, "Request information about all defined groups."), + GW_GET_GROUP_INFORMATION_CFM((short) 0x0221, "Acknowledge to GW_GET_GROUP_INFORMATION_REQ."), + GW_GET_GROUP_INFORMATION_NTF((short) 0x0230, "Acknowledge to GW_GET_NODE_INFORMATION_REQ."), + GW_SET_GROUP_INFORMATION_REQ((short) 0x0222, "Change an existing group."), + GW_SET_GROUP_INFORMATION_CFM((short) 0x0223, "Acknowledge to GW_SET_GROUP_INFORMATION_REQ."), + GW_GROUP_INFORMATION_CHANGED_NTF((short) 0x0224, + "Broadcast to all, about group information of a group has been changed."), + GW_DELETE_GROUP_REQ((short) 0x0225, "Delete a group."), + GW_DELETE_GROUP_CFM((short) 0x0226, "Acknowledge to GW_DELETE_GROUP_INFORMATION_REQ."), + GW_NEW_GROUP_REQ((short) 0x0227, "Request new group to be created."), + GW_NEW_GROUP_CFM((short) 0x0228, ""), + GW_GET_ALL_GROUPS_INFORMATION_REQ((short) 0x0229, "Request information about all defined groups."), + GW_GET_ALL_GROUPS_INFORMATION_CFM((short) 0x022A, "Acknowledge to GW_GET_ALL_GROUPS_INFORMATION_REQ."), + GW_GET_ALL_GROUPS_INFORMATION_NTF((short) 0x022B, "Acknowledge to GW_GET_ALL_GROUPS_INFORMATION_REQ."), + GW_GET_ALL_GROUPS_INFORMATION_FINISHED_NTF((short) 0x022C, "Acknowledge to GW_GET_ALL_GROUPS_INFORMATION_REQ."), + GW_GROUP_DELETED_NTF((short) 0x022D, + "GW_GROUP_DELETED_NTF is broadcasted to all, when a group has been removed."), + GW_HOUSE_STATUS_MONITOR_ENABLE_REQ((short) 0x0240, "Enable house status monitor."), + GW_HOUSE_STATUS_MONITOR_ENABLE_CFM((short) 0x0241, "Acknowledge to GW_HOUSE_STATUS_MONITOR_ENABLE_REQ."), + GW_HOUSE_STATUS_MONITOR_DISABLE_REQ((short) 0x0242, "Disable house status monitor."), + GW_HOUSE_STATUS_MONITOR_DISABLE_CFM((short) 0x0243, "Acknowledge to GW_HOUSE_STATUS_MONITOR_DISABLE_REQ."), + + GW_COMMAND_SEND_REQ((short) 0x0300, "Send activating command direct to one or more io-homecontrol® nodes."), + GW_COMMAND_SEND_CFM((short) 0x0301, "Acknowledge to GW_COMMAND_SEND_REQ."), + GW_COMMAND_RUN_STATUS_NTF((short) 0x0302, "Gives run status for io-homecontrol® node."), + GW_COMMAND_REMAINING_TIME_NTF((short) 0x0303, + "Gives remaining time before io-homecontrol® node enter target position."), + GW_SESSION_FINISHED_NTF((short) 0x0304, + "Command send, Status request, Wink, Mode or Stop session is finished."), + GW_STATUS_REQUEST_REQ((short) 0x0305, "Get status request from one or more io-homecontrol® nodes."), + GW_STATUS_REQUEST_CFM((short) 0x0306, "Acknowledge to GW_STATUS_REQUEST_REQ."), + GW_STATUS_REQUEST_NTF((short) 0x0307, + "Acknowledge to GW_STATUS_REQUEST_REQ. Status request from one or more io-homecontrol® nodes."), + GW_WINK_SEND_REQ((short) 0x0308, "Request from one or more io-homecontrol® nodes to Wink."), + GW_WINK_SEND_CFM((short) 0x0309, "Acknowledge to GW_WINK_SEND_REQ"), + GW_WINK_SEND_NTF((short) 0x030A, "Status info for performed wink request."), + + GW_SET_LIMITATION_REQ((short) 0x0310, "Set a parameter limitation in an actuator."), + GW_SET_LIMITATION_CFM((short) 0x0311, "Acknowledge to GW_SET_LIMITATION_REQ."), + GW_GET_LIMITATION_STATUS_REQ((short) 0x0312, "Get parameter limitation in an actuator."), + GW_GET_LIMITATION_STATUS_CFM((short) 0x0313, "Acknowledge to GW_GET_LIMITATION_STATUS_REQ."), + GW_LIMITATION_STATUS_NTF((short) 0x0314, "Hold information about limitation."), + GW_MODE_SEND_REQ((short) 0x0320, "Send Activate Mode to one or more io-homecontrol® nodes."), + GW_MODE_SEND_CFM((short) 0x0321, "Acknowledge to GW_MODE_SEND_REQ"), + GW_MODE_SEND_NTF((short) 0x0322, "Notify with Mode activation info."), + + GW_INITIALIZE_SCENE_REQ((short) 0x0400, "Prepare gateway to record a scene."), + GW_INITIALIZE_SCENE_CFM((short) 0x0401, "Acknowledge to GW_INITIALIZE_SCENE_REQ."), + GW_INITIALIZE_SCENE_NTF((short) 0x0402, "Acknowledge to GW_INITIALIZE_SCENE_REQ."), + GW_INITIALIZE_SCENE_CANCEL_REQ((short) 0x0403, "Cancel record scene process."), + GW_INITIALIZE_SCENE_CANCEL_CFM((short) 0x0404, "Acknowledge to GW_INITIALIZE_SCENE_CANCEL_REQ command."), + GW_RECORD_SCENE_REQ((short) 0x0405, "Store actuator positions changes since GW_INITIALIZE_SCENE, as a scene."), + GW_RECORD_SCENE_CFM((short) 0x0406, "Acknowledge to GW_RECORD_SCENE_REQ."), + GW_RECORD_SCENE_NTF((short) 0x0407, "Acknowledge to GW_RECORD_SCENE_REQ."), + GW_DELETE_SCENE_REQ((short) 0x0408, "Delete a recorded scene."), + GW_DELETE_SCENE_CFM((short) 0x0409, "Acknowledge to GW_DELETE_SCENE_REQ."), + GW_RENAME_SCENE_REQ((short) 0x040A, "Request a scene to be renamed."), + GW_RENAME_SCENE_CFM((short) 0x040B, "Acknowledge to GW_RENAME_SCENE_REQ."), + GW_GET_SCENE_LIST_REQ((short) 0x040C, "Request a list of scenes."), + GW_GET_SCENE_LIST_CFM((short) 0x040D, "Acknowledge to GW_GET_SCENE_LIST."), + GW_GET_SCENE_LIST_NTF((short) 0x040E, "Acknowledge to GW_GET_SCENE_LIST."), + GW_GET_SCENE_INFOAMATION_REQ((short) 0x040F, "Request extended information for one given scene."), + GW_GET_SCENE_INFOAMATION_CFM((short) 0x0410, "Acknowledge to GW_GET_SCENE_INFOAMATION_REQ."), + GW_GET_SCENE_INFOAMATION_NTF((short) 0x0411, "Acknowledge to GW_GET_SCENE_INFOAMATION_REQ."), + GW_ACTIVATE_SCENE_REQ((short) 0x0412, "Request gateway to enter a scene."), + GW_ACTIVATE_SCENE_CFM((short) 0x0413, "Acknowledge to GW_ACTIVATE_SCENE_REQ."), + GW_STOP_SCENE_REQ((short) 0x0415, "Request all nodes in a given scene to stop at their current position."), + GW_STOP_SCENE_CFM((short) 0x0416, "Acknowledge to GW_STOP_SCENE_REQ."), + GW_SCENE_INFORMATION_CHANGED_NTF((short) 0x0419, "A scene has either been changed or removed."), + + GW_ACTIVATE_PRODUCTGROUP_REQ((short) 0x0447, "Activate a product group in a given direction."), + GW_ACTIVATE_PRODUCTGROUP_CFM((short) 0x0448, "Acknowledge to GW_ACTIVATE_PRODUCTGROUP_REQ."), + GW_ACTIVATE_PRODUCTGROUP_NTF((short) 0x0449, "Acknowledge to GW_ACTIVATE_PRODUCTGROUP_REQ."), + + GW_GET_CONTACT_INPUT_LINK_LIST_REQ((short) 0x0460, + "Get list of assignments to all Contact Input to scene or product group."), + GW_GET_CONTACT_INPUT_LINK_LIST_CFM((short) 0x0461, "Acknowledge to GW_GET_CONTACT_INPUT_LINK_LIST_REQ."), + GW_SET_CONTACT_INPUT_LINK_REQ((short) 0x0462, "Set a link from a Contact Input to a scene or product group."), + GW_SET_CONTACT_INPUT_LINK_CFM((short) 0x0463, "Acknowledge to GW_SET_CONTACT_INPUT_LINK_REQ."), + GW_REMOVE_CONTACT_INPUT_LINK_REQ((short) 0x0464, "Remove a link from a Contact Input to a scene."), + GW_REMOVE_CONTACT_INPUT_LINK_CFM((short) 0x0465, "Acknowledge to GW_REMOVE_CONTACT_INPUT_LINK_REQ."), + + GW_GET_ACTIVATION_LOG_HEADER_REQ((short) 0x0500, "Request header from activation log."), + GW_GET_ACTIVATION_LOG_HEADER_CFM((short) 0x0501, "Confirm header from activation log."), + GW_CLEAR_ACTIVATION_LOG_REQ((short) 0x0502, "Request clear all data in activation log."), + GW_CLEAR_ACTIVATION_LOG_CFM((short) 0x0503, "Confirm clear all data in activation log."), + GW_GET_ACTIVATION_LOG_LINE_REQ((short) 0x0504, "Request line from activation log."), + GW_GET_ACTIVATION_LOG_LINE_CFM((short) 0x0505, "Confirm line from activation log."), + GW_ACTIVATION_LOG_UPDATED_NTF((short) 0x0506, "Confirm line from activation log."), + GW_GET_MULTIPLE_ACTIVATION_LOG_LINES_REQ((short) 0x0507, "Request lines from activation log."), + GW_GET_MULTIPLE_ACTIVATION_LOG_LINES_NTF((short) 0x0508, "Error log data from activation log."), + GW_GET_MULTIPLE_ACTIVATION_LOG_LINES_CFM((short) 0x0509, "Confirm lines from activation log."), + + GW_SET_UTC_REQ((short) 0x2000, "Request to set UTC time."), + GW_SET_UTC_CFM((short) 0x2001, "Acknowledge to GW_SET_UTC_REQ."), + GW_RTC_SET_TIME_ZONE_REQ((short) 0x2002, "Set time zone and daylight savings rules."), + GW_RTC_SET_TIME_ZONE_CFM((short) 0x2003, "Acknowledge to GW_RTC_SET_TIME_ZONE_REQ."), + GW_GET_LOCAL_TIME_REQ((short) 0x2004, + "Request the local time based on current time zone and daylight savings rules."), + GW_GET_LOCAL_TIME_CFM((short) 0x2005, "Acknowledge to GW_RTC_SET_TIME_ZONE_REQ."), + GW_PASSWORD_ENTER_REQ((short) 0x3000, "Enter password to authenticate request"), + GW_PASSWORD_ENTER_CFM((short) 0x3001, "Acknowledge to GW_PASSWORD_ENTER_REQ"), + GW_PASSWORD_CHANGE_REQ((short) 0x3002, "Request password change."), + GW_PASSWORD_CHANGE_CFM((short) 0x3003, "Acknowledge to GW_PASSWORD_CHANGE_REQ."), + GW_PASSWORD_CHANGE_NTF((short) 0x3004, + "Acknowledge to GW_PASSWORD_CHANGE_REQ. Broadcasted to all connected clients."), + + ; + + // Class internal + + private CommandNumber command; + private String description; + + // Reverse-lookup map for getting a Command from an TypeId + private static final Map LOOKUPTYPEID2ENUM = Stream.of(Command.values()) + .collect(Collectors.toMap(Command::getShort, Function.identity())); + + // Constructor + + private Command(short typeId, String description) { + this.command = new CommandNumber(typeId); + this.description = description; + } + + // Class access methods + + public CommandNumber getCommand() { + return command; + } + + public short getShort() { + return command.toShort(); + } + + public String getDescription() { + return description; + } + + public static Command get(short thisTypeId) { + if (LOOKUPTYPEID2ENUM.containsKey(thisTypeId)) { + return LOOKUPTYPEID2ENUM.get(thisTypeId); + } else { + return Command.UNDEFTYPE; + } + } + + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProduct.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProduct.java new file mode 100644 index 0000000000000..262a169a1bd65 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProduct.java @@ -0,0 +1,332 @@ +/** + * Copyright (c) 2010-2019 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.velux.things; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Velux product representation. + *

+ * Combined set of information describing a single Velux product. + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxProduct { + private final Logger logger = LoggerFactory.getLogger(VeluxProduct.class); + + // Public definition + + public static final VeluxProduct UNKNOWN = new VeluxProduct(); + + // Type definitions + + public static class ProductBridgeIndex { + + // Public definition + private static final ProductBridgeIndex UNKNOWN = new ProductBridgeIndex(0); + + // Class internal + private int id; + + // Constructor + public ProductBridgeIndex(int id) { + this.id = id; + } + + // Class access methods + public int toInt() { + return id; + } + } + + // Class internal + + private VeluxProductName name; + private VeluxProductType typeId; + private ProductBridgeIndex bridgeProductIndex; + + private boolean v2 = false; + private int order = 0; + private int placement = 0; + private int velocity = 0; + private int variation = 0; + private int powerMode = 0; + private String serialNumber = VeluxProductSerialNo.UNKNOWN; + private int state = 0; + private int currentPosition = 0; + private int target = 0; + private int remainingTime = 0; + private int timeStamp = 0; + + // Constructor + + /** + * Constructor + * + * just for the dummy VeluxProduct. + */ + public VeluxProduct() { + logger.trace("VeluxProduct() created."); + this.name = VeluxProductName.UNKNOWN; + this.typeId = VeluxProductType.UNDEFTYPE; + this.bridgeProductIndex = ProductBridgeIndex.UNKNOWN; + } + + /** + * Constructor + * + * @param name This field Name holds the name of the actuator, ex. “Window 1”. This field is 64 bytes + * long, formatted as UTF-8 characters. + * @param typeId This field indicates the node type, ex. Window, Roller shutter, Light etc. + * @param bridgeProductIndex NodeID is an Actuator index in the system table, to get information from. It must be a + * value from 0 to 199. + */ + public VeluxProduct(VeluxProductName name, VeluxProductType typeId, ProductBridgeIndex bridgeProductIndex) { + logger.trace("VeluxProduct(v1,name={}) created.", name.toString()); + this.name = name; + this.typeId = typeId; + this.bridgeProductIndex = bridgeProductIndex; + } + + /** + * Constructor + * + * @param name This field Name holds the name of the actuator, ex. “Window 1”. This field is 64 bytes + * long, formatted as UTF-8 characters. + * @param typeId This field indicates the node type, ex. Window, Roller shutter, Light etc. + * @param bridgeProductIndex NodeID is an Actuator index in the system table, to get information from. It must be a + * value from 0 to 199. + * @param order Order can be used to store a sort order. The sort order is used in client end, when + * presenting a list of nodes for the user. + * @param placement Placement can be used to store a room group index or house group index number. + * @param velocity This field indicates what velocity the node is operation with. + * @param variation More detail information like top hung, kip, flat roof or sky light window. + * @param powerMode This field indicates the power mode of the node (ALWAYS_ALIVE/LOW_POWER_MODE). + * @param serialNumber This field tells the serial number of the node. This field is 8 bytes. + * @param state This field indicates the operating state of the node. + * @param currentPosition This field indicates the current position of the node. + * @param target This field indicates the target position of the current operation. + * @param remainingTime This field indicates the remaining time for a node activation in seconds. + * @param timeStamp UTC time stamp for last known position. + */ + public VeluxProduct(VeluxProductName name, VeluxProductType typeId, ProductBridgeIndex bridgeProductIndex, + int order, int placement, int velocity, int variation, int powerMode, String serialNumber, int state, + int currentPosition, int target, int remainingTime, int timeStamp) { + logger.trace("VeluxProduct(v2,name={}) created.", name.toString()); + this.name = name; + this.typeId = typeId; + this.bridgeProductIndex = bridgeProductIndex; + this.v2 = true; + this.order = order; + this.placement = placement; + this.velocity = velocity; + this.variation = variation; + this.powerMode = powerMode; + this.serialNumber = serialNumber; + this.state = state; + this.currentPosition = currentPosition; + this.target = target; + this.remainingTime = remainingTime; + this.timeStamp = timeStamp; + } + + // Utility methods + + @Override + public VeluxProduct clone() { + if (this.v2) { + return new VeluxProduct(this.name, this.typeId, this.bridgeProductIndex, this.order, this.placement, + this.velocity, this.variation, this.powerMode, this.serialNumber, this.state, this.currentPosition, + this.target, this.remainingTime, this.timeStamp); + } else { + return new VeluxProduct(this.name, this.typeId, this.bridgeProductIndex); + } + } + + // Class access methods + + /** + * Returns the name of the current product (aka actuator) for convenience as type-specific class. + * + * @return nameOfThisProduct as type {@link VeluxProductName}. + */ + public VeluxProductName getProductName() { + return this.name; + } + + /** + * Returns the type of the current product (aka actuator) for convenience as type-specific class. + * + * @return typeOfThisProduct as type {@link VeluxProductType}. + */ + public VeluxProductType getProductType() { + return this.typeId; + } + + public ProductBridgeIndex getBridgeProductIndex() { + return this.bridgeProductIndex; + } + + @Override + public String toString() { + if (this.v2) { + return String.format("Product \"%s\" / %s (bridgeIndex=%d,serial=%s,position=%04X)", this.name, this.typeId, + this.bridgeProductIndex.toInt(), this.serialNumber, this.currentPosition); + } else { + return String.format("Product \"%s\" / %s (bridgeIndex %d)", this.name, this.typeId, + this.bridgeProductIndex.toInt()); + } + } + + // Class helper methods + + public String getProductUniqueIndex() { + return this.name.toString().concat("#").concat(this.typeId.toString()); + } + + // Getter and Setter methods + + /** + * @return v2 as type boolean signals the availability of firmware version two (product) details. + */ + public boolean isV2() { + return v2; + } + + /** + * @return order as type int describes the user-oriented sort-order. + */ + public int getOrder() { + return order; + } + + /** + * @return placement as type int is used to describe a group index or house group index number. + */ + public int getPlacement() { + return placement; + } + + /** + * @return velocity as type int describes what velocity the node is operation with + */ + public int getVelocity() { + return velocity; + } + + /** + * @return variation as type int describes detail information like top hung, kip, flat roof or sky light + * window. + */ + public int getVariation() { + return variation; + } + + /** + * @return powerMode as type int is used to show the power mode of the node (ALWAYS_ALIVE/LOW_POWER_MODE). + */ + public int getPowerMode() { + return powerMode; + } + + /** + * @return serialNumber as type String is the serial number of 8 bytes length of the node. + */ + public String getSerialNumber() { + return serialNumber; + } + + /** + * @return state as type int is used to operating state of the node. + */ + public int getState() { + return state; + } + + /** + * @param newState Update the operating state of the node. + * @return modified as type boolean to signal a real modification. + */ + public boolean setState(int newState) { // NO_UCD (use default) + if (this.state == newState) { + return false; + } else { + logger.trace("setState(name={},index={}) state {} replaced by {}.", name.toString(), + bridgeProductIndex.toInt(), this.state, newState); + this.state = newState; + return true; + } + } + + /** + * @return currentPosition as type int signals the current position of the node. + */ + public int getCurrentPosition() { + return currentPosition; + } + + /** + * @param newCurrentPosition Update the current position of the node. + * @return modified as boolean to signal a real modification. + */ + public boolean setCurrentPosition(int newCurrentPosition) { // NO_UCD (use default) + if (this.currentPosition == newCurrentPosition) { + return false; + } else { + logger.trace("setCurrentPosition(name={},index={}) currentPosition {} replaced by {}.", name.toString(), + bridgeProductIndex.toInt(), this.currentPosition, newCurrentPosition); + this.currentPosition = newCurrentPosition; + return true; + } + } + + /** + * @return target as type int shows the target position of the current operation. + */ + public int getTarget() { + return target; + } + + /** + * @param newTarget Update the target position of the current operation. + * @return modified as boolean to signal a real modification. + */ + public boolean setTarget(int newTarget) { // NO_UCD (use default) + if (this.target == newTarget) { + return false; + } else { + logger.trace("setCurrentPosition(name={},index={}) target {} replaced by {}.", name.toString(), + bridgeProductIndex.toInt(), this.target, newTarget); + this.target = newTarget; + return true; + } + } + + /** + * @return remainingTime as type int describes the intended remaining time of current operation. + */ + public int getRemainingTime() { + return remainingTime; + } + + /** + * @return timeStamp as type int describes the current time. + */ + public int getTimeStamp() { + return timeStamp; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductName.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductName.java new file mode 100644 index 0000000000000..959622ab843fa --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductName.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2010-2019 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.velux.things; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.VeluxBindingConstants; + +/** + * Velux product representation. + *

+ * Combined set of information describing a single Velux product. + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxProductName { + + // Public definition + + public static final VeluxProductName UNKNOWN = new VeluxProductName(VeluxBindingConstants.UNKNOWN); // NO_UCD (use + // default) + + // Class internal + + private String name; + + // Constructor + + public VeluxProductName(String name) { + this.name = name; + } + + // Class access methods + + @Override + public String toString() { + return name; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductPosition.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductPosition.java new file mode 100644 index 0000000000000..30274874fb37e --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductPosition.java @@ -0,0 +1,165 @@ +/** + * Copyright (c) 2010-2019 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.velux.things; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Velux product characteristics: Product Position. + *

+ * See KLF200 + * Standard Parameter definition + *

+ * Methods in handle this type of information: + *

    + *
  • {@link #VeluxProductPosition(int)} to convert a Velux value into the characteristic.
  • + *
  • {@link #VeluxProductPosition(PercentType)} to convert an openHAB value into the characteristic.
  • + *
  • {@link #VeluxProductPosition()} to convert an openHAB STOP value into the characteristic.
  • + *
  • {@link #isValid} to determine whether the characteristic has got a valid value.
  • + *
  • {@link #getPositionAsPercentType()} to convert the characteristic into an openHAB value.
  • + *
  • {@link #getPositionAsVeluxType()} to convert the characteristic into a Velux value.
  • + *
  • {@link #toString} to retrieve a human-readable description of this characteristic.
  • + *
+ * + * @see VeluxKLFAPI + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxProductPosition { + private static final Logger LOGGER = LoggerFactory.getLogger(VeluxProductPosition.class); + private final Logger logger = LoggerFactory.getLogger(getClass()); + + // Public definition + + public static final VeluxProductPosition UNKNOWN = new VeluxProductPosition(); + public static final int VPP_VELUX_STOP = 0xD200; // NO_UCD (use private) + + // Make sure that the calculation are done as non-integer + private static final float ONE = 1; + + private static final int VPP_UNKNOWN = 0; + + private static final int VPP_OPENHAB_MIN = 0; + private static final int VPP_OPENHAB_MAX = 100; + private static final int VPP_VELUX_MIN = 0x0000; + private static final int VPP_VELUX_MAX = 0xc800; + private static final int VPP_VELUX_UNKNOWN = 0xF7FF; + + private static final int VPP_VELUX_PERCENTAGE_MIN = 0xc900; + private static final int VPP_VELUX_PERCENTAGE_MAX = 0xd0d0; + + // Class internal + + private PercentType position; + private boolean isValid = false; + + // Constructor + + /** + * Creation of a Position object to specify a distinct actuator setting. + * + * @param position A position as type {@link PercentType} (between 0 and 100). + */ + public VeluxProductPosition(PercentType position) { + logger.trace("VeluxProductPosition({} as PercentType) created.", position.intValue()); + this.position = position; + this.isValid = true; + } + + /** + * Creation of a Position object to specify a distinct actuator setting. + * + * @param veluxPosition A position as type {@link int} based on the Velux-specific value ranges (between 0x0000 and + * 0xc800, or 0xD200 for stop). + */ + public VeluxProductPosition(int veluxPosition) { + logger.trace("VeluxProductPosition(constructur with {} as veluxPosition) called.", veluxPosition); + if ((veluxPosition == VPP_VELUX_UNKNOWN) || (veluxPosition == VPP_VELUX_STOP) || (veluxPosition < VPP_VELUX_MIN) + || (veluxPosition > VPP_VELUX_MAX)) { + logger.trace("VeluxProductPosition() gives up."); + this.position = new PercentType(VPP_UNKNOWN); + this.isValid = false; + } else { + float result = (ONE * veluxPosition - VPP_VELUX_MIN) / (VPP_VELUX_MAX - VPP_VELUX_MIN); + result = Math.round(VPP_OPENHAB_MIN + result * (VPP_OPENHAB_MAX - VPP_OPENHAB_MIN)); + logger.trace("VeluxProductPosition() created with percent-type {}.", (int) result); + this.position = new PercentType((int) result); + this.isValid = true; + } + } + + /** + * Creation of a Position object to specify a STOP. + */ + public VeluxProductPosition() { + logger.trace("VeluxProductPosition() as STOP position created."); + this.position = new PercentType(VPP_UNKNOWN); + this.isValid = false; + } + + // Class access methods + + public boolean isValid() { + return this.isValid; + } + + public PercentType getPositionAsPercentType() { + return position; + } + + public PercentType getPositionAsPercentType(boolean toBeInverted) { + return toBeInverted ? new PercentType(PercentType.HUNDRED.intValue() - position.intValue()) : position; + } + + public int getPositionAsVeluxType() { + if (this.isValid) { + float result = (ONE * position.intValue() - VPP_OPENHAB_MIN) / (VPP_OPENHAB_MAX - VPP_OPENHAB_MIN); + result = VPP_VELUX_MIN + result * (VPP_VELUX_MAX - VPP_VELUX_MIN); + return (int) result; + } else { + return VPP_VELUX_STOP; + } + } + + @Override + public String toString() { + if (this.isValid) { + return String.format("%d", position.intValue()); + } else { + return new String(VeluxBindingConstants.UNKNOWN); + } + } + + // Helper methods + + public static int getRelativePositionAsVeluxType(boolean upwards, PercentType position) { // NO_UCD (unused code) + LOGGER.trace("getRelativePositionAsVeluxType(upwards={},{}) created.", upwards, position); + float result = (VPP_VELUX_PERCENTAGE_MAX + VPP_VELUX_PERCENTAGE_MIN) / 2; + if (upwards) { + result = result + (ONE * position.intValue() - VPP_OPENHAB_MIN) / (VPP_OPENHAB_MAX - VPP_OPENHAB_MIN) + * ((VPP_VELUX_PERCENTAGE_MAX - VPP_VELUX_PERCENTAGE_MIN) / 2); + } else { + result = result - (ONE * position.intValue() - VPP_OPENHAB_MIN) / (VPP_OPENHAB_MAX - VPP_OPENHAB_MIN) + * ((VPP_VELUX_PERCENTAGE_MAX - VPP_VELUX_PERCENTAGE_MIN) / 2); + } + LOGGER.trace("getRelativePositionAsVeluxType() returns {}.", (int) result); + return (int) result; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductReference.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductReference.java new file mode 100644 index 0000000000000..08bddc4929d2d --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductReference.java @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2010-2019 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.velux.things; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Velux product characteristics: Product Reference. + *

+ * Combined set of information which describes a current state of a single Velux product. + *

+ * Methods in handle this type of information: + *
    + *
  • {@link #getProductName()} to retrieve the name representing this actuator/product.
  • + *
  • {@link #getProductType()} to retrieve the type of the product.
  • + *
  • {@link #toString} to retrieve a human-readable description of the product state.
  • + *
+ * + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxProductReference { + private final Logger logger = LoggerFactory.getLogger(VeluxProductReference.class); + + // Class internal + + private final VeluxProductName name; + private final VeluxProductType typeId; + + // Constructor + + /** + * Initializes the {@link VeluxProductReference} based on a given {@link VeluxProduct} and its associated type. + *

+ * + * @param name as {@link VeluxProductName} referencing to a specific actuator/product. + * @param type as int as handled by {@link VeluxProductType#get(int)}. + */ + public VeluxProductReference(VeluxProductName name, int type) { + this.name = name; + this.typeId = VeluxProductType.get(type); + if (this.typeId == VeluxProductType.UNDEFTYPE) { + logger.warn( + "Please report this to maintainer of the {} binding: VeluxProductReference({}) has found an unregistered ProductTypeId.", + VeluxBindingConstants.BINDING_ID, type); + } + } + + // Class access methods + + public VeluxProductName getProductName() { + return this.name; + } + + public VeluxProductType getProductType() { + return this.typeId; + } + + @Override + public String toString() { + return String.format("Prod.ref. \"%s\"/%s", this.name, this.typeId); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductSerialNo.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductSerialNo.java new file mode 100644 index 0000000000000..14d4acd1fb397 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductSerialNo.java @@ -0,0 +1,138 @@ +/** + * Copyright (c) 2010-2019 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.velux.things; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * This class support handling of Serial Number used with Velux. + *

    + *
  • {@link #UNKNOWN} defines an unknown serial number as constant,
  • + *
+ *
    + *
  • {@link #toString} converts the serial number as array of bytes into a human-readable String,
  • + *
  • {@link #isInvalid} evaluates whether the given serial number is valid,
  • + *
  • {@link #indicatesRevertedValues} returns a flag whether the serial number indicates inversion,
  • + *
  • {@link #cleaned} returns a plain serial number without any inverse indicators.
  • + *
+ *

+ * Example: + *

    + *
  • 12:23:34:45:56:67:78:89 represents a normal serial number,
  • + *
  • 12:23:34:45:56:67:78:89* represents a serial number which leads to an inverted value handling.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public final class VeluxProductSerialNo { + + /* + * *************************** + * ***** Private Objects ***** + */ + + private static final String HEXBYTE_SEPARATOR = ":"; + private static final char SUFFIX_MARKER = '*'; + + /* + * ************************** + * ***** Public Objects ***** + */ + + public static final String UNKNOWN = "00:00:00:00:00:00:00:00"; + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for creating a non-instantiable class. + + private VeluxProductSerialNo() { + throw new AssertionError(); + } + + /* + * *************************** + * ***** Utility Methods ***** + */ + + /** + * Returns the complete serial number as human-readable sequence of hex bytes each separated by the given separator. + * + * @param serialNumber as array of Type byte. + * @param separator as of Type String. + * @return serialNumberString of type String. + */ + public static String toString(byte[] serialNumber, String separator) { // NO_UCD (use private) + StringBuilder sb = new StringBuilder(); + for (byte b : serialNumber) { + sb.append(String.format("%02X", b)); + sb.append(separator); + } + if (sb.lastIndexOf(separator) > 0) { + sb.deleteCharAt(sb.lastIndexOf(separator)); + } + return (sb.toString()); + } + + /** + * Returns the complete serial number as human-readable sequence of hex bytes each separated by a colon. + * + * @param serialNumber as array of Type byte. + * @return serialNumberString of type String. + */ + public static String toString(byte[] serialNumber) { + return toString(serialNumber, HEXBYTE_SEPARATOR); + } + + /** + * Evaluates whether the given serial number is valid. + * + * @param serialNumber as array of type {@link byte}, + * @return invalid of type {@link boolean}. + */ + public static boolean isInvalid(byte[] serialNumber) { + if (serialNumber.length != 8) { + return true; + } + return ((serialNumber[0] == 0) && (serialNumber[1] == 0) && (serialNumber[2] == 0) && (serialNumber[3] == 0) + && (serialNumber[4] == 0) && (serialNumber[5] == 0) && (serialNumber[6] == 0) + && (serialNumber[7] == 0)); + } + + /** + * Evaluates a given serial number to determine whether any item value should be handled inverted. + * + * @param serialNumberString as type {@link String}, + * @return isInverted of type {@link boolean}. + */ + public static boolean indicatesRevertedValues(String serialNumberString) { + return (serialNumberString.length() == 0) ? false + : serialNumberString.charAt(serialNumberString.length() - 1) == SUFFIX_MARKER; + } + + /** + * Converts a given serial number into a plain serial number without any inversion markers being left. + * + * @param serialNumberString as type {@link String}, + * @return cleanedSerialNumberString of type {@link String}. + */ + public static String cleaned(String serialNumberString) { + return indicatesRevertedValues(serialNumberString) + ? serialNumberString.substring(0, serialNumberString.length() - 1) + : serialNumberString; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductState.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductState.java new file mode 100644 index 0000000000000..9833726a8b763 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductState.java @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2010-2019 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.velux.things; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Velux product characteristics: Product State. + *

+ * Combined set of information which describes a current state of a single Velux product. + *

+ * Methods in handle this type of information: + *
    + *
  • {@link #VeluxProductState(VeluxProductReference, int, int)} to create a new product state.
  • + *
  • {@link #getActuator()} to retrieve the number representing this actuator/product.
  • + *
  • {@link #getProductReference()} to retrieve reference to a product.
  • + *
  • {@link #getState()} to retrieve the current {@link VeluxProductState.ProductState product state}.
  • + *
  • {@link #getStateAsInt()} to retrieve the current product state.
  • + *
  • {@link #toString} to retrieve a human-readable description of the product state.
  • + *
+ * + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxProductState { + + // Type definitions + + private class ProductState { + + private int state; + + public int getState() { + return state; + } + + private ProductState(int state) { + this.state = state; + } + } + + // Class internal + + private VeluxProductReference productReference; + private int actuator; + private ProductState state; + + // Constructor + + public VeluxProductState(VeluxProductReference productReference, int actuator, int state) { + this.productReference = productReference; + this.actuator = actuator; + this.state = new ProductState(state); + } + + // Class access methods + + public VeluxProductReference getProductReference() { + return this.productReference; + } + + public int getActuator() { + return this.actuator; + } + + public ProductState getState() { + return this.state; + } + + public int getStateAsInt() { + return this.state.getState(); + } + + @Override + public String toString() { + return String.format("State (%s, actuator %d, value %d)", this.productReference, this.actuator, this.state); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductType.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductType.java new file mode 100644 index 0000000000000..522ced7e09a75 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductType.java @@ -0,0 +1,127 @@ +/** + * Copyright (c) 2010-2019 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.velux.things; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.VeluxBindingConstants; + +/** + * Velux product characteristics: Product Type. + *

+ * See KLF200 + * List of actuator types and their use of Main Parameter and Functional Parameters + *

+ * Methods in handle this type of information: + *

    + *
  • {@link #get(int)} to convert a value into the VeluxProductType.
  • + *
  • {@link #toString(int)} to convert a value into the description of the VeluxProductType.
  • + *
+ * + * @see VeluxKLFAPI + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public enum VeluxProductType { + SLIDER_SHUTTER, + SLIDER_WINDOW, + SWITCH, + UNDEFTYPE; + + private static enum ActuatorType { + UNDEFTYPE((short) 0xffff, VeluxBindingConstants.UNKNOWN, VeluxProductType.SWITCH), + BLIND_1_0((short) 0x0040, "Interior Venetian Blind", VeluxProductType.SLIDER_SHUTTER), + ROLLERSHUTTER_2_0((short) 0x0080, "Roller Shutter", VeluxProductType.SLIDER_SHUTTER), + ROLLERSHUTTER_2_1((short) 0x0081, "Roller Shutter", VeluxProductType.SLIDER_SHUTTER), + ROLLERSHUTTER_2_2((short) 0x0082, "Roller Shutter", VeluxProductType.SLIDER_SHUTTER), + AWNING_3_0((short) 0x00C0, "Vertical Exterior Awning", VeluxProductType.SLIDER_SHUTTER), + WINDOW_4_0((short) 0x0100, "Window opener", VeluxProductType.SLIDER_WINDOW), + WINDOW_4_1((short) 0x0101, "Window opener", VeluxProductType.SLIDER_WINDOW), + OPENER_5_0((short) 0x0140, "Garage door opener", VeluxProductType.SLIDER_SHUTTER), + OPENER_5_8((short) 0x017A, "Garage door opener", VeluxProductType.SLIDER_SHUTTER), + LIGHT_6_0((short) 0x0180, "Light", VeluxProductType.SLIDER_SHUTTER), + LIGHT_6_5((short) 0x01BA, "Light", VeluxProductType.SLIDER_SHUTTER), + OPENER_7_0((short) 0x01C0, "Gate opener", VeluxProductType.SLIDER_SHUTTER), + OPENER_7_5((short) 0x01FA, "Gate opener", VeluxProductType.SLIDER_SHUTTER), + LOCK_9_0((short) 0x0240, "Door lock", VeluxProductType.SLIDER_SHUTTER), + LOCK_9_1((short) 0x0241, "Window lock", VeluxProductType.SLIDER_SHUTTER), + BLIND_10((short) 0x0280, "Vertical Interior Blinds", VeluxProductType.SLIDER_SHUTTER), + SHUTTER_13((short) 0x0340, "Dual Roller Shutter", VeluxProductType.SLIDER_SHUTTER), + SWITCH_15((short) 0x03C0, "On/Off switch", VeluxProductType.SWITCH), + AWNING_16((short) 0x0400, "Horizontal awning", VeluxProductType.SLIDER_SHUTTER), + BLIND_17((short) 0x0440, "Exterior Venetian blind", VeluxProductType.SLIDER_SHUTTER), + BLIND_18((short) 0x0480, "Louver blind", VeluxProductType.SLIDER_SHUTTER), + TRACK_19((short) 0x04C0, "Curtain track", VeluxProductType.SLIDER_SHUTTER), + POINT_20((short) 0x0500, "Ventilation point", VeluxProductType.SLIDER_SHUTTER), + POINT_20_1((short) 0x0501, "Ventilation point", VeluxProductType.SLIDER_SHUTTER), + POINT_20_2((short) 0x0502, "Ventilation point", VeluxProductType.SLIDER_SHUTTER), + POINT_20_3((short) 0x0503, "Ventilation point", VeluxProductType.SLIDER_SHUTTER), + HEATING_21((short) 0x0540, "Exterior heating", VeluxProductType.SLIDER_SHUTTER), + HEATING_21_5((short) 0x57A, "Exterior heating", VeluxProductType.SLIDER_SHUTTER), + SHUTTER_24_0((short) 0x0600, "Swinging Shutters", VeluxProductType.SLIDER_SHUTTER), + SHUTTER_24_1((short) 0x0601, "Swinging Shutters", VeluxProductType.SLIDER_SHUTTER),; + + // Class internal + + private short nodeType; + private String description; + private VeluxProductType typeClass; + + // Reverse-lookup map for getting a ActuatorType from an TypeId + private static final Map LOOKUPTYPEID2ENUM = Stream.of(ActuatorType.values()) + .collect(Collectors.toMap(ActuatorType::getNodeType, Function.identity())); + + // Constructor + + private ActuatorType(short nodeType, String description, VeluxProductType typeClass) { + this.nodeType = nodeType; + this.description = description; + this.typeClass = typeClass; + } + + // Class access methods + + int getNodeType() { + return nodeType; + } + + String getDescription() { + return description; + } + + static ActuatorType get(int nodeType) { + return LOOKUPTYPEID2ENUM.getOrDefault(nodeType, ActuatorType.UNDEFTYPE); + } + } + + // Class access methods + + public static VeluxProductType get(int nodeType) { + if (ActuatorType.get(nodeType) != ActuatorType.UNDEFTYPE) { + return ActuatorType.get(nodeType).typeClass; + } else { + return VeluxProductType.UNDEFTYPE; + } + } + + public static String toString(int nodeType) { + return ActuatorType.get(nodeType).getDescription(); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductVelocity.java new file mode 100644 index 0000000000000..de47cf096381e --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductVelocity.java @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2010-2019 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.velux.things; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.VeluxBindingConstants; + +/** + * Velux product characteristics: Velocity. + *

+ * See KLF200 + * Velocity parameter + *

+ * Methods in handle this type of information: + *

    + *
  • {@link #getVelocity()} to retrieve the value of the characteristic.
  • + *
  • {@link #get(int)} to convert a value into the characteristic.
  • + *
  • {@link #getByName(String)} to convert a name into the characteristic.
  • + *
  • {@link #dump} to retrieve a human-readable description of all values.
  • + *
+ * + * @see VeluxKLFAPI + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public enum VeluxProductVelocity { + DEFAULT((short) 0, "default"), + SILENT((short) 1, "short"), + FAST((short) 2, "fast"), + VELOCITY_NOT_AVAILABLE((short) 255, ""), + UNDEFTYPE((short) 0xffff, VeluxBindingConstants.UNKNOWN); + + // Class internal + + private short velocity; + private String velocityName; + + // Reverse-lookup map for getting a VeluxProductVelocity from a value. + private static final Map LOOKUPTYPEID2ENUM = Stream.of(VeluxProductVelocity.values()) + .collect(Collectors.toMap(VeluxProductVelocity::getVelocity, Function.identity())); + + // Constructor + + private VeluxProductVelocity(short velocity, String velocityName) { + this.velocity = velocity; + this.velocityName = velocityName; + } + + // Class access methods + + public short getVelocity() { + return velocity; + } + + public static VeluxProductVelocity get(int velocity) { // NO_UCD (unused code) + return LOOKUPTYPEID2ENUM.getOrDefault(velocity, VeluxProductVelocity.UNDEFTYPE); + } + + public static VeluxProductVelocity getByName(String velocityName) { // NO_UCD (unused code) + for (VeluxProductVelocity enumItem : VeluxProductVelocity.values()) { + if (enumItem.velocityName.equals(velocityName)) { + return enumItem; + } + } + return VeluxProductVelocity.UNDEFTYPE; + } + + public static String dump() { // NO_UCD (unused code) + StringBuilder sb = new StringBuilder(); + for (VeluxProductVelocity typeId : VeluxProductVelocity.values()) { + sb.append(typeId).append(VeluxBindingConstants.OUTPUT_VALUE_SEPARATOR); + } + if (sb.lastIndexOf(VeluxBindingConstants.OUTPUT_VALUE_SEPARATOR) > 0) { + sb.deleteCharAt(sb.lastIndexOf(VeluxBindingConstants.OUTPUT_VALUE_SEPARATOR)); + } + return sb.toString(); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxScene.java new file mode 100644 index 0000000000000..dc1da61a1e499 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxScene.java @@ -0,0 +1,139 @@ +/** + * Copyright (c) 2010-2019 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.velux.things; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.VeluxBindingConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Velux scene representation. + *

+ * Combined set of information with references towards multiple Velux product states. + *

+ * Methods in handle this type of information: + *

    + *
  • {@link #VeluxScene(String, int, boolean, VeluxProductState[])} to create a new scene.
  • + *
  • {@link #VeluxScene(VeluxScene)} to duplicate a scene.
  • + *
  • {@link #getName} to retrieve the name of this scene.
  • + *
  • {@link #getBridgeSceneIndex()} to retrieve the index of this scene.
  • + *
  • {@link #toString()} to retrieve a human-readable description of this scene.
  • + *
+ * + * @see VeluxProductState + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxScene { + private final Logger logger = LoggerFactory.getLogger(VeluxScene.class); + + // Public definition + + public static final VeluxScene UNKNOWN = new VeluxScene(); + + // Type definitions + + public static class SceneName { + + private static final SceneName UNKNOWN = new SceneName(VeluxBindingConstants.UNKNOWN); + + private String name; + + @Override + public String toString() { + return name; + } + + public SceneName(String name) { + this.name = name; + } + + public boolean equals(SceneName anotherName) { + return this.name.equals(anotherName.toString()); + } + } + + public static class SceneBridgeIndex { // NO_UCD (use private) + + private static final SceneBridgeIndex UNKNOWN = new SceneBridgeIndex(0); + + private int id; + + @Override + public String toString() { + return String.valueOf(id); + } + + public int toInt() { + return id; + } + + private SceneBridgeIndex(int id) { + this.id = id; + } + } + + // Class internal + + private SceneName name; + private SceneBridgeIndex bridgeSceneIndex; + private boolean silent; + private VeluxProductState[] productStates; + + // Constructor + + /** + * Constructor + * + * just for the dummy VeluxProduct. + */ + private VeluxScene() { + logger.trace("VeluxScene() created."); + this.name = SceneName.UNKNOWN; + this.bridgeSceneIndex = SceneBridgeIndex.UNKNOWN; + this.silent = false; + this.productStates = new VeluxProductState[0]; + } + + public VeluxScene(String name, int sceneBridgeIndex, boolean silentOperation, VeluxProductState[] actions) { + this.name = new SceneName(name); + this.bridgeSceneIndex = new SceneBridgeIndex(sceneBridgeIndex); + this.silent = silentOperation; + this.productStates = actions; + } + + public VeluxScene(VeluxScene scene) { + this.name = new SceneName(scene.name.toString()); + this.bridgeSceneIndex = new SceneBridgeIndex(scene.bridgeSceneIndex.toInt()); + this.silent = scene.silent; + this.productStates = scene.productStates; + } + // Class access methods + + public SceneName getName() { + return this.name; + } + + public SceneBridgeIndex getBridgeSceneIndex() { + return this.bridgeSceneIndex; + } + + @Override + public String toString() { + return String.format("Scene \"%s\" (index %d) with %ssilent mode and %d actions", this.name, + this.bridgeSceneIndex.toInt(), this.silent ? "" : "non-", this.productStates.length); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/package-info.java new file mode 100644 index 0000000000000..1d552ca0c938c --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/package-info.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2010-2019 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 + */ +/** + * Classes for modeling the Velux devices and their characteristics in the openHAB environment. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.things; diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/binding/binding.xml new file mode 100644 index 0000000000000..259c5ac623306 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/binding/binding.xml @@ -0,0 +1,11 @@ + + + + @text/binding.velux.name + @text/binding.velux.description + + Guenther Schreiner + + diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/config/config.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/config/config.xml new file mode 100644 index 0000000000000..08290ca86b54e --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/config/config.xml @@ -0,0 +1,196 @@ + + + + + + + + + + @text/config.velux.bridge.ipAddress.description + true + + + + @text/config.velux.bridge.protocol.description + slip + true + + + + + + false + true + + + + @text/config.velux.bridge.tcpPort.description + 51200 + false + true + + + + @text/config.velux.bridge.password.description + true + password + + velux123 + + + + @text/config.velux.bridge.timeoutMsecs.description + false + 1000 + true + + + + @text/config.velux.bridge.retries.description + false + 5 + true + + + + @text/config.velux.bridge.refreshMsecs.description + false + 10000 + true + + + + @text/config.velux.bridge.isBulkRetrievalEnabled.description + false + true + true + + + + @text/config.velux.bridge.isSequentialEnforced.description + false + false + true + + + + @text/config.velux.bridge.isProtocolTraceEnabled.description + false + false + true + + + + + + + + + @text/config.velux.thing.scene.sceneName.description + true + false + + + + @text/config.velux.thing.scene.velocity.description + default + true + + + + + + false + true + + + + + + + @text/config.velux.thing.actuator.serial.description + true + false + + + + @text/config.velux.thing.actuator.name.description + false + true + + + + @text/config.velux.thing.actuator.inverted.description + true + false + false + + + + + + + @text/config.velux.thing.rollershutter.serial.description + true + false + + + + @text/config.velux.thing.rollershutter.name.description + false + true + + + + @text/config.velux.thing.rollershutter.inverted.description + true + false + false + + + + + + + @text/config.velux.thing.window.serial.description + true + false + + + + @text/config.velux.thing.window.name.description + false + true + + + + @text/config.velux.thing.window.inverted.description + true + false + false + + + + + + + @text/config.velux.thing.vshutter.sceneLevels.description + true + false + + + + @text/config.velux.thing.vshutter.currentLevel.description + true + 0 + true + + + + + diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux.properties b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux.properties new file mode 100644 index 0000000000000..ce0eb2b78817d --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux.properties @@ -0,0 +1,165 @@ +# +# Copyright (c) 2010-2019 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 +# +# @author Guenther Schreiner - Initial contribution. +# +# Binding descriptions +# +binding.velux.name = Velux Binding +binding.velux.description = A binding for the Velux KLF200 Bridge. The Velux Binding interacts via a Velux Bridge with the different Velux devices like controlling window openers, shutters and others. For example a KLF200 can act as interface between the HomeAutomation and the VELUX INTEGRA products with wireless connectivity based on the io-homecontrol standard. +# +# Bridge Thing types descriptions +# +bridge-type.velux.klf200.label = Velux KLF200 Bridge +bridge-type.velux.klf200.description = The Velux KLF200 represents a gateway to all Velux resp. io-homecontrol devices. +# +# Thing types descriptions +# +thing-type.velux.binding.label = Velux Binding Information +thing-type.velux.binding.description = Presents the status of the openHAB Velux binding. +# +thing-type.velux.actuator.label = Velux Actuator Information +thing-type.velux.actuator.description = Control an actuator via the Velux KLF 200. +# +thing-type.velux.rollershutter.label = Velux Rollershutter Information +thing-type.velux.rollershutter.description = Control a rollershutter on the Velux KLF 200. +# +thing-type.velux.scene.label = Velux Scene Information +thing-type.velux.scene.description = Control a scene on the Velux KLF 200. +# +thing-type.velux.vshutter.label = Virtual Shutter Information +thing-type.velux.vshutter.description = A Set of Scenes which act together as Shutter. +# +thing-type.velux.window.label = Velux Window Information +thing-type.velux.window.description = Control a window on the Velux KLF 200. +# +# Config descriptions +# +config.velux.bridge.ipAddress.label = IP Address +config.velux.bridge.ipAddress.description = The IP address of the Velux Bridge. +config.velux.bridge.protocol.label = Protocol +config.velux.bridge.protocol.description = The connection protocol to contact the Velux Bridge with (either slip or http or https). +config.velux.bridge.tcpPort.label = Port +config.velux.bridge.tcpPort.description = TCP port of the Velux gateway. +config.velux.bridge.password.label = Password +config.velux.bridge.password.description = Password for authentication against the Velux Bridge. +config.velux.bridge.timeoutMsecs.label = Timeout +config.velux.bridge.timeoutMsecs.description = Initial Connection timeout in milliseconds. +config.velux.bridge.retries.label = Connection Retries +config.velux.bridge.retries.description = Number of retries with timing along the Binary Exponential Backoff (BEB) Algorithm. +config.velux.bridge.refreshMsecs.label = Refresh Interval +config.velux.bridge.refreshMsecs.description = Refresh interval in milliseconds. +config.velux.bridge.isBulkRetrievalEnabled.label = Enable Bulk retrieval +config.velux.bridge.isBulkRetrievalEnabled.description = Fetch the complete actuator information in a bulk. +config.velux.bridge.isSequentialEnforced.label = Enforce Sequential Mode +config.velux.bridge.isSequentialEnforced.description = Enforce Sequential Actuator Control. Determine the mode of operation for long-time actions like running commands or activation of scenes. However the parallelism disables the in-depth protocol handshake processing which does not affect or limit any functionalities. +config.velux.bridge.isProtocolTraceEnabled.label = Enable Protocol Trace +config.velux.bridge.isProtocolTraceEnabled.description = Provide KLF200 protocol details. +# +config.velux.thing.scene.sceneName.label = Scene Name +config.velux.thing.scene.sceneName.description = Name of the scene to be handled. +config.velux.thing.scene.velocity.label = Velocity +config.velux.thing.scene.velocity.description = Velocity Level. +# +config.velux.thing.actuator.serial.label = Serial Number +config.velux.thing.actuator.serial.description = Eight hex digits (i.e. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.actuator.name.label = Name +config.velux.thing.actuator.name.description = (Optional) Name of the Actuator as defined in the gateway. +config.velux.thing.actuator.inverted.label = Actuator Position Inverted +config.velux.thing.actuator.inverted.description = The actuator position is inverted (i.e. 0% translates to 100%). +config.velux.thing.actuator.limitation.label = Limitation Position +config.velux.thing.actuator.limitation.description = The limitation position of the actuator. +# +config.velux.thing.rollershutter.serial.label = Serial Number +config.velux.thing.rollershutter.serial.description = Eight hex digits (i.e. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.rollershutter.name.label = Name +config.velux.thing.rollershutter.name.description = (Optional) Name of the Actuator as defined in the gateway. +config.velux.thing.rollershutter.inverted.label = Actuator Position Inverted +config.velux.thing.rollershutter.inverted.description = The actuator position is inverted (i.e. 0% translates to 100%). +config.velux.thing.rollershutter.limitation.label = Limitation Position +config.velux.thing.rollershutter.limitation.description = The limitation position of the rollershutter. +# +config.velux.thing.window.serial.label = Serial Number +config.velux.thing.window.serial.description = Eight hex digits (i.e. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.window.name.label = Name +config.velux.thing.window.name.description = (Optional) Name of the Actuator as defined in the gateway. +config.velux.thing.window.inverted.label = Actuator Position Inverted +config.velux.thing.window.inverted.description = The actuator position is inverted (i.e. 0% translates to 100%). +config.velux.thing.window.limitation.label = Limitation Position +config.velux.thing.window.limitation.description = The limitation position of the windows. +# +config.velux.thing.vshutter.sceneLevels.label = List of Positions/Scenes +config.velux.thing.vshutter.sceneLevels.description = Definition of a virtual shutter by declaring one scene corresponding to one position. Therefore this parameter look like: ,,, +config.velux.thing.vshutter.currentLevel.label = Current Position +config.velux.thing.vshutter.currentLevel.description = Value between 0 and 100. +# +# Channel types descriptions +# +channel-type.velux.information.label = Binding Information +channel-type.velux.information.description = Description of current Binding State. +channel-type.velux.status.label = Bridge State +channel-type.velux.status.description = Description of current Bridge State. +channel-type.velux.reload.label = Reload +channel-type.velux.reload.description = Reload Bridge Information. +channel-type.velux.timestamp.label = Timestamp +channel-type.velux.timestamp.description = Timestamp when last successful device interaction happened (seconds since epoch). +channel-type.velux.doDetection.label = Activate Bridge Detection mode +channel-type.velux.doDetection.description = Start of the product detection mode. +channel-type.velux.firmware.label = Firmware Version +channel-type.velux.firmware.description = Software version of the Bridge. +channel-type.velux.ipAddress.label = IP-Adresse +channel-type.velux.ipAddress.description = IP address of the Bridge. +channel-type.velux.subnetMask.label = IP Subnet Mask +channel-type.velux.subnetMask.description = IP subnetmask of the Bridge. +channel-type.velux.defaultGW.label = Default Gateway +channel-type.velux.defaultGW.description = IP address of the default Gateway of the Bridge. +channel-type.velux.DHCP.label = DHCP Enabled +channel-type.velux.DHCP.description = Flag whether automatic IP configuration is enabled. +channel-type.velux.WLANSSID.label = SSID +channel-type.velux.WLANSSID.description = Name of the wireless network. +channel-type.velux.WLANPassword.label = WLAN Authentication +channel-type.velux.WLANPassword.description = WLAN Authentication Password. +channel-type.velux.products.label = Identified Products +channel-type.velux.products.description = Products which are registered on the Bridge. +channel-type.velux.scenes.label = Identified Scenes +channel-type.velux.scenes.description = Scenes which are configured on the Bridge. +channel-type.velux.check.label = Check of configuration +channel-type.velux.check.description = Result of the check of current item configuration. +# +channel-type.velux.position.label = Position +channel-type.velux.position.description = Device control (UP, DOWN, STOP, closure 0-100%). +channel-type.velux.state.label = State +channel-type.velux.state.description = Device control (ON, OFF). +channel-type.velux.action.label = Start of a Scene +channel-type.velux.action.description = Activates a set of predefined product settings. +channel-type.velux.silentMode.label = Enabling silent mode +channel-type.velux.silentMode.description = Activates the silent mode of the predefined product settings. +channel-type.velux.limitation.label = Limitation Position +channel-type.velux.limitation.description = The limitation position of the actuator/rollershutter/window. +channel-type.velux.vposition.label = Position +channel-type.velux.vposition.description = Device control (UP, DOWN, STOP, closure 0-100%). +# +# Runtime status descriptions +# +runtime.no-bridge = So far no bridge is defined. Please add a thing of type "Velux KLF200" to establish a connection to the gateway, which provides the prerequisite for further commissioning. +runtime.one-bridge = A bridge element is already defined. Thus, you can now set up additional devices by means of search (or discovery) or by adding things manually. +runtime.multiple-bridges = There are more than one bridges defined. This is of course possible with several Velux KLF200 gateways or with a redundant setup (SLIP via LAN parallel to JSON via WLAN). Every other case should be avoided. +runtime.bridge-offline-no-valid-bridgeProtocol-selected = The parameter "bridgeProtocol" has to be set correctly. +runtime.bridge-offline-login-sequence-failed = Login sequence failed. +# +# Thing status descriptions +# +channelValue.check-integrity-failed = Integrity check failed: The following scenes are unused: +channelValue.check-integrity-ok = Integrity check ok. All scenes are used within Items. +# +# end-of-ESH-INF/i18n/velux.properties +# diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_de.properties b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_de.properties new file mode 100644 index 0000000000000..d85a0fa6e87eb --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_de.properties @@ -0,0 +1,325 @@ +# +# Copyright (c) 2010-2019 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 +# +# @author Guenther Schreiner - Initial contribution. +# +# +# Binding descriptions +# +binding.velux.name = Velux Binding +binding.velux.description = Dieses Binding integriert io-homecontrol-Gerte, wie Rollden und Fenster, mittels dem Velux KLF200 Kopplungselement. +# +# Bridge Thing types descriptions +# +bridge-type.velux.klf200.label = Velux KLF200 +bridge-type.velux.klf200.description = Dieses Element bildet das Kopplungselement ab, welches zur Interaktion mit unterschiedlichen Velux- bzw. io-homecontrol-Gerten dient. Das Kopplungselement kann nicht per Discovery gefunden werden, sondern ist per Konfiguration einmalig einzurichten. +# +# Thing types descriptions +# +thing-type.velux.binding.label = Velux Binding Information +thing-type.velux.binding.description = Dieses Element informiert ber den Status des Velux-Bindings und wird ohne weitere Konfiguration mittels Discovery automatisch erzeugt. Es dient ausschlielich der Information, um die Inbetriebnahmephase zu untersttzen. +# +thing-type.velux.actuator.label = Velux Aktor +thing-type.velux.actuator.description = Aktor, welcher ber io-homecontrol steuert wird. +# +thing-type.velux.rollershutter.label = Velux Rolladen +thing-type.velux.rollershutter.description = Rolladen, welcher ber io-homecontrol steuert wird. +# +thing-type.velux.scene.label = Velux Szene +thing-type.velux.scene.description = Szene als Sammlung von Gertezustnden, welche aktiviert werden knnen. +# +thing-type.velux.vshutter.label = Virtueller Rolladen +thing-type.velux.vshutter.description = Rolladen, welcher ber eine Menge von Szenen definiert wird. +# +thing-type.velux.window.label = Velux Fenster +thing-type.velux.window.description = Fenster, welcher ber io-homecontrol steuert wird. +# +# Config descriptions +# +config.velux.bridge.ipAddress.label = Adresse des Kopplungselements +config.velux.bridge.ipAddress.description = Name oder IP-Adresse zum Zugriff auf das Velux KLF200 Kopplungselement. +config.velux.bridge.protocol.label = Kommunikationsprotokoll +config.velux.bridge.protocol.description = Protokoll zum Zugriff auf das Velux KLF200 Kopplungselement. +config.velux.bridge.tcpPort.label = Port des Kopplungselements +config.velux.bridge.tcpPort.description = TCP Portnummer zum HTTP-Zugriff auf das Velux KLF200 Kopplungselement. +config.velux.bridge.password.label = Passwort +config.velux.bridge.password.description = Passwort zur Anmeldung an dem Velux KLF200 Kopplungselement. +config.velux.bridge.timeoutMsecs.label = Zeitberschreitung +config.velux.bridge.timeoutMsecs.description = Zeit in Millisekunden. +config.velux.bridge.retries.label = Wiederholungsversuche +config.velux.bridge.retries.description = Anzahl der Verbindungswiederholungen fr den Binary Exponential Backoff (BEB) Algorithmus. +config.velux.bridge.refreshMsecs.label = Auffrischintervall +config.velux.bridge.refreshMsecs.description = Zeit in Millisekunden. +config.velux.bridge.isBulkRetrievalEnabled.label = Massenabfrage +config.velux.bridge.isBulkRetrievalEnabled.description = Aktivierung von Massenabfrage zur Gertebersicht. +config.velux.bridge.isSequentialEnforced.label = Sequentielles Modus +config.velux.bridge.isSequentialEnforced.description = Erzwingt den sequentiellen Modus von Operationen, insbesondere von lnger andauernden Aktionen, wie der Aktivierung von Szenen. +config.velux.bridge.isProtocolTraceEnabled.label = Protokolleinblick +config.velux.bridge.isProtocolTraceEnabled.description = Aktiviert KLF200 Protokolldetails. +# +config.velux.thing.scene.sceneName.label = Scenenname +config.velux.thing.scene.sceneName.description = Name der Szene, wie sie auf dem Kopplungselements definiert wurde. +config.velux.thing.scene.velocity.label = Geschwindigkeit +config.velux.thing.scene.velocity.description = Geschwindigkeitsniveau. +# +config.velux.thing.actuator.serial.label = Seriennumber +config.velux.thing.actuator.serial.description = Acht hexadezimale Ziffern (bspw. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.actuator.name.label = Name +config.velux.thing.actuator.name.description = (Optionaler) Name des Aktuators, wie er auf dem Kopplungselements definiert wurde. +config.velux.thing.actuator.inverted.label = Positioninvertierung +config.velux.thing.actuator.inverted.description = Die Aktuatorposition wird invertiert (bspw. 0% umgesetzt zu 100%). +config.velux.thing.actuator.limitation.label = Begrenzungsposition +config.velux.thing.actuator.limitation.description = Die Begrenzungsposition des Stellantriebs. +# +config.velux.thing.rollershutter.serial.label = Seriennumber +config.velux.thing.rollershutter.serial.description = Acht hexadezimale Ziffern (bspw. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.rollershutter.name.label = Name +config.velux.thing.rollershutter.name.description = (Optionaler) Name des Rolladens, wie er auf dem Kopplungselements definiert wurde. +config.velux.thing.rollershutter.inverted.label = Positioninvertierung +config.velux.thing.rollershutter.inverted.description = Die Rolladenposition wird invertiert (bspw. 0% umgesetzt zu 100%). +config.velux.thing.rollershutter.limitation.label = Begrenzungsposition +config.velux.thing.rollershutter.limitation.description = Die Begrenzungsposition des Rolladens. +# +config.velux.thing.window.serial.label = Seriennumber +config.velux.thing.window.serial.description = Acht hexadezimale Ziffern (bspw. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.window.name.label = Name +config.velux.thing.window.name.description = (Optionaler) Name des Fensters, wie er auf dem Kopplungselements definiert wurde. +config.velux.thing.window.inverted.label = Positioninvertierung +config.velux.thing.window.inverted.description = Die Fensterposition wird invertiert (bspw. 0% umgesetzt zu 100%). +config.velux.thing.window.limitation.label = Begrenzungsposition +config.velux.thing.window.limitation.description = Die Begrenzungsposition des Fensters. +# +config.velux.thing.vshutter.sceneLevels.label = Liste von Positionen/Szenen +config.velux.thing.vshutter.sceneLevels.description = Definition eines virtuellen Rolladens mittels Festlegung von einer Position durch eine Szene. So sieht der Parameter aus wie: ,,, +config.velux.thing.vshutter.currentLevel.label = Aktuelle Position +config.velux.thing.vshutter.currentLevel.description = Wert zwischen 0 und 100. +# +# Channel types descriptions +# +channel-type.velux.information.label = Binding Information +channel-type.velux.information.description = Beschreibung des aktuellen Binding Status. +channel-type.velux.status.label = Status +channel-type.velux.status.description = Status des Kopplungselements. +channel-type.velux.reload.label = Nachladen +channel-type.velux.reload.description = Konfigurationen neu laden. +channel-type.velux.timestamp.label = Zeitstempel +channel-type.velux.timestamp.description = Zeitpunkt des letzten erfolgreichen Kommunikation +channel-type.velux.doDetection.label = Produkterkennung +channel-type.velux.doDetection.description = Schalter zum Start der Produkterkennung. +channel-type.velux.firmware.label = Firmware +channel-type.velux.firmware.description = Softwareversion des Kopplungselements. +channel-type.velux.ipAddress.label = LAN IP-Adresse +channel-type.velux.ipAddress.description = IP-Adresse des Kopplungselements. +channel-type.velux.subnetMask.label = LAN Subnetzmaske +channel-type.velux.subnetMask.description = IP-Subnetzmaske des Kopplungselements. +channel-type.velux.defaultGW.label = LAN Router-IP-Adresse +channel-type.velux.defaultGW.description = IP-Adresse des Routers. +channel-type.velux.DHCP.label = LAN DHCP-Aktivierung +channel-type.velux.DHCP.description = Aktivierung der automatischen IP-Konfiguration mittels DHCP. +channel-type.velux.WLANSSID.label = WLAN-SSID +channel-type.velux.WLANSSID.description = Netzwerkkennung des Wireless-Netzwerks. +channel-type.velux.WLANPassword.label = WLAN-Passwort +channel-type.velux.WLANPassword.description = Passwort zum Zugriff auf das Wireless-Netzwerk. +channel-type.velux.products.label = Identifizierte Produkte +channel-type.velux.products.description = Produkte welche auf dem Kopplungselement im Rahmen von Szenen definiert sind. +channel-type.velux.scenes.label = Identifizierte Szenen +channel-type.velux.scenes.description = Szenen welche auf dem Kopplungselement definiert sind. +channel-type.velux.check.label = Konfigurationsprfung +channel-type.velux.check.description = Vergleich der Gateway-Konfiguration mit der openHAB-Konfiguration. +# +channel-type.velux.position.label = Position +channel-type.velux.position.description = Positionssteuerungselement (hoch/runter/stop). +channel-type.velux.state.label = Zustand +channel-type.velux.state.description = Steuerung (AN, AUS). +channel-type.velux.action.label = Szenenstart +channel-type.velux.action.description = Schalter zum Start dieser Szene +channel-type.velux.silentMode.label = Stillmodus +channel-type.velux.silentMode.description = Aktiviert den Leise-Modus auf einer definierten Szene. +channel-type.velux.limitation.label = Begrenzungsposition +channel-type.velux.limitation.description = Begrenzungsposition des Aktuators/Rolladens/Fensters. +channel-type.velux.vposition.label = Position +channel-type.velux.vposition.description = Positionssteuerungselement (hoch/runter/stop). +# +# Runtime status descriptions +# +runtime.no-bridge = Bislang ist noch ist kein Kopplungselement definiert. Bitte fgen Sie ein Thing vom Typ "Velux KLF200" hinzu, um eine Verbindung zum Kopplungselement aufzubauen, welches die Voraussetzung fr die weitere Inbetriebnahme darstellt. +runtime.one-bridge = Es ist bereits ein Kopplungselement definiert. Somit knnen Sie nun weitere Gerte mittels Suche (bzw. Discovery) oder durch manuelles Hinzufgen von Things einrichten. +runtime.multiple-bridges = Es sind mehr als ein Kopplungselement definiert. Dies ist natrlich bei mehreren Velux KLF200 Gateways oder bei einem redundanten Aufbau (SLIP ber LAN parallel zu JSON ber WLAN) mglich. Jeder andere Fall sollte vermieden werden. +runtime.bridge-offline-no-valid-bridgeProtocol-selected = Der Parameter "bridgeProtocol" muss korrekt gesetzt werden. +runtime.bridge-offline-login-sequence-failed = Anmeldung am Kopplungselement fehlgeschlagen. +# +# Thing status descriptions +# +channelValue.check-integrity-failed = Integrittsprfung fehlgeschlagen. Nachfolgende Szenen werden nicht verwendet: +channelValue.check-integrity-ok = Integrittsprfung bestanden. Alle definierten Szenen werden verwendet. +# +# Note: this entry should overwrite the implicit naming within paperUI slider element +# +channel-type.velux.control.label = Steuerung +channel-type.velux.control.description = Steuerungselement (hoch/runter/stop). +# +# end-of-ESH-INF/i18n/velux_de.properties +# +# +config.velux.thing.window.serial.label = Seriennumber +config.velux.thing.window.serial.description = Acht hexadezimale Ziffern (bspw. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.window.name.label = Name +config.velux.thing.window.name.description = (Optionaler) Name des Fensters, wie er auf dem Kopplungselements definiert wurde. +config.velux.thing.window.inverted.label = Positioninvertierung +config.velux.thing.window.inverted.description = Die Fensterposition wird invertiert (bspw. 0% umgesetzt zu 100%). +# +config.velux.thing.vshutter.sceneLevels.label = Liste von Positionen/Szenen +config.velux.thing.vshutter.sceneLevels.description = Definition eines virtuellen Rolladens mittels Festlegung von einer Position durch eine Szene. So sieht der Parameter aus wie: ,,, +config.velux.thing.vshutter.currentLevel.label = Aktuelle Position +config.velux.thing.vshutter.currentLevel.description = Wert zwischen 0 und 100. +# +# Channel types descriptions +# +channel-type.velux.information.label = Binding Information +channel-type.velux.information.description = Beschreibung des aktuellen Binding Status. +channel-type.velux.status.label = Status +channel-type.velux.status.description = Status des Kopplungselements. +channel-type.velux.reload.label = Nachladen +channel-type.velux.reload.description = Konfigurationen neu laden. +channel-type.velux.timestamp.label = Zeitstempel +channel-type.velux.timestamp.description = Zeitpunkt des letzten erfolgreichen Kommunikation +channel-type.velux.doDetection.label = Produkterkennung +channel-type.velux.doDetection.description = Schalter zum Start der Produkterkennung. +channel-type.velux.firmware.label = Firmware +channel-type.velux.firmware.description = Softwareversion des Kopplungselements. +channel-type.velux.ipAddress.label = LAN IP-Adresse +channel-type.velux.ipAddress.description = IP-Adresse des Kopplungselements. +channel-type.velux.subnetMask.label = LAN Subnetzmaske +channel-type.velux.subnetMask.description = IP-Subnetzmaske des Kopplungselements. +channel-type.velux.defaultGW.label = LAN Router-IP-Adresse +channel-type.velux.defaultGW.description = IP-Adresse des Routers. +channel-type.velux.DHCP.label = LAN DHCP-Aktivierung +channel-type.velux.DHCP.description = Aktivierung der automatischen IP-Konfiguration mittels DHCP. +channel-type.velux.WLANSSID.label = WLAN-SSID +channel-type.velux.WLANSSID.description = Netzwerkkennung des Wireless-Netzwerks. +channel-type.velux.WLANPassword.label = WLAN-Passwort +channel-type.velux.WLANPassword.description = Passwort zum Zugriff auf das Wireless-Netzwerk. +channel-type.velux.products.label = Identifizierte Produkte +channel-type.velux.products.description = Produkte welche auf dem Kopplungselement im Rahmen von Szenen definiert sind. +channel-type.velux.scenes.label = Identifizierte Szenen +channel-type.velux.scenes.description = Szenen welche auf dem Kopplungselement definiert sind. +channel-type.velux.check.label = Konfigurationsprfung +channel-type.velux.check.description = Vergleich der Gateway-Konfiguration mit der openHAB-Konfiguration. +# +channel-type.velux.position.label = Position +channel-type.velux.position.description = Positionssteuerungselement (hoch/runter/stop). +channel-type.velux.state.label = Zustand +channel-type.velux.state.description = Steuerung (AN, AUS). +channel-type.velux.action.label = Szenenstart +channel-type.velux.action.description = Schalter zum Start dieser Szene +channel-type.velux.silentMode.label = Stillmodus +channel-type.velux.silentMode.description = Aktiviert den Leise-Modus auf einer definierten Szene. +channel-type.velux.vposition.label = Position +channel-type.velux.vposition.description = Positionssteuerungselement (hoch/runter/stop). +# +# Runtime status descriptions +# +runtime.no-bridge = Bislang ist noch ist kein Kopplungselement definiert. Bitte fgen Sie ein Thing vom Typ "Velux KLF200" hinzu, um eine Verbindung zum Kopplungselement aufzubauen, welches die Voraussetzung fr die weitere Inbetriebnahme darstellt. +runtime.one-bridge = Es ist bereits ein Kopplungselement definiert. Somit knnen Sie nun weitere Gerte mittels Suche (bzw. Discovery) oder durch manuelles Hinzufgen von Things einrichten. +runtime.multiple-bridges = Es sind mehr als ein Kopplungselement definiert. Dies ist natrlich bei mehreren Velux KLF200 Gateways oder bei einem redundanten Aufbau (SLIP ber LAN parallel zu JSON ber WLAN) mglich. Jeder andere Fall sollte vermieden werden. +runtime.bridge-offline-no-valid-bridgeProtocol-selected = Der Parameter "bridgeProtocol" muss korrekt gesetzt werden. +runtime.bridge-offline-login-sequence-failed = Anmeldung am Kopplungselement fehlgeschlagen. +# +# Thing status descriptions +# +channelValue.check-integrity-failed = Integrittsprfung fehlgeschlagen. Nachfolgende Szenen werden nicht verwendet: +channelValue.check-integrity-ok = Integrittsprfung bestanden. Alle definierten Szenen werden verwendet. +# +# Note: this entry should overwrite the implicit naming within paperUI slider element +# +channel-type.velux.control.label = Steuerung +channel-type.velux.control.description = Steuerungselement (hoch/runter/stop). +# +# end-of-ESH-INF/i18n/velux_de.properties +# +# +config.velux.thing.window.serial.label = Seriennumber +config.velux.thing.window.serial.description = Acht hexadezimale Ziffern (bspw. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.window.name.label = Name +config.velux.thing.window.name.description = (Optionaler) Name des Fensters, wie er auf dem Kopplungselements definiert wurde. +config.velux.thing.window.inverted.label = Positioninvertierung +config.velux.thing.window.inverted.description = Die Fensterposition wird invertiert (bspw. 0% umgesetzt zu 100%). +# +config.velux.thing.vshutter.sceneLevels.label = Liste von Positionen/Szenen +config.velux.thing.vshutter.sceneLevels.description = Definition eines virtuellen Rolladens mittels Festlegung von einer Position durch eine Szene. So sieht der Parameter aus wie: ,,, +config.velux.thing.vshutter.currentLevel.label = Aktuelle Position +config.velux.thing.vshutter.currentLevel.description = Wert zwischen 0 und 100. +# +# Channel types descriptions +# +channel-type.velux.information.label = Binding Information +channel-type.velux.information.description = Beschreibung des aktuellen Binding Status. +channel-type.velux.status.label = Status +channel-type.velux.status.description = Status des Kopplungselements. +channel-type.velux.reload.label = Nachladen +channel-type.velux.reload.description = Konfigurationen neu laden. +channel-type.velux.timestamp.label = Zeitstempel +channel-type.velux.timestamp.description = Zeitpunkt des letzten erfolgreichen Kommunikation +channel-type.velux.doDetection.label = Produkterkennung +channel-type.velux.doDetection.description = Schalter zum Start der Produkterkennung. +channel-type.velux.firmware.label = Firmware +channel-type.velux.firmware.description = Softwareversion des Kopplungselements. +channel-type.velux.ipAddress.label = LAN IP-Adresse +channel-type.velux.ipAddress.description = IP-Adresse des Kopplungselements. +channel-type.velux.subnetMask.label = LAN Subnetzmaske +channel-type.velux.subnetMask.description = IP-Subnetzmaske des Kopplungselements. +channel-type.velux.defaultGW.label = LAN Router-IP-Adresse +channel-type.velux.defaultGW.description = IP-Adresse des Routers. +channel-type.velux.DHCP.label = LAN DHCP-Aktivierung +channel-type.velux.DHCP.description = Aktivierung der automatischen IP-Konfiguration mittels DHCP. +channel-type.velux.WLANSSID.label = WLAN-SSID +channel-type.velux.WLANSSID.description = Netzwerkkennung des Wireless-Netzwerks. +channel-type.velux.WLANPassword.label = WLAN-Passwort +channel-type.velux.WLANPassword.description = Passwort zum Zugriff auf das Wireless-Netzwerk. +channel-type.velux.products.label = Identifizierte Produkte +channel-type.velux.products.description = Produkte welche auf dem Kopplungselement im Rahmen von Szenen definiert sind. +channel-type.velux.scenes.label = Identifizierte Szenen +channel-type.velux.scenes.description = Szenen welche auf dem Kopplungselement definiert sind. +channel-type.velux.check.label = Konfigurationsprfung +channel-type.velux.check.description = Vergleich der Gateway-Konfiguration mit der openHAB-Konfiguration. +# +channel-type.velux.position.label = Position +channel-type.velux.position.description = Positionssteuerungselement (hoch/runter/stop). +channel-type.velux.state.label = Zustand +channel-type.velux.state.description = Steuerung (AN, AUS). +channel-type.velux.action.label = Szenenstart +channel-type.velux.action.description = Schalter zum Start dieser Szene +channel-type.velux.silentMode.label = Stillmodus +channel-type.velux.silentMode.description = Aktiviert den Leise-Modus auf einer definierten Szene. +channel-type.velux.vposition.label = Position +channel-type.velux.vposition.description = Positionssteuerungselement (hoch/runter/stop). +# +# Runtime status descriptions +# +runtime.no-bridge = Bislang ist noch ist kein Kopplungselement definiert. Bitte fgen Sie ein Thing vom Typ "Velux KLF200" hinzu, um eine Verbindung zum Kopplungselement aufzubauen, welches die Voraussetzung fr die weitere Inbetriebnahme darstellt. +runtime.one-bridge = Es ist bereits ein Kopplungselement definiert. Somit knnen Sie nun weitere Gerte mittels Suche (bzw. Discovery) oder durch manuelles Hinzufgen von Things einrichten. +runtime.multiple-bridges = Es sind mehr als ein Kopplungselement definiert. Dies ist natrlich bei mehreren Velux KLF200 Gateways oder bei einem redundanten Aufbau (SLIP ber LAN parallel zu JSON ber WLAN) mglich. Jeder andere Fall sollte vermieden werden. +runtime.bridge-offline-no-valid-bridgeProtocol-selected = Der Parameter "bridgeProtocol" muss korrekt gesetzt werden. +runtime.bridge-offline-login-sequence-failed = Anmeldung am Kopplungselement fehlgeschlagen. +# +# Thing status descriptions +# +channelValue.check-integrity-failed = Integrittsprfung fehlgeschlagen. Nachfolgende Szenen werden nicht verwendet: +channelValue.check-integrity-ok = Integrittsprfung bestanden. Alle definierten Szenen werden verwendet. +# +# Note: this entry should overwrite the implicit naming within paperUI slider element +# +channel-type.velux.control.label = Steuerung +channel-type.velux.control.description = Steuerungselement (hoch/runter/stop). +# +# end-of-ESH-INF/i18n/velux_de.properties +# diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_dk.properties b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_dk.properties new file mode 100644 index 0000000000000..7912aac57b188 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_dk.properties @@ -0,0 +1,160 @@ +# +# Copyright (c) 2010-2019 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 +# +# @author Guenther Schreiner - Initial contribution. +# +# Binding descriptions +# +binding.velux.name = Velux-binding +binding.velux.description = En binding til Velux KLF200 Bridge. Velux Binding interagerer via en Velux Bridge med de forskellige Velux-enheder, såsom styring af vinduesåbnere, skodder og andre. F.eks. Kan en KLF200 fungere som interface mellem HomeAutomation og VELUX INTEGRA-produkter med trådløs tilslutning baseret på io-homecontrol-standarden. +# +# Bridge Thing typer beskrivelser +# +bridge-type.velux.klf200.label = Velux KLF200 Bridge +bridge-type.velux.klf200.description = Velux KLF200 repræsenterer en gateway til alle Velux resp. io-homecontrol-enheder. +# +# Beskrivelse af tingstyper +# +thing-type.velux.actuator.label = Velux-aktuatorinformation +thing-type.velux.actuator.description = Styr en aktuator via Velux KLF 200 +# +thing-type.velux.rollershutter.label = Velux-rullershutterinformation +thing-type.velux.rollershutter.description = Styr en rullehjul på Velux KLF 200 +# +thing-type.velux.scene.label = Velux-sceneoplysninger +thing-type.velux.scene.description = Styr en scene på Velux KLF 200 +# +thing-type.velux.vshutter.label = Information om virtuel lukker +thing-type.velux.vshutter.description = Et sæt scener, der fungerer sammen som lukker +# +thing-type.velux.window.label = Velux-vinduesinformation +thing-type.velux.window.description = Styr et vindue på Velux KLF 200 +# +# Konfig-beskrivelser +# +config.velux.bridge.ipAddress.label = IP-adresse +config.velux.bridge.ipAddress.description = IP-adressen på Velux Bridge. +config.velux.bridge.protocol.label = Protokol +config.velux.bridge.protocol.description = Forbindelsesprotokollen for at kontakte Velux Bridge med (enten slip eller http eller https). +config.velux.bridge.tcpPort.label = Port +config.velux.bridge.tcpPort.description = TCP-port på Velux-gateway. +config.velux.bridge.password.label = Adgangskode +config.velux.bridge.password.description = Adgangskode til godkendelse mod Velux Bridge. +config.velux.bridge.timeoutMsecs.label = Timeout +config.velux.bridge.timeoutMsecs.description = Timeout for startforbindelse i millisekunder. +config.velux.bridge.retries.label = Tilslutningsforsøg igen +config.velux.bridge.retries.description = Antal forsøg med timing langs algoritmen Binary Exponential Backoff (BEB). +config.velux.bridge.refreshMsecs.label = Opdater interval +config.velux.bridge.refreshMsecs.description = Opdater interval i millisekunder. +config.velux.bridge.isBulkRetrievalEnabled.label = Aktivér masseopsamling +config.velux.bridge.isBulkRetrievalEnabled.description = Hent de komplette aktuatoroplysninger i en bulk. +config.velux.bridge.isSequentialEnforced.label = Håndhæv sekventiel tilstand +config.velux.bridge.isSequentialEnforced.description = Håndhæv sekventiel aktuatorstyring. Bestem driftsmåden for handlinger i lang tid som at køre kommandoer eller aktivering af scener. Paralleliteten deaktiverer imidlertid den dybdegående protokolhåndtryksbehandling, som ikke påvirker eller begrænser nogen funktionaliteter. +config.velux.bridge.isProtocolTraceEnabled.label = Protocol Insight +config.velux.bridge.isProtocolTraceEnabled.description = Aktiverer KLF200-protokoldetaljer. +# +config.velux.thing.scene.sceneName.label = Scenavn +config.velux.thing.scene.sceneName.description = Navn på den scene, der skal håndteres. +config.velux.thing.scene.velocity.label = Velocity +config.velux.thing.scene.velocity.description = Hastighedsniveau. +# +config.velux.thing.actuator.serial.label = Serienummer +config.velux.thing.actuator.serial.description = Otte hexcifre (dvs. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.actuator.name.label = Navn +config.velux.thing.actuator.name.description = (Valgfrit) Navn på aktuatoren som defineret i gateway. +config.velux.thing.actuator.inverted.label = Aktuatorposition Inverteret +config.velux.thing.actuator.inverted.description = Aktuatorpositionen er omvendt (dvs. 0% oversættes til 100%). +config.velux.thing.actuator.limitation.label = Begrænsningsposition +config.velux.thing.actuator.limitation.description = Aktuatorens begrænsningsposition. +# +config.velux.thing.rollershutter.serial.label = Serienummer +config.velux.thing.rollershutter.serial.description = Otte hex-cifre (dvs. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.rollershutter.name.label = Navn +config.velux.thing.rollershutter.name.description = (Valgfrit) Navn på rulleskodder som defineret i gateway. +config.velux.thing.rollershutter.inverted.label = Rulleskodderposition Inverteret +config.velux.thing.rollershutter.inverted.description = Rulleskodderposition er omvendt (dvs. 0% oversættes til 100%). +config.velux.thing.rollershutter.limitation.label = Begrænsningsposition +config.velux.thing.rollershutter.limitation.description = Rulleskodders begrænsningsposition. +# +config.velux.thing.window.serial.label = Serienummer +config.velux.thing.window.serial.description = Otte hexcifre (dvs. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.window.name.label = Navn +config.velux.thing.window.name.description = (Valgfrit) Navn på vindue som defineret i gatewayen. +config.velux.thing.window.inverted.label = Vindueposition Inverteret +config.velux.thing.window.inverted.description = Vinduepositionen er omvendt (dvs. 0% oversættes til 100%). +config.velux.thing.window.limitation.label = Begrænsningsposition +config.velux.thing.window.limitation.description = Vindues begrænsningsposition. +# +config.velux.thing.vshutter.sceneLevels.label = Liste over positioner / scener +config.velux.thing.vshutter.sceneLevels.description = Definition af en virtuel lukker ved at erklære en scene, der svarer til en position. Derfor ser denne parameter ud: , , , +config.velux.thing.vshutter.currentLevel.label = Aktuel position +config.velux.thing.vshutter.currentLevel.description = Værdi mellem 0 og 100. +# +# Channel types descriptions +# +channel-type.velux.status.label = Bridge State +channel-type.velux.status.description = Beskrivelse af den aktuelle Bridge State. +channel-type.velux.reload.label = Genindlæs +channel-type.velux.reload.description = Opdater broinformation. +channel-type.velux.timestamp.label = Tidstempel +channel-type.velux.timestamp.description = Tidsstempel, når sidst vellykkede enhedsinteraktion skete (sekunder siden epoke). +channel-type.velux.doDetection.label = Aktivér Bridge Detection-tilstand +channel-type.velux.doDetection.description = Start af produktdetekteringstilstand. +channel-type.velux.firmware.label = Firmwareversion +channel-type.velux.firmware.description = Softwareversion af Bridge. +channel-type.velux.ipAddress.label = IP-adresse +channel-type.velux.ipAddress.description = IP-adressen på Bridge. +channel-type.velux.subnetMask.label = IP-subnetmaske +channel-type.velux.subnetMask.description = IP-subnetmaske af Bridge. +channel-type.velux.defaultGW.label = Standard Gateway +channel-type.velux.defaultGW.description = IP-adresse til standard Gateway of the Bridge. +channel-type.velux.DHCP.label = DHCP aktiveret +channel-type.velux.DHCP.description = Marker, om automatisk IP-konfiguration er aktiveret. +channel-type.velux.WLANSSID.label = SSID +channel-type.velux.WLANSSID.description = Navn på det trådløse netværk. +channel-type.velux.WLANPassword.label = WLAN-godkendelse +channel-type.velux.WLANPassword.description = WLAN-godkendelsesadgangskode. +channel-type.velux.products.label = Identificerede produkter +channel-type.velux.products.description = Produkter, der er registreret på Bridge. +channel-type.velux.scenes.label = Identificerede scener +channel-type.velux.scenes.description = Scener, der er konfigureret på Bridge. +channel-type.velux.check.label = Kontrol af konfiguration +channel-type.velux.check.description = Resultat af kontrollen med den aktuelle varekonfiguration. +# +channel-type.velux.position.label = Position +channel-type.velux.position.description = Enhedskontrol (OP, NED, STOP, lukning 0-100%). +channel-type.velux.state.label = State +channel-type.velux.state.description = Device control (ON, OFF). +channel-type.velux.action.label = Start af en scene +channel-type.velux.action.description = Aktiverer et sæt foruddefinerede produktindstillinger. +channel-type.velux.silentMode.label = Aktiverer lydtilstand +channel-type.velux.silentMode.description = Aktiverer lydløs tilstand for de foruddefinerede produktindstillinger. +channel-type.velux.limitation.label = Begrænsningsposition +channel-type.velux.limitation.description = Aktuatorens/Rulleskodders/Vindues begrænsningsposition. +channel-type.velux.vposition.label = Position +channel-type.velux.vposition.description = Enhedskontrol (OP, NED, STOP, lukning 0-100%). +# +# Statusbeskrivelser for kørsel +# +runtime.no-bridge = Indtil videre er intet koblingselement defineret. Tilføj en "Velux KLF200" ting for at etablere en forbindelse til koblingselementet, hvilket er forudsætningen for yderligere idriftsættelse. +runtime.one-bridge = Et koblingselement er allerede defineret. Således kan du nu konfigurere yderligere enheder ved hjælp af søgning (eller opdagelse) eller ved at tilføje ting manuelt. +runtime.multiple-bridges = Der er mere end et koblingselement defineret. Dette er selvfølgelig muligt med flere Velux KLF200 gateways eller med en redundant opsætning (SLIP via LAN parallel med JSON via WLAN). Alle andre tilfælde bør undgås. +runtime.bridge-offline-no-valid-bridgeProtocol = Parameteren "bridgeProtocol" skal indstilles korrekt. +runtime.bridge-offline-login -sequence-failed = Login-sekvens mislykkedes. +# +# Statusbeskrivelser +# +channelValue.check-integrity-failed = Integritetskontrol mislykkedes: Følgende scener er ubrugte: +channelValue.check-integrity-ok = Integrity check ok. Alle scener bruges i artikler. +# +# end-of-ESH-INF/i18n/velux.properties +# diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_nl.properties b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_nl.properties new file mode 100644 index 0000000000000..91cb6854f1661 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_nl.properties @@ -0,0 +1,160 @@ +# +# Copyright (c) 2010-2019 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 +# +# @author Guenther Schreiner - Initial contribution. +# +# Binding descriptions +# +binding.velux.name = Velux binding +binding.velux.description = Een binding voor de Velux KLF200 Bridge. De Velux-binding werkt via een Velux-brug samen met de verschillende Velux-apparaten, zoals het bedienen van vensteropeners, rolluiken en andere. Een KLF200 kan bijvoorbeeld fungeren als interface tussen de HomeAutomation en de VELUX INTEGRA-producten met draadloze connectiviteit op basis van de io-homecontrol-standaard. +# +# Beschrijvingen van Bridge Thing-typen +# +bridge-type.velux.klf200.label = Velux KLF200-brug +bridge-type.velux.klf200.description = De Velux KLF200 vertegenwoordigt een toegangspoort tot alle Velux resp. io-homecontrol-apparaten. +# +# Beschrijvingen van dingtypen +# +thing-type.velux.actuator.label = Velux Actuator-informatie +thing-type.velux.actuator.description = Bedien een actuator via de Velux KLF 200 +# +thing-type.velux.rollershutter.label = Velux Rollershutter-informatie +thing-type.velux.rollershutter.description = Bedien een rolluik op de Velux KLF 200 +# +thing-type.velux.scene.label = Velux Scene-informatie +thing-type.velux.scene.description = Bedien een scène op de Velux KLF 200 +# +thing-type.velux.vshutter.label = Virtuele sluiterinformatie +thing-type.velux.vshutter.description = Een set scènes die samenwerken als sluiter +# +thing-type.velux.window.label = Velux-vensterinformatie +thing-type.velux.window.description = Bedien een venster op de Velux KLF 200 +# +# Config beschrijvingen +# +config.velux.bridge.ipAddress.label = IP-adres +config.velux.bridge.ipAddress.description = Het IP-adres van de Velux Bridge. +config.velux.bridge.protocol.label = Protocol +config.velux.bridge.protocol.description = Het verbindingsprotocol waarmee contact wordt gemaakt met de Velux Bridge (slip of http of https). +config.velux.bridge.tcpPort.label = Poort +config.velux.bridge.tcpPort.description = TCP-poort van de Velux-gateway. +config.velux.bridge.password.label = Wachtwoord +config.velux.bridge.password.description = Wachtwoord voor authenticatie tegen de Velux Bridge. +config.velux.bridge.timeoutMsecs.label = Timeout +config.velux.bridge.timeoutMsecs.description = Eerste time-out van de verbinding in milliseconden. +config.velux.bridge.retries.label = Opnieuw proberen verbinding +config.velux.bridge.retries.description = Aantal nieuwe pogingen met timing langs het Binary Exponential Backoff (BEB) algoritme. +config.velux.bridge.refreshMsecs.label = Vernieuwingsinterval +config.velux.bridge.refreshMsecs.description = Vernieuwingsinterval in milliseconden. +config.velux.bridge.isBulkRetrievalEnabled.label = Bulk ophalen inschakelen +config.velux.bridge.isBulkRetrievalEnabled.description = Haal de volledige actuatorinformatie in bulk op. +config.velux.bridge.isSequentialEnforced.label = Opeenvolgende modus afdwingen +config.velux.bridge.isSequentialEnforced.description = Handhaving van sequentiële actuatorcontrole. Bepaal de werkingsmodus voor langdurige acties zoals het uitvoeren van opdrachten of het activeren van scènes. Het parallellisme schakelt echter de diepgaande protocolhandshake-verwerking uit die geen enkele functionaliteit beïnvloedt of beperkt. +config.velux.bridge.isProtocolTraceEnabled.label = Protocol Insight +config.velux.bridge.isProtocolTraceEnabled.description = Schakelt KLF200-protocoldetails in. +# +config.velux.thing.scene.sceneName.label = Scènenaam +config.velux.thing.scene.sceneName.description = Naam van de te behandelen scène. +config.velux.thing.scene.velocity.label = Snelheid +config.velux.thing.scene.velocity.description = Velocity Level. +# +config.velux.thing.actuator.serial.label = Serienummer +config.velux.thing.actuator.serial.description = Acht hexadecimale cijfers (d.w.z. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.actuator.name.label = Naam +config.velux.thing.actuator.name.description = (Optioneel) Naam van de actuator zoals gedefinieerd in de gateway. +config.velux.thing.actuator.inverted.label = Actuatorpositie omgekeerd +config.velux.thing.actuator.inverted.description = De actuatorpositie is omgekeerd (d.w.z. 0% vertaalt zich naar 100%). +config.velux.thing.actuator.limitation.label = Beperkingspositie +config.velux.thing.actuator.limitation.description = De beperkingspositie van de actuator. +# +config.velux.thing.rollershutter.serial.label = Serienummer +config.velux.thing.rollershutter.serial.description = Acht hexadecimale cijfers (d.w.z. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.rollershutter.name.label = Naam +config.velux.thing.rollershutter.name.description = (Optioneel) Naam van de rolluik zoals gedefinieerd in de gateway. +config.velux.thing.rollershutter.inverted.label = Rolluikpositie omgekeerd +config.velux.thing.rollershutter.inverted.description = De Rolluikpositie is omgekeerd (d.w.z. 0% vertaalt zich naar 100%). +config.velux.thing.rollershutter.limitation.label = Beperkingspositie +config.velux.thing.rollershutter.limitation.description = De beperkingspositie van de rolluik. +# +config.velux.thing.window.serial.label = Serienummer +config.velux.thing.window.serial.description = Acht hexadecimale cijfers (d.w.z. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.window.name.label = Naam +config.velux.thing.window.name.description = (Optioneel) Naam van de venster zoals gedefinieerd in de gateway. +config.velux.thing.window.inverted.label = Vensterpositie omgekeerd +config.velux.thing.window.inverted.description = De Vensterpositie is omgekeerd (d.w.z. 0% vertaalt zich naar 100%). +config.velux.thing.window.limitation.label = Beperkingspositie +config.velux.thing.window.limitation.description = De beperkingspositie van de venster. +# +config.velux.thing.vshutter.sceneLevels.label = Lijst met posities / scènes +config.velux.thing.vshutter.sceneLevels.description = Definitie van een virtuele sluiter door een scène aan te geven die overeenkomt met een positie. Daarom ziet deze parameter er als volgt uit: , , , +config.velux.thing.vshutter.currentLevel.label = Huidige positie +thing.vshutter.currentLevel.description = Waarde tussen 0 en 100. +# +# Kanaaltypebeschrijvingen +# +channel-type.velux.status.label = Bridge State +channel-type.velux.status.description = Beschrijving van huidige brugstatus. +channel-type.velux.reload.label = Herladen +channel-type.velux.reload.description = Bruginformatie opnieuw laden. +channel-type.velux.timestamp.label = Tijdstempel +channel-type.velux.timestamp.description = Tijdstempel wanneer de laatste succesvolle apparaatinteractie plaatsvond (seconden sinds het tijdperk). +channel-type.velux.doDetection.label = Activeer de brugdetectiemodus +channel-type.velux.doDetection.description = Start van de productdetectiemodus. +channel-type.velux.firmware.label = Firmwareversie +channel-type.velux.firmware.description = Softwareversie van de Bridge. +channel-type.velux.ipAddress.label = IP-adres +channel-type.velux.ipAddress.description = IP-adres van de Bridge. +channel-type.velux.subnetMask.label = IP-subnetmasker +channel-type.velux.subnetMask.description = IP-subnetmasker van de brug. +channel-type.velux.defaultGW.label = Standaard gateway +channel-type.velux.defaultGW.description = IP-adres van de standaardgateway van de brug. +channel-type.velux.DHCP.label = DHCP ingeschakeld +channel-type.velux.DHCP.description = Markeren of automatische IP-configuratie is ingeschakeld. +channel-type.velux.WLANSSID.label = SSID +channel-type.velux.WLANSSID.description = Naam van het draadloze netwerk. +channel-type.velux.WLANPassword.label = WLAN-verificatie +channel-type.velux.WLANPassword.description = WLAN-verificatiewachtwoord. +channel-type.velux.products.label = Geïdentificeerde producten +channel-type.velux.products.description = Producten die op de Bridge zijn geregistreerd. +channel-type.velux.scenes.label = Geïdentificeerde scènes +channel-type.velux.scenes.description = Scènes die op de Bridge zijn geconfigureerd. +channel-type.velux.check.label = Controle van configuratie +channel-type.velux.check.description = Resultaat van de controle van de huidige itemconfiguratie. +# +channel-type.velux.position.label = Positie +channel-type.velux.position.description = Apparaatbeheer (OMHOOG, OMLAAG, STOP, sluiting 0-100%). +channel-type.velux.state.label = State +channel-type.velux.state.description = Device control (ON, OFF). +channel-type.velux.action.label = Start van een scène +channel-type.velux.action.description = Activeert een set vooraf gedefinieerde productinstellingen. +channel-type.velux.silentMode.label = Stille modus inschakelen +channel-type.velux.silentMode.description = Activeert de stille modus van de vooraf gedefinieerde productinstellingen. +channel-type.velux.limitation.label = Beperkingspositie +channel-type.velux.limitation.description = De beperkingspositie van de actuator/rolluik/venster. +channel-type.velux.vposition.label = Positie +channel-type.velux.vposition.description = Apparaatbeheer (OMHOOG, OMLAAG, STOP, sluiting 0-100%). +# +# Beschrijvingen van de runtimestatus +# +runtime.no-bridge = Tot nu toe is er geen koppelingselement gedefinieerd. Voeg een "Velux KLF200" toe om een verbinding met het koppelingselement tot stand te brengen, wat een voorwaarde is voor verdere inbedrijfstelling. +runtime.one-bridge = Een koppelelement is al gedefinieerd. Je kunt nu dus extra apparaten instellen door te zoeken (of te zoeken) of door dingen handmatig toe te voegen. +runtime.multiple-bridges = Er zijn meer dan één koppelelement gedefinieerd. Dit is natuurlijk mogelijk met verschillende Velux KLF200-gateways of met een redundante installatie (SLIP via LAN parallel aan JSON via WLAN). Elk ander geval moet worden vermeden. +runtime.bridge-offline-no-valid-bridgeProtocol-selected = De parameter "bridgeProtocol" moet correct worden ingesteld. +runtime.bridge-offline-login-sequence-failure = Aanmeldingsreeks mislukt. +# +# Ding statusbeschrijvingen +# +channelValue.check-integrity-failure = Integriteitscontrole mislukt: de volgende scènes worden niet gebruikt: +channelValue.check-integrity-ok = Integriteitscontrole ok. Alle scènes worden gebruikt binnen Items. +# +# end-of-ESH-INF / i18n / velux_nl.properties +# diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/actuator.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/actuator.xml new file mode 100644 index 0000000000000..cf33c07a5c9c7 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/actuator.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + @text/thing-type.velux.actuator.description + Rollershutter + + + + + + + serialNumber + + + + + + diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/binding.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/binding.xml new file mode 100644 index 0000000000000..45df7f30d2f96 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/binding.xml @@ -0,0 +1,24 @@ + + + + + + + + + @text/thing-type.velux.binding.description + Other + + + + + N/A + + + + + + diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/bridge.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/bridge.xml new file mode 100644 index 0000000000000..c696e9159774f --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/bridge.xml @@ -0,0 +1,43 @@ + + + + + + + + @text/bridge-type.velux.klf200.description + + + + + + + + + + + + + + + + + + + + + Velux + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/channels.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/channels.xml new file mode 100644 index 0000000000000..6f65da79f4026 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/channels.xml @@ -0,0 +1,181 @@ + + + + + + + + String + + @text/channel-type.velux.information.description + Binding + + + + + + + + String + + @text/channel-type.velux.status.description + Bridge + + + + + Switch + + @text/channel-type.velux.reload.description + Bridge + + + + Number + + @text/channel-type.velux.timestamp.description + Bridge + + + + + Switch + + @text/channel-type.velux.doDetection.description + Bridge + + + + String + + @text/channel-type.velux.firmware.description + Bridge + + + + + String + + @text/channel-type.velux.ipAddress.description + Bridge + + + + + String + + @text/channel-type.velux.subnetMask.description + Bridge + + + + + String + + @text/channel-type.velux.defaultGW.description + Bridge + + + + + Switch + + @text/channel-type.velux.DHCP.description + Bridge + + + + + String + + @text/channel-type.velux.WLANSSID.description + Bridge + + + + + String + + @text/channel-type.velux.WLANPassword.description + Bridge + + + + + String + + @text/channel-type.velux.products.description + Bridge + + + + + String + + @text/channel-type.velux.scenes.description + Bridge + + + + + String + + @text/channel-type.velux.check.description + Bridge + + + + + + + Rollershutter + + @text/channel-type.velux.position.description + Rollershutter + + + + Switch + + @text/channel-type.velux.state.description + Switch + + + + Rollershutter + + @text/channel-type.velux.limitation.description + Rollershutter + + + + + Switch + + @text/channel-type.velux.action.description + Scene + + + + Switch + + @text/channel-type.velux.silentMode.description + Scene + + + + Rollershutter + + @text/channel-type.velux.vposition.description + Rollershutter + + + + + + + + diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/rollershutter.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/rollershutter.xml new file mode 100644 index 0000000000000..13f678d74de49 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/rollershutter.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + @text/thing-type.velux.rollershutter.description + Rollershutter + + + + + + unique + + + + + + diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/scene.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/scene.xml new file mode 100644 index 0000000000000..d6dfad74ac383 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/scene.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + @text/thing-type.velux.scene.description + Scene + + + + + + unique + + + + + + diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/vshutter.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/vshutter.xml new file mode 100644 index 0000000000000..80ded429820cf --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/vshutter.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + @text/thing-type.velux.vshutter.description + Rollershutter + + + + + + + + + diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/window.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/window.xml new file mode 100644 index 0000000000000..4b67180f61dd8 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/window.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + @text/thing-type.velux.window.description + Window + + + + + + serialNumber + + + + + + diff --git a/bundles/pom.xml b/bundles/pom.xml index 6700dc816c842..42c81b6eef9c7 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -1,5 +1,5 @@ - - + + 4.0.0 @@ -197,6 +197,7 @@ org.openhab.binding.valloxmv org.openhab.binding.vektiva org.openhab.binding.velbus + org.openhab.binding.velux org.openhab.binding.vitotronic org.openhab.binding.volvooncall org.openhab.binding.weatherunderground From 1ce77abf4f0f162ad9942cdbf8cbe2400b920d31 Mon Sep 17 00:00:00 2001 From: Guenther Schreiner Date: Tue, 31 Dec 2019 01:04:41 +0100 Subject: [PATCH 2/9] [velux] Adapted to latest review. Signed-off-by: Guenther Schreiner --- bundles/org.openhab.binding.velux/.classpath | 32 -- bundles/org.openhab.binding.velux/.gitignore | 27 -- bundles/org.openhab.binding.velux/.project | 23 -- bundles/org.openhab.binding.velux/README.md | 298 +++------------ .../doc/conf/items/velux.items | 7 - .../doc/conf/rules/velux.rules | 7 - .../doc/conf/sitemaps/velux.sitemap | 7 - .../doc/conf/things/velux.things | 7 - .../lib/gson-2.2.4.jar | Bin 190418 -> 0 bytes .../bridge/VeluxBridgeGetLimitation.java | 61 ---- .../handler/ChannelActuatorLimitation.java | 239 ------------ .../binding/velux/internal/VeluxBinding.java | 1 - .../velux/internal/VeluxBindingConfig.java | 2 +- .../{ => internal}/VeluxBindingConstants.java | 13 +- .../VeluxBindingProperties.java | 18 +- .../velux/internal/VeluxHandlerFactory.java | 132 ++++--- .../binding/velux/internal/VeluxItemType.java | 33 +- .../velux/internal/VeluxRSBindingConfig.java | 5 +- .../{ => internal}/bridge/VeluxBridge.java | 16 +- .../bridge/VeluxBridgeActuators.java | 47 +-- .../bridge/VeluxBridgeDetectProducts.java | 20 +- .../VeluxBridgeDeviceCheckLostNodes.java | 4 +- .../bridge/VeluxBridgeDeviceStatus.java | 11 +- .../bridge/VeluxBridgeGetFirmware.java | 7 +- .../bridge/VeluxBridgeGetHouseStatus.java | 6 +- .../bridge/VeluxBridgeGetLimitation.java | 104 ++++++ .../bridge/VeluxBridgeInstance.java | 10 +- .../bridge/VeluxBridgeLANConfig.java | 7 +- .../bridge/VeluxBridgeProvider.java | 6 +- .../bridge/VeluxBridgeRunProductCommand.java | 6 +- .../bridge/VeluxBridgeRunScene.java | 4 +- .../bridge/VeluxBridgeScenes.java | 18 +- .../VeluxBridgeSetHouseStatusMonitor.java | 4 +- .../bridge/VeluxBridgeSetLimitation.java | 93 +++++ .../bridge/VeluxBridgeSetSceneVelocity.java | 6 +- .../bridge/VeluxBridgeWLANConfig.java | 7 +- .../bridge/common/BridgeAPI.java | 53 +-- .../common/BridgeCommunicationProtocol.java | 2 +- .../bridge/common/GetDeviceStatus.java | 4 +- .../bridge/common/GetFirmware.java | 4 +- .../bridge/common/GetHouseStatus.java | 2 +- .../bridge/common/GetLANConfig.java | 4 +- .../bridge/common/GetProduct.java | 4 +- .../bridge/common/GetProductLimitation.java | 6 +- .../bridge/common/GetProducts.java | 4 +- .../bridge/common/GetScenes.java | 4 +- .../bridge/common/GetWLANConfig.java | 4 +- .../{ => internal}/bridge/common/Login.java | 2 +- .../{ => internal}/bridge/common/Logout.java | 2 +- .../bridge/common/RunProductCommand.java | 2 +- .../bridge/common/RunProductDiscovery.java | 2 +- .../common/RunProductIdentification.java | 2 +- .../bridge/common/RunProductSearch.java | 2 +- .../bridge/common/RunScene.java | 2 +- .../bridge/common/SetHouseStatusMonitor.java | 2 +- .../bridge/common/SetProductLimitation.java | 54 +++ .../bridge/common/SetSceneVelocity.java | 2 +- .../bridge/common/package-info.java | 2 +- .../bridge/json/JCgetDeviceStatus.java | 24 +- .../bridge/json/JCgetFirmware.java | 19 +- .../bridge/json/JCgetLANConfig.java | 17 +- .../bridge/json/JCgetProducts.java | 25 +- .../bridge/json/JCgetScenes.java | 24 +- .../bridge/json/JCgetWLANConfig.java | 17 +- .../{ => internal}/bridge/json/JClogin.java | 20 +- .../{ => internal}/bridge/json/JClogout.java | 12 +- .../bridge/json/JCrunProductDiscovery.java | 10 +- .../json/JCrunProductIdentification.java | 11 +- .../bridge/json/JCrunProductSearch.java | 16 +- .../bridge/json/JCrunScene.java | 13 +- .../bridge/json/JCsetSceneVelocity.java | 13 +- .../bridge/json/JsonBridgeAPI.java | 50 +-- .../json/JsonBridgeCommunicationProtocol.java | 10 +- .../bridge/json/JsonVeluxBridge.java | 74 ++-- .../bridge/json/package-info.java | 2 +- .../{ => internal}/bridge/package-info.java | 2 +- .../bridge/slip/SCgetDeviceStatus.java | 18 +- .../bridge/slip/SCgetFirmware.java | 18 +- .../bridge/slip/SCgetHouseStatus.java | 18 +- .../bridge/slip/SCgetLANConfig.java | 18 +- .../bridge/slip/SCgetLimitation.java | 124 +++++-- .../bridge/slip/SCgetProduct.java | 26 +- .../bridge/slip/SCgetProducts.java | 26 +- .../bridge/slip/SCgetScenes.java | 20 +- .../bridge/slip/SCgetWLANConfig.java | 14 +- .../{ => internal}/bridge/slip/SClogin.java | 24 +- .../{ => internal}/bridge/slip/SClogout.java | 14 +- .../bridge/slip/SCrunProductCommand.java | 16 +- .../bridge/slip/SCrunProductDiscovery.java | 18 +- .../slip/SCrunProductIdentification.java | 16 +- .../bridge/slip/SCrunProductSearch.java | 14 +- .../bridge/slip/SCrunScene.java | 16 +- .../bridge/slip/SCsetHouseStatusMonitor.java | 16 +- .../internal/bridge/slip/SCsetLimitation.java | 320 ++++++++++++++++ .../bridge/slip/SCsetSceneVelocity.java | 18 +- .../bridge/slip/SlipBridgeAPI.java | 49 +-- .../slip/SlipBridgeCommunicationProtocol.java | 6 +- .../bridge/slip/SlipVeluxBridge.java | 81 ++-- .../bridge/slip/io/Connection.java | 24 +- .../slip/io/DataInputStreamWithTimeout.java | 137 +++++++ .../bridge/slip/io/SSLconnection.java | 25 +- .../bridge/slip/io/package-info.java | 2 +- .../bridge/slip/package-info.java | 2 +- .../bridge/slip/utils}/KLF200Response.java | 6 +- .../bridge/slip/utils}/Packet.java | 27 +- .../bridge/slip/utils}/SlipEncoding.java | 6 +- .../bridge/slip/utils}/SlipRFC1055.java | 8 +- .../bridge/slip/utils}/package-info.java | 2 +- .../config/VeluxBridgeConfiguration.java | 2 +- .../config/VeluxThingConfiguration.java | 37 +- .../velux/internal/development/Threads.java | 80 ++++ .../internal/development/package-info.java | 18 + .../discovery/VeluxDiscoveryService.java | 123 ++++--- .../discovery/package-info.java | 2 +- .../handler/BridgeChannels.java | 32 +- .../handler/ChannelActuatorLimitation.java | 212 +++++++++++ .../handler/ChannelActuatorPosition.java | 28 +- .../handler/ChannelBridgeCheck.java | 13 +- .../handler/ChannelBridgeDoDetection.java | 8 +- .../handler/ChannelBridgeFirmware.java | 6 +- .../handler/ChannelBridgeLANconfig.java | 8 +- .../handler/ChannelBridgeProducts.java | 4 +- .../handler/ChannelBridgeScenes.java | 4 +- .../handler/ChannelBridgeStatus.java | 6 +- .../handler/ChannelBridgeWLANconfig.java | 8 +- .../handler/ChannelHandlerTemplate.java} | 15 +- .../handler/ChannelSceneAction.java | 14 +- .../handler/ChannelSceneSilentmode.java | 12 +- .../handler/ChannelVShutterPosition.java | 14 +- .../handler/ExtendedBaseBridgeHandler.java | 13 +- .../handler/ExtendedBaseThingHandler.java | 2 +- .../{ => internal}/handler/StateUtils.java | 2 +- .../{ => internal}/handler/ThingProperty.java | 41 ++- .../handler/VeluxBindingHandler.java | 120 +++--- .../handler/VeluxBridgeHandler.java | 345 ++++++++++-------- .../{ => internal}/handler/VeluxHandler.java | 23 +- .../{ => internal}/handler/package-info.java | 2 +- .../things/VeluxExistingProducts.java | 24 +- .../things/VeluxExistingScenes.java | 18 +- .../things/VeluxGwFirmware.java | 4 +- .../{ => internal}/things/VeluxGwLAN.java | 4 +- .../{ => internal}/things/VeluxGwState.java | 2 +- .../{ => internal}/things/VeluxGwWLAN.java | 4 +- .../{ => internal}/things/VeluxKLFAPI.java | 4 +- .../{ => internal}/things/VeluxProduct.java | 9 +- .../things/VeluxProductName.java | 7 +- .../things/VeluxProductPosition.java | 13 +- .../things/VeluxProductReference.java | 4 +- .../things/VeluxProductSerialNo.java | 4 +- .../things/VeluxProductState.java | 3 +- .../things/VeluxProductType.java | 4 +- .../things/VeluxProductVelocity.java | 10 +- .../{ => internal}/things/VeluxScene.java | 8 +- .../{ => internal}/things/package-info.java | 2 +- .../velux/internal/utils/Localization.java | 77 +++- .../velux/internal/utils/LoggerFulltrace.java | 88 ----- .../internal/utils/ManifestInformation.java | 11 +- .../main/resources/ESH-INF/config/config.xml | 7 +- .../resources/ESH-INF/i18n/velux.properties | 34 +- .../ESH-INF/i18n/velux_de.properties | 190 ++-------- .../ESH-INF/i18n/velux_dk.properties | 34 +- .../ESH-INF/i18n/velux_nl.properties | 34 +- .../main/resources/ESH-INF/thing/actuator.xml | 11 +- .../main/resources/ESH-INF/thing/binding.xml | 7 +- .../main/resources/ESH-INF/thing/bridge.xml | 8 +- .../main/resources/ESH-INF/thing/channels.xml | 69 ++-- .../resources/ESH-INF/thing/rollershutter.xml | 12 +- .../main/resources/ESH-INF/thing/scene.xml | 9 +- .../main/resources/ESH-INF/thing/vshutter.xml | 8 +- .../main/resources/ESH-INF/thing/window.xml | 10 +- 170 files changed, 2719 insertions(+), 2149 deletions(-) delete mode 100644 bundles/org.openhab.binding.velux/.classpath delete mode 100644 bundles/org.openhab.binding.velux/.gitignore delete mode 100644 bundles/org.openhab.binding.velux/.project delete mode 100644 bundles/org.openhab.binding.velux/lib/gson-2.2.4.jar delete mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeGetLimitation.java delete mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelActuatorLimitation.java rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/VeluxBindingConstants.java (91%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/VeluxBindingProperties.java (83%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/VeluxBridge.java (95%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/VeluxBridgeActuators.java (76%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/VeluxBridgeDetectProducts.java (81%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/VeluxBridgeDeviceCheckLostNodes.java (93%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/VeluxBridgeDeviceStatus.java (92%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/VeluxBridgeGetFirmware.java (94%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/VeluxBridgeGetHouseStatus.java (92%) create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetLimitation.java rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/VeluxBridgeInstance.java (87%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/VeluxBridgeLANConfig.java (95%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/VeluxBridgeProvider.java (90%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/VeluxBridgeRunProductCommand.java (92%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/VeluxBridgeRunScene.java (95%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/VeluxBridgeScenes.java (90%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/VeluxBridgeSetHouseStatusMonitor.java (94%) create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetLimitation.java rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/VeluxBridgeSetSceneVelocity.java (91%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/VeluxBridgeWLANConfig.java (94%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/common/BridgeAPI.java (75%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/common/BridgeCommunicationProtocol.java (96%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/common/GetDeviceStatus.java (91%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/common/GetFirmware.java (91%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/common/GetHouseStatus.java (94%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/common/GetLANConfig.java (91%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/common/GetProduct.java (92%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/common/GetProductLimitation.java (90%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/common/GetProducts.java (91%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/common/GetScenes.java (91%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/common/GetWLANConfig.java (91%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/common/Login.java (96%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/common/Logout.java (94%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/common/RunProductCommand.java (96%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/common/RunProductDiscovery.java (94%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/common/RunProductIdentification.java (95%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/common/RunProductSearch.java (94%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/common/RunScene.java (96%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/common/SetHouseStatusMonitor.java (96%) create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetProductLimitation.java rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/common/SetSceneVelocity.java (95%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/common/package-info.java (90%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/json/JCgetDeviceStatus.java (83%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/json/JCgetFirmware.java (85%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/json/JCgetLANConfig.java (88%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/json/JCgetProducts.java (84%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/json/JCgetScenes.java (88%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/json/JCgetWLANConfig.java (88%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/json/JClogin.java (88%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/json/JClogout.java (88%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/json/JCrunProductDiscovery.java (91%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/json/JCrunProductIdentification.java (92%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/json/JCrunProductSearch.java (86%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/json/JCrunScene.java (88%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/json/JCsetSceneVelocity.java (89%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/json/JsonBridgeAPI.java (79%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/json/JsonBridgeCommunicationProtocol.java (86%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/json/JsonVeluxBridge.java (84%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/json/package-info.java (90%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/package-info.java (92%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/SCgetDeviceStatus.java (87%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/SCgetFirmware.java (88%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/SCgetHouseStatus.java (90%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/SCgetLANConfig.java (86%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/SCgetLimitation.java (59%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/SCgetProduct.java (92%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/SCgetProducts.java (92%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/SCgetScenes.java (89%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/SCgetWLANConfig.java (87%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/SClogin.java (84%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/SClogout.java (84%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/SCrunProductCommand.java (95%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/SCrunProductDiscovery.java (88%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/SCrunProductIdentification.java (91%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/SCrunProductSearch.java (87%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/SCrunScene.java (95%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/SCsetHouseStatusMonitor.java (88%) create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetLimitation.java rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/SCsetSceneVelocity.java (90%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/SlipBridgeAPI.java (80%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/SlipBridgeCommunicationProtocol.java (93%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/SlipVeluxBridge.java (83%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/io/Connection.java (92%) create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/DataInputStreamWithTimeout.java rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/io/SSLconnection.java (91%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/io/package-info.java (89%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/bridge/slip/package-info.java (90%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{bridge/slip/util => internal/bridge/slip/utils}/KLF200Response.java (97%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{bridge/slip/util => internal/bridge/slip/utils}/Packet.java (89%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{bridge/slip/util => internal/bridge/slip/utils}/SlipEncoding.java (95%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{bridge/slip/util => internal/bridge/slip/utils}/SlipRFC1055.java (97%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{bridge/slip/util => internal/bridge/slip/utils}/package-info.java (88%) create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/development/Threads.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/development/package-info.java rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/discovery/VeluxDiscoveryService.java (66%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/discovery/package-info.java (90%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/handler/BridgeChannels.java (63%) create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorLimitation.java rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/handler/ChannelActuatorPosition.java (92%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/handler/ChannelBridgeCheck.java (90%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/handler/ChannelBridgeDoDetection.java (92%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/handler/ChannelBridgeFirmware.java (93%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/handler/ChannelBridgeLANconfig.java (94%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/handler/ChannelBridgeProducts.java (95%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/handler/ChannelBridgeScenes.java (95%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/handler/ChannelBridgeStatus.java (93%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/handler/ChannelBridgeWLANconfig.java (93%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{handler/VeluxChannelHandler.java => internal/handler/ChannelHandlerTemplate.java} (85%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/handler/ChannelSceneAction.java (92%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/handler/ChannelSceneSilentmode.java (91%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/handler/ChannelVShutterPosition.java (93%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/handler/ExtendedBaseBridgeHandler.java (80%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/handler/ExtendedBaseThingHandler.java (97%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/handler/StateUtils.java (98%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/handler/ThingProperty.java (80%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/handler/VeluxBindingHandler.java (55%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/handler/VeluxBridgeHandler.java (65%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/handler/VeluxHandler.java (83%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/handler/package-info.java (90%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/things/VeluxExistingProducts.java (94%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/things/VeluxExistingScenes.java (91%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/things/VeluxGwFirmware.java (91%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/things/VeluxGwLAN.java (95%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/things/VeluxGwState.java (99%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/things/VeluxGwWLAN.java (92%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/things/VeluxKLFAPI.java (99%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/things/VeluxProduct.java (97%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/things/VeluxProductName.java (78%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/things/VeluxProductPosition.java (92%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/things/VeluxProductReference.java (95%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/things/VeluxProductSerialNo.java (98%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/things/VeluxProductState.java (97%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/things/VeluxProductType.java (97%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/things/VeluxProductVelocity.java (91%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/things/VeluxScene.java (94%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/{ => internal}/things/package-info.java (91%) delete mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/LoggerFulltrace.java diff --git a/bundles/org.openhab.binding.velux/.classpath b/bundles/org.openhab.binding.velux/.classpath deleted file mode 100644 index 615608997a6c5..0000000000000 --- a/bundles/org.openhab.binding.velux/.classpath +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/bundles/org.openhab.binding.velux/.gitignore b/bundles/org.openhab.binding.velux/.gitignore deleted file mode 100644 index 052e6417f38dc..0000000000000 --- a/bundles/org.openhab.binding.velux/.gitignore +++ /dev/null @@ -1,27 +0,0 @@ -.antlr* -.idea -.DS_Store -*.iml -npm-debug.log -.build.log - -.metadata/ -bin/ -target/ -src-gen/ -xtend-gen/ - -*/plugin.xml_gen -**/.settings/org.eclipse.* - -bundles/**/src/main/history -features/**/src/main/history -features/**/src/main/feature - -.vscode -.factorypath - -doc/**/*.html -doc/package-list -doc/script.js -doc/stylesheet.css diff --git a/bundles/org.openhab.binding.velux/.project b/bundles/org.openhab.binding.velux/.project deleted file mode 100644 index e49cf15609981..0000000000000 --- a/bundles/org.openhab.binding.velux/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - org.openhab.binding.velux - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - - diff --git a/bundles/org.openhab.binding.velux/README.md b/bundles/org.openhab.binding.velux/README.md index 18be768dff6bf..8d4767a2529cb 100644 --- a/bundles/org.openhab.binding.velux/README.md +++ b/bundles/org.openhab.binding.velux/README.md @@ -35,18 +35,18 @@ Items marked with (\*) are fully implemented. Items marked with (+) have only pa The Velux KLF200 bridge has to be configured with some parameters, at least with the IP address of the bridge. -| Property | Default | Required | Description | -|----------------------|------------------|:--------:|--------------------------------------------------------------| -| ipAddress | | Yes | Hostname or address for accessing the Velux Bridge. | -| protocol | slip | No | Underlying communication protocol (http/https/slip). | -| tcpPort | 51200 | No | TCP port (80 or 51200) for accessing the Velux Bridge. | -| password | velux123 | No | Password for authentication against the Velux Bridge.(\*\*) | -| timeoutMsecs | 1000 | No | Initial Connection timeout in milliseconds. | -| retries | 5 | No | Number of retries during I/O. | -| refreshMsecs | 10000 | No | Refresh interval in milliseconds. | -| isBulkRetrievalEnabled | yes | No | Load all scenes and actuators in one step. | -| isSequentialEnforced | no | No | Enforce Sequential Actuator Control even for long operations.| -| isProtocolTraceEnabled | no | No | Show any protocol interaction (loglevel INFO). | +| Property | Default | Required | Description | +|------------------------|------------------|:--------:|--------------------------------------------------------------| +| ipAddress | | Yes | Hostname or address for accessing the Velux Bridge. | +| protocol | slip | No | Underlying communication protocol (http/https/slip). | +| tcpPort | 51200 | No | TCP port (80 or 51200) for accessing the Velux Bridge. | +| password | velux123 | No | Password for authentication against the Velux Bridge.(\*\*) | +| timeoutMsecs | 1000 | No | Initial Connection timeout in milliseconds. | +| retries | 5 | No | Number of retries during I/O. | +| refreshMsecs | 10000 | No | Refresh interval in milliseconds. | +| isBulkRetrievalEnabled | yes | No | Load all scenes and actuators in one step. | +| isSequentialEnforced | no | No | Enforce Sequential Actuator Control even for long operations.| +| isProtocolTraceEnabled | no | No | Show any protocol interaction (loglevel INFO). | (\*\*) Note: This password is the API password that is printed on the back of the unit. Normally it differs from the password of the web frontend. @@ -119,30 +119,44 @@ Optionally the subtype is enhanced with parameters like the appropriate name of ### Subtype -| Subtype | Item Type | Description | Mastertype | Parameter | -|--------------|---------------|-----------------------------------------------------------------|------------|-----------| -| information | String | Describes the current state of the binding | binding | N/A | -| status | String | Current Bridge State (\*\*\*) | bridge | N/A | -| reload | Switch | Reload information from bridge into binding | bridge | N/A | -| timestamp | Number | Timestamp of last successful device interaction | bridge | N/A | -| doDetection | Switch | Start of the product detection mode | bridge | N/A | -| firmware | String | Software version of the Bridge | bridge | N/A | -| ipAddress | String | IP address of the Bridge | bridge | N/A | -| subnetMask | String | IP subnetmask of the Bridge | bridge | N/A | -| defaultGW | String | IP address of the Default Gateway of the Bridge | bridge | N/A | -| DHCP | Switch | Flag whether automatic IP configuration is enabled | bridge | N/A | -| WLANSSID | String | Name of the wireless network | bridge | N/A | -| WLANPassword | String | WLAN Authentication Password | bridge | N/A | -| products | String | List of all recognized products | bridge | N/A | -| scenes | String | List of all defined scenes | bridge | N/A | -| check | String | Result of the check of current item configuration | bridge | N/A | -| shutter | Rollershutter | Virtual rollershutter as combination of different scenes | bridge | required | -| position | Rollershutter | Position of the IO-Homecontrol'ed device (\*\*\*\*) (\*\*\*\*\*)| actuator | required | -| state | Switch | Position of the IO-Homecontrol'ed device (\*\*\*\*) (\*\*\*\*\*)| actuator | required | -| serial | Rollershutter | IO-Homecontrol'ed device (\*\*\*\*) (\*\*\*\*\*) | actuator | required | -| silentMode | Switch | NOT YET IMPLEMENTED. | actuator | required | -| action | Switch | Activates a set of predefined product settings | scene | required | -| silentMode | Switch | Modification of the silent mode of the defined product settings | scene | required | +| Subtype | Item Type | Description | Mastertype | Parameter | +|--------------|---------------|-----------------------------------------------------------------|--------------|-----------| +| information | String | Describes the current state of the binding | binding | N/A | +| status | String | Current Bridge State (\*\*\*) | bridge | N/A | +| reload | Switch | Reload information from bridge into binding | bridge | N/A | +| timestamp | Number | Timestamp of last successful device interaction | bridge | N/A | +| doDetection | Switch | Start of the product detection mode | bridge | N/A | +| firmware | String | Software version of the Bridge | bridge | N/A | +| ipAddress | String | IP address of the Bridge | bridge | N/A | +| subnetMask | String | IP subnetmask of the Bridge | bridge | N/A | +| defaultGW | String | IP address of the Default Gateway of the Bridge | bridge | N/A | +| DHCP | Switch | Flag whether automatic IP configuration is enabled | bridge | N/A | +| WLANSSID | String | Name of the wireless network | bridge | N/A | +| WLANPassword | String | WLAN Authentication Password | bridge | N/A | +| products | String | List of all recognized products | bridge | N/A | +| scenes | String | List of all defined scenes | bridge | N/A | +| check | String | Result of the check of current item configuration | bridge | N/A | +| shutter | Rollershutter | Virtual rollershutter as combination of different scenes | bridge | required | +|--------------|---------------|-----------------------------------------------------------------|--------------|-----------| +| serial | String | IO-Homecontrol'ed device (\*\*\*\*) (\*\*\*\*\*) | actuator | required | +| position | Rollershutter | Position of the IO-Homecontrol'ed device (\*\*\*\*) | actuator | optional | +| state | Switch | State of the IO-Homecontrol'ed device | actuator | optional | +| limitMinimum | Rollershutter | Minimum position of the IO-Homecontrol'ed device (\*\*\*\*) | actuator | optional | +| limitMaximum | Rollershutter | Maximum position of the IO-Homecontrol'ed device (\*\*\*\*) | actuator | optional | +|--------------|---------------|-----------------------------------------------------------------|--------------|-----------| +| serial | String | IO-Homecontrol'ed device (\*\*\*\*) (\*\*\*\*\*) | window | required | +| position | Rollershutter | Position of the IO-Homecontrol'ed device (\*\*\*\*) | window | optional | +| limitMinimum | Rollershutter | Minimum position of the IO-Homecontrol'ed device (\*\*\*\*) | window | optional | +| limitMaximum | Rollershutter | Maximum position of the IO-Homecontrol'ed device (\*\*\*\*) | window | optional | +|--------------|---------------|-----------------------------------------------------------------|--------------|-----------| +| serial | String | IO-Homecontrol'ed device (\*\*\*\*) (\*\*\*\*\*) | rollershutter| required | +| position | Rollershutter | Position of the IO-Homecontrol'ed device (\*\*\*\*) | rollershutter| optional | +| limitMinimum | Rollershutter | Minimum position of the IO-Homecontrol'ed device (\*\*\*\*) | rollershutter| optional | +| limitMaximum | Rollershutter | Maximum position of the IO-Homecontrol'ed device (\*\*\*\*) | rollershutter| optional | +|--------------|---------------|-----------------------------------------------------------------|--------------|-----------| +| sceneName | String | Defines the scene by name according to registration in KLF200 | scene | required | +| action | Switch | Activates a set of predefined product settings | scene | optional | +| silentMode | Switch | Modification of the silent mode of the defined product settings | scene | optional | Notes: (\*\*\*) The existence of this item triggers the continuous realtime status updates of any Velux item like shutters even if they are manually controlled by other controllers. @@ -169,226 +183,24 @@ The subtype shutter requires an even pair of parameters, each defining the shutt ### Virtual shutter -As the bridge does not support a real rollershutter interaction, this binding provides a virtual rollershutter consisting of different scenes which set a specific shutter level. Therefore the item definition contains multiple pairs of rollershutter levels each followed by a scene name, which leads to this setting. - - -## Full Example for firmware version One - +As the bridge with firmware version one does not support a real rollershutter interaction, this binding provides a virtual rollershutter consisting of different scenes which set a specific shutter level. Therefore the item definition contains multiple pairs of rollershutter levels each followed by a scene name, which leads to this setting. ### Items -``` -// Group for simulating push buttons - -Group:Switch:OR(ON, OFF) gV "PushButton" - -// Velux Scenes - -Switch V_W_S_OPEN "Velux DG Rolladen West open" (gV) { velux="thing=scene;channel=action#V_Shutter_West_000" } -Switch V_W_S_SUNNY "Velux DG Rolladen West sunny" (gV) { velux="thing=scene;channel=action#V_Shutter_West_090" } -Switch V_W_S_CLOSED "Velux DG Rolladen West closed" (gV) { velux="thing=scene;channel=action#V_Shutter_West_100" } - -Switch V_O_S_OPEN "Velux DG Rolladen Ost open" (gV) { velux="thing=scene;channel=action#V_Shutter_Ost_000" } -Switch V_O_S_SUNNY "Velux DG Rolladen Ost sunny" (gV) { velux="thing=scene;channel=action#V_Shutter_Ost_090" } -Switch V_O_S_CLOSED "Velux DG Rolladen Ost closed" (gV) { velux="thing=scene;channel=action#V_Shutter_Ost_100" } - -Switch V_M_S_OPEN "Velux DG Rolladen Mitte open" (gV) { velux="thing=scene;channel=action#V_Shutter_Mitte_000" } -Switch V_M_S_SUNNY "Velux DG Rolladen Mitte sunny" (gV) { velux="thing=scene;channel=action#V_Shutter_Mitte_090" } -Switch V_M_S_CLOSED "Velux DG Rolladen Mitte closed" (gV) { velux="thing=scene;channel=action#V_Shutter_Mitte_100" } - -Switch V_M_W_OPEN "Velux DG Window open" (gV) { velux="thing=scene;channel=action#V_Window_Mitte_000" } -Switch V_M_W_UNLOCKED "Velux DG Window unlocked" (gV) { velux="thing=scene;channel=action#V_Window_Mitte_010" } -Switch V_M_W_CLOSED "Velux DG Window closed" (gV) { velux="thing=scene;channel=action#V_Window_Mitte_100" } - -Switch V_OPEN "Velux DG open" (gV) { velux="thing=scene;channel=action#V_Shutters_000" } -Switch V_SUNNY "Velux DG sunny" (gV) { velux="thing=scene;channel=action#V_Shutters_090" } -Switch V_CLOSED "Velux DG closed" (gV) { velux="thing=scene;channel=action#V_Shutters_100" } - -// Velux Bridge parameters - -Switch V_RELOAD "Reload info from bridge" { velux="thing=bridge;channel=reload" } -String V_STATUS "Status [%s]" { velux="thing=bridge;channel=status" } -String V_TIMESTAMP "Timestamp [%.1f]" { velux="thing=bridge;channel=timestamp" } -String V_CHECK "Velux Config Check [%s]" { velux="thing=bridge;channel=check" } -String V_FIRMWARE "Firmware [%s]" { velux="thing=bridge;channel=firmware" } -String V_CONF_LAN_IP "KLF LAN IP [%s]" { velux="thing=bridge;channel=ipAddress" } -String V_CONF_LAN_SUBNET "KLF LAN Subnet [%s]" { velux="thing=bridge;channel=subnetMask" } -String V_CONF_LAN_GW "KLF LAN Gateway [%s]" { velux="thing=bridge;channel=defaultGW" } -Switch V_CONF_LAN_DHCP "KLF LAN DHCP [%s]" { velux="thing=bridge;channel=DHCP" } -String V_CONF_WLAN_SSID "KLF WLAN SSID [%s]" { velux="thing=bridge;channel=WLANSSID" } -String V_CONF_WLAN_PW "KLF WLAN Password [%s]"{ velux="thing=bridge;channel=WLANPassword" } - - -// Velux Shutters - -Rollershutter V_W_S "Velux DG Rolladen West [%d]" { velux="thing=bridge;channel=shutter#0,V_Shutter_West_000,90,V_Shutter_West_090, 100,V_Shutter_West_100"} -Rollershutter V_O_S "Velux DG Rolladen Ost [%d]" { velux="thing=bridge;channel=shutter#0,V_Shutter_Ost_000,90,V_Shutter_Ost_090,10 0,V_Shutter_Ost_100"} -Rollershutter V_M_S "Velux DG Rolladen Mitte [%d]" { velux="thing=bridge;channel=shutter#0,V_Shutter_Mitte_000,90,V_Shutter_Mitte_09 -0,100,V_Shutter_Mitte_100"} -Rollershutter V_M_W "Velux DG Window Mitte [%d]" { velux="thing=bridge;channel=shutter#0,V_Window_Mitte_000,10,V_Window_Mitte_010, -100,V_Window_Mitte_100"} -``` +[Sample items file for textual configuration](doc/conf/items/velux.items) ### Sitemap -``` -sitemap velux label="Velux Environment" -{ - Frame label="Velux Shutter and Window" { - Switch item=V_W_S - Switch item=V_O_S - Switch item=V_M_S - Switch item=V_M_W - } - Frame label="Velux Bridge" { - Switch item=V_RELOAD - Text item=V_STATUS - Text item=V_TIMESTAMP - Text item=V_CHECK - Text item=V_FIRMWARE - Text item=V_CONF_LAN_IP - Text item=V_CONF_LAN_SUBNET - Text item=V_CONF_LAN_GW - Switch item=V_CONF_LAN_DHCP - Text item=V_CONF_WLAN_SSID - Text item=V_CONF_WLAN_PW - } -} -``` +[Sample sitemaps file for textual configuration](doc/conf/sitemaps/velux.sitemap) ### Rules -``` -/** - * This rule simulates the push button behaviour. - */ -rule "PushButton of group gV" - when - Item gV changed - then - // waiting a second. - Thread::sleep(1000) - // Foreach-Switch-is-ON - gV.allMembers.filter( s | s.state == ON).forEach[i| - // switching OFF - i.sendCommand(OFF) - ] - end -``` - -## Full Example for firmware version Two (text-based configuration) - +[Sample rules file for textual configuration](doc/conf/rules/velux.rules) ### Things -``` -// -// Definition of Velux bridge velux:klf200:home -// - -Bridge velux:klf200:home [ ipAddress="192.168.1.1", tcpPort=51200, password="secret" ] { - -// Velux scenes - - Thing scene windowClosed [ sceneName="V_Window_Mitte_000" ] - Thing scene windowUnlocked [ sceneName="V_Window_Mitte_005" ] - Thing scene windowOpened [ sceneName="V_Window_Mitte_100" ] - -// Velux IO-homecontrol devices - - Thing actuator V_M_W [ serial="43:12:3E:26:0C:1B:00:10" ] - Thing actuator V_M_S [ serial="43:12:14:5A:12:1C:05:5F" ] - Thing actuator V_W_S [ serial="43:12:40:5A:0C:2A:05:64" ] - Thing actuator V_O_S [ serial="43:12:40:5A:0C:23:0A:6E",inverted=true ] - -} - -``` - -### Items - -``` -// Group for simulating push buttons - -Group:Switch:OR(ON, OFF) gV "PushButton" - -// Velux Bridge channels - -String V_BRIDGE_STATUS "Velux Bridge Status [%s]" { channel="velux:klf200:home:status" } -Switch V_BRIDGE_RELOAD "Velux Bridge Reload" (gV) { channel="velux:klf200:home:reload" } -String V_BRIDGE_TIMESTAMP "Velux Bridge Timestamp [%d]" { channel="velux:klf200:home:timestamp" } - -String V_BRIDGE_FIRMWARE "Velux Bridge Firmware version [%s]" { channel="velux:klf200:home:firmware" } -String V_BRIDGE_IPADDRESS "Velux Bridge LAN IP Address" { channel="velux:klf200:home:ipAddress" } -String V_BRIDGE_SUBNETMASK "Velux Bridge LAN IP Subnet Mask" { channel="velux:klf200:home:subnetMask" } -String V_BRIDGE_DEFAULTGW "Velux Bridge LAN Default Gateway" { channel="velux:klf200:home:defaultGW" } -String V_BRIDGE_DHCP "Velux Bridge LAN DHCP Enabled" { channel="velux:klf200:home:DHCP" } -String V_BRIDGE_WLANSSID "Velux Bridge WLAN SSID" { channel="velux:klf200:home:WLANSSID" } -String V_BRIDGE_WLANPASSWD "Velux Bridge WLAN Password" { channel="velux:klf200:home:WLANPassword" } - -Switch V_BRIDGE_DETECTION "Velux Bridge Detection mode" (gV) { channel="velux:klf200:home:doDetection" } -String V_BRIDGE_CHECK "Velux Bridge Check" { channel="velux:klf200:home:check" } -String V_BRIDGE_SCENES "Velux Bridge Scenes" { channel="velux:klf200:home:scenes" } -String V_BRIDGE_PRODUCTS "Velux Bridge Products" { channel="velux:klf200:home:products" } - -// Velux Scene channels - -Switch V_M_W_OPEN "Velux Window open" (gV) { channel="velux:scene:home:windowOpened:action" } -Switch V_M_W_UNLOCKED "Velux Window a little open" (gV) { channel="velux:scene:home:windowUnlocked:action" } -Switch V_M_W_CLOSED "Velux Window closed" (gV) { channel="velux:scene:home:windowClosed:action" } - -// Velux Bridge channel - -Rollershutter RS2 "Velux Rolladen 2 [%d]" { channel="velux:klf200:home:shutter#0,V_Shutter_Ost_000,100,V_Shutter_Ost_100", channel="knx:device:bridge:control:VeluxFenster" } - -// Velux Actuator channels - -Rollershutter V_M_W "DG Fenster Bad [%d]" { channel="velux:klf200:home:V_M_W" } -Rollershutter V_M_S "DG Bad [%d]" { channel="velux:klf200:home:V_M_S" } -Rollershutter V_W_S "DG West [%d]" { channel="velux:klf200:home:V_W_S" } -Rollershutter V_O_S "DG Ost [%d]" { channel="velux:klf200:home:V_O_S" } - -``` - -### Sitemap - -``` -sitemap velux label="Velux Environment" -{ - Frame label="Velux Shutter and Window" { - - Switch item=V_M_W_OPEN - Switch item=V_M_W_UNLOCKED - Switch item=V_M_W_CLOSED - Slider item=V_M_W - } - - Frame label="Velux Bridge Status" { - Text item=V_BRIDGE_STATUS - Text item=V_BRIDGE_TIMESTAMP - Switch item=V_BRIDGE_RELOAD - } - - Frame label="Velux Bridge Status" { - Switch item=V_BRIDGE_DETECTION - Text item=V_BRIDGE_CHECK - Text item=V_BRIDGE_SCENES - Text item=V_BRIDGE_PRODUCTS - } - - Frame label="Velux Bridge Configuration" { - Text item=V_BRIDGE_FIRMWARE - Text item=V_BRIDGE_IPADDRESS - Text item=V_BRIDGE_SUBNETMASK - Text item=V_BRIDGE_DEFAULTGW - Switch item=V_BRIDGE_DHCP - Text item=V_BRIDGE_WLANSSID - Text item=V_BRIDGE_WLANPASSWD - } - -} -``` +[Sample things file for textual configuration](doc/conf/things/velux.things) ## More automation samples diff --git a/bundles/org.openhab.binding.velux/doc/conf/items/velux.items b/bundles/org.openhab.binding.velux/doc/conf/items/velux.items index 3e010fb29a148..6b8ca67bb34cc 100644 --- a/bundles/org.openhab.binding.velux/doc/conf/items/velux.items +++ b/bundles/org.openhab.binding.velux/doc/conf/items/velux.items @@ -8,13 +8,6 @@ * 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 - * - * 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 */ /** diff --git a/bundles/org.openhab.binding.velux/doc/conf/rules/velux.rules b/bundles/org.openhab.binding.velux/doc/conf/rules/velux.rules index 739ed6b3930ee..b60541b1c75f2 100644 --- a/bundles/org.openhab.binding.velux/doc/conf/rules/velux.rules +++ b/bundles/org.openhab.binding.velux/doc/conf/rules/velux.rules @@ -8,13 +8,6 @@ * 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 - * - * 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 */ /** diff --git a/bundles/org.openhab.binding.velux/doc/conf/sitemaps/velux.sitemap b/bundles/org.openhab.binding.velux/doc/conf/sitemaps/velux.sitemap index d25ea058bce6b..f71efbfb89581 100644 --- a/bundles/org.openhab.binding.velux/doc/conf/sitemaps/velux.sitemap +++ b/bundles/org.openhab.binding.velux/doc/conf/sitemaps/velux.sitemap @@ -8,13 +8,6 @@ * 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 - * - * 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 */ /** diff --git a/bundles/org.openhab.binding.velux/doc/conf/things/velux.things b/bundles/org.openhab.binding.velux/doc/conf/things/velux.things index 20ede95546b70..324681e94375e 100644 --- a/bundles/org.openhab.binding.velux/doc/conf/things/velux.things +++ b/bundles/org.openhab.binding.velux/doc/conf/things/velux.things @@ -8,13 +8,6 @@ * 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 - * - * 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 */ /** diff --git a/bundles/org.openhab.binding.velux/lib/gson-2.2.4.jar b/bundles/org.openhab.binding.velux/lib/gson-2.2.4.jar deleted file mode 100644 index 9478253e8cb62cd840a21e1f323b68a507c98d66..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 190418 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

y)d@^y=s9`#39r;>ehppwbYq0U{|$2}4K7 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

-X*@Tz0V@JE<{JVc!OO7(MCcH*%up!yjaDX0y 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 M$1P8rF<>(NAA9kPA^-pY diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeGetLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeGetLimitation.java deleted file mode 100644 index fdb97ec54bba7..0000000000000 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeGetLimitation.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) 2010-2019 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.velux.bridge; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.GetProductLimitation; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The {@link VeluxBridgeGetLimitation} represents a complete set of transactions - * for executing a scene defined on the Velux bridge. - *

- * It provides a method {@link VeluxBridgeGetLimitation#getLimitation} for querying a limitation of an actuators. - * Any parameters are controlled by {@link org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration}. - * - * @see VeluxBridgeProvider - * - * @author Guenther Schreiner - Initial contribution - */ -@NonNullByDefault -public class VeluxBridgeGetLimitation { - private final Logger logger = LoggerFactory.getLogger(VeluxBridgeGetLimitation.class); - - // Class access methods - - /** - * Login into bridge, instruct the bridge to pass a command towards an actuator based - * on a well-prepared environment of a {@link VeluxBridgeProvider}. - * - * @param bridge Initialized Velux bridge handler. - * @param nodeId Number of Actuator to be modified. - * @param limitationMinimum query minimum or maximum. - * @return true if successful, and false otherwise. - */ - public boolean getLimitation(VeluxBridge bridge, int nodeId, boolean limitationMinimum) { - logger.trace("getLimitation(nodeId={}) called.", nodeId); - - boolean success = false; - GetProductLimitation bcp = bridge.bridgeAPI().getProductLimitation(); - if (bcp != null) { - bcp.setActuatorIdAndLimitationType(nodeId, limitationMinimum); - if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { - success = true; - } - } - logger.debug("getLimitation() finished {}.", (success ? "successfully" : "with failure")); - return success; - } - -} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelActuatorLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelActuatorLimitation.java deleted file mode 100644 index 6032f23a50f7d..0000000000000 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelActuatorLimitation.java +++ /dev/null @@ -1,239 +0,0 @@ -/** - * Copyright (c) 2010-2019 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.velux.handler; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.library.types.PercentType; -import org.eclipse.smarthome.core.thing.ChannelUID; -import org.eclipse.smarthome.core.types.Command; -import org.eclipse.smarthome.core.types.State; -import org.openhab.binding.velux.VeluxBindingProperties; -import org.openhab.binding.velux.bridge.common.GetProductLimitation; -import org.openhab.binding.velux.things.VeluxProduct; -import org.openhab.binding.velux.things.VeluxProductPosition; -import org.openhab.binding.velux.things.VeluxProductSerialNo; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Channel-specific retrieval and modification. - *

- * This class implements the Channel position of the Thing actuator: - *

    - *
  • Velux bridgeOpenHAB: - *

    - * Information retrieval by method {@link #handleRefresh}.

  • - *
- *
    - *
  • OpenHAB Event Bus → Velux bridge - *

    - * Sending commands and value updates by method {@link #handleCommand}.

  • - *
- * - * @author Guenther Schreiner - Initial contribution. - */ -@NonNullByDefault -final class ChannelActuatorLimitation extends VeluxChannelHandler { - private static final Logger LOGGER = LoggerFactory.getLogger(ChannelActuatorLimitation.class); - - /* - * ************************ - * ***** Constructors ***** - */ - - // Suppress default constructor for non-instantiability - - private ChannelActuatorLimitation() { - throw new AssertionError(); - } - - /** - * Communication method to retrieve information to update the channel value. - * - * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. - * @param channelId The same item passed as type {@link String} for which a refresh is intended. - * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides - * information for this channel. - * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. - */ - static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, - VeluxBridgeHandler thisBridgeHandler) { - LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler); - State newState = null; - do { // just for common exit - if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { - LOGGER.trace("handleRefresh(): there are some existing products."); - } - if (!ThingProperty.exists(thisBridgeHandler, channelUID, - VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER)) { - LOGGER.trace("handleRefresh(): aborting processing as {} is not set.", - VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER); - break; - } - String actuatorSerial = (String) ThingProperty.getValue(thisBridgeHandler, channelUID, - VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER); - LOGGER.trace("handleRefresh(): actuatorSerial={}", actuatorSerial); - - // Handle value inversion - boolean propertyInverted = false; - if (ThingProperty.exists(thisBridgeHandler, channelUID, - VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) { - propertyInverted = (boolean) ThingProperty.getValue(thisBridgeHandler, channelUID, - VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED); - } - boolean isInverted = propertyInverted || VeluxProductSerialNo.indicatesRevertedValues(actuatorSerial); - LOGGER.trace("handleRefresh(): isInverted={}.", isInverted); - actuatorSerial = VeluxProductSerialNo.cleaned(actuatorSerial); - - if (!thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts - .isRegistered(actuatorSerial)) { - LOGGER.info("handleRefresh(): cannot work on unknown actuator with serial {}.", actuatorSerial); - break; - } - LOGGER.trace("handleRefresh(): fetching actuator for {}.", actuatorSerial); - VeluxProduct thisProduct = thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts - .get(actuatorSerial); - LOGGER.trace("handleRefresh(): found actuator {}.", thisProduct); - - GetProductLimitation bcp = thisBridgeHandler.thisBridge.bridgeAPI().getProductLimitation(); - if (bcp == null) { - LOGGER.trace("handleRefresh(): aborting processing as handler is null."); - break; - } - bcp.setActuatorIdAndLimitationType(thisProduct.getBridgeProductIndex().toInt(), true); - if (thisBridgeHandler.thisBridge.bridgeCommunicate(bcp)) { - int limitation = bcp.getLimitation(); - if (bcp.isCommunicationSuccessful()) { - try { - VeluxProductPosition position = new VeluxProductPosition(limitation); - if (position.isValid()) { - PercentType positionAsPercent = position.getPositionAsPercentType(isInverted); - LOGGER.trace("handleRefresh(): found limitation of actuator at level {}.", - positionAsPercent); - newState = positionAsPercent; - } else { - LOGGER.trace("handleRefresh(): limitation level of actuator is unknown."); - } - } catch (Exception e) { - LOGGER.warn("handleRefresh(): getProducts() exception: {}.", e.getMessage()); - } - } - } - } while (false); // common exit - LOGGER.trace("handleRefresh() returns {}.", newState); - return newState; - } - - /** - * Communication method to update the real world according to the passed channel value (or command). - * - * @param channelUID The item passed as type {@link ChannelUID} for which to following command is addressed to. - * @param channelId The same item passed as type {@link String} for which a refresh is intended. - * @param command The command passed as type {@link Command} for the mentioned item. - * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides - * information for this channel. - * @return newValue ... - */ - static @Nullable Command handleCommand(ChannelUID channelUID, String channelId, Command command, - VeluxBridgeHandler thisBridgeHandler) { - LOGGER.debug("handleCommand({},{},{},{}) called.", channelUID, channelId, command, thisBridgeHandler); - LOGGER.warn("handleCommand(): not yet implemented."); - Command newValue = null; - // do { // just for common exit - // if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { - // LOGGER.trace("handleCommand(): there are some existing products."); - // } - // if (!ThingProperty.exists(thisBridgeHandler, channelUID, - // VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER)) { - // LOGGER.trace("handleCommand(): aborting processing as actuatorSerial is not set."); - // break; - // } - // String actuatorSerial = (String) ThingProperty.getValue(thisBridgeHandler, channelUID, - // VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER); - // LOGGER.trace("handleCommand(): actuatorSerial={}", actuatorSerial); - // - // // Handle value inversion - // boolean propertyInverted = false; - // if (ThingProperty.exists(thisBridgeHandler, channelUID, - // VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) { - // propertyInverted = (boolean) ThingProperty.getValue(thisBridgeHandler, channelUID, - // VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED); - // } - // boolean isInverted = propertyInverted || VeluxProductSerialNo.indicatesRevertedValues(actuatorSerial); - // LOGGER.trace("handleCommand(): isInverted={}.", isInverted); - // actuatorSerial = VeluxProductSerialNo.cleaned(actuatorSerial); - // - // if (!thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts - // .isRegistered(actuatorSerial)) { - // LOGGER.info("handleCommand({},{}): cannot work on unknown actuator: {}.", channelUID.getAsString(), - // command, actuatorSerial); - // break; - // } - // LOGGER.trace("handleCommand(): fetching product for {}.", actuatorSerial); - // VeluxProduct thisProduct = thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts - // .get(actuatorSerial); - // LOGGER.trace("handleCommand(): found product {}.", thisProduct); - // - // VeluxProductPosition targetLevel = VeluxProductPosition.UNKNOWN; - // if (channelId.equals(CHANNEL_ACTUATOR_POSITION)) { - // if ((command instanceof UpDownType) && ((UpDownType) command == UpDownType.UP)) { - // LOGGER.trace("handleCommand(): found UP command."); - // targetLevel = isInverted ? new VeluxProductPosition(PercentType.HUNDRED) - // : new VeluxProductPosition(PercentType.ZERO); - // } else if ((command instanceof UpDownType) && ((UpDownType) command == UpDownType.DOWN)) { - // LOGGER.trace("handleCommand(): found DOWN command."); - // targetLevel = isInverted ? new VeluxProductPosition(PercentType.ZERO) - // : new VeluxProductPosition(PercentType.HUNDRED); - // } else if ((command instanceof StopMoveType) && ((StopMoveType) command == StopMoveType.STOP)) { - // LOGGER.trace("handleCommand(): found STOP command."); - // targetLevel = new VeluxProductPosition(); - // } else if (command instanceof PercentType) { - // LOGGER.trace("handleCommand(): found command of type PercentType."); - // PercentType ptCommand = (PercentType) command; - // if (isInverted) { - // ptCommand = new PercentType(PercentType.HUNDRED.intValue() - ptCommand.intValue()); - // } - // LOGGER.trace("handleCommand(): found command to set level to {}.", ptCommand); - // targetLevel = new VeluxProductPosition(ptCommand); - // } else { - // LOGGER.info("handleCommand({},{}): ignoring command.", channelUID.getAsString(), command); - // break; - // } - // } else { - // if ((command instanceof OnOffType) && ((OnOffType) command == OnOffType.ON)) { - // LOGGER.trace("handleCommand(): found ON command."); - // targetLevel = isInverted ? new VeluxProductPosition(PercentType.HUNDRED) - // : new VeluxProductPosition(PercentType.ZERO); - // } else if ((command instanceof OnOffType) && ((OnOffType) command == OnOffType.OFF)) { - // LOGGER.trace("handleCommand(): found OFF command."); - // targetLevel = isInverted ? new VeluxProductPosition(PercentType.ZERO) - // : new VeluxProductPosition(PercentType.HUNDRED); - // } else { - // LOGGER.info("handleCommand({},{}): ignoring command.", channelUID.getAsString(), command); - // break; - // } - // } - // LOGGER.debug("handleCommand(): sending command with target level {}.", targetLevel); - // new VeluxBridgeRunProductCommand().sendCommand(thisBridgeHandler.thisBridge, - // thisProduct.getBridgeProductIndex().toInt(), targetLevel); - // LOGGER.trace("handleCommand(): The new shutter level will be send through the home monitoring events."); - // - // if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { - // LOGGER.trace("handleCommand(): position of actuators are updated."); - // } - // } while (false); // common exit - return newValue; - } - -} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java index 7292afc5cfea4..d09e4b229a024 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java @@ -19,7 +19,6 @@ import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.velux.VeluxBindingConstants; import org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConfig.java index c216ce46b0059..61890703062bf 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConfig.java @@ -63,7 +63,7 @@ class VeluxBindingConfig { * @param bindingConfig * The optional configuration type of the Velux binding. */ - public VeluxBindingConfig(VeluxItemType bindingItemType, String bindingConfig) { // NO_UCD (use default) + public VeluxBindingConfig(VeluxItemType bindingItemType, String bindingConfig) { logger.trace("VeluxBindingConfig(constructor:{},{}) called.", bindingItemType, bindingConfig); this.bindingItemType = bindingItemType; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/VeluxBindingConstants.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConstants.java similarity index 91% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/VeluxBindingConstants.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConstants.java index 938b5a8af1c5a..9a8968c138132 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/VeluxBindingConstants.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConstants.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux; +package org.openhab.binding.velux.internal; import java.util.Arrays; import java.util.HashSet; @@ -107,8 +107,9 @@ public class VeluxBindingConstants { /** Channel identifier describing the current Bridge State. */ public static final String CHANNEL_BRIDGE_STATUS = "status"; public static final String CHANNEL_BRIDGE_RELOAD = "reload"; - public static final String CHANNEL_BRIDGE_TIMESTAMP = "timestamp"; - public static final String CHANNEL_BRIDGE_IOTIMESTAMP = "ioTimestamp"; + public static final String CHANNEL_BRIDGE_DOWNTIME = "downtime"; + public static final String PROPERTY_BRIDGE_TIMESTAMP_SUCCESS = "connectionSuccess"; + public static final String PROPERTY_BRIDGE_TIMESTAMP_ATTEMPT = "connectionAttempt"; public static final String CHANNEL_BRIDGE_FIRMWARE = "firmware"; public static final String CHANNEL_BRIDGE_IPADDRESS = "ipAddress"; @@ -131,7 +132,8 @@ public class VeluxBindingConstants { public static final String CHANNEL_ACTUATOR_POSITION = "position"; public static final String CHANNEL_ACTUATOR_STATE = "state"; public static final String CHANNEL_ACTUATOR_SILENTMODE = "silentMode"; - public static final String CHANNEL_ACTUATOR_LIMITATION = "limitation"; + public static final String CHANNEL_ACTUATOR_LIMIT_MINIMUM = "limitMinimum"; + public static final String CHANNEL_ACTUATOR_LIMIT_MAXIMUM = "limitMaximum"; // List of all virtual shutter channel ids public static final String CHANNEL_VSHUTTER_POSITION = "vposition"; @@ -141,4 +143,7 @@ public class VeluxBindingConstants { public static final String OUTPUT_VALUE_SEPARATOR = ","; public static final String UNKNOWN = "unknown"; + // Critical issues to be reported will use the following message + public static final String LOGGING_CONTACT = "Please report to maintainer: "; + } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/VeluxBindingProperties.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingProperties.java similarity index 83% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/VeluxBindingProperties.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingProperties.java index c146807355340..5d33388eb7ab8 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/VeluxBindingProperties.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingProperties.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux; +package org.openhab.binding.velux.internal; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -20,20 +20,20 @@ * * This class contains the property identifications: *
    - *
  • {@link #PROPERTY_BINDING_BUNDLEVERSION} for identification of the binding, + *
  • {@link #PROPERTY_BINDING_BUNDLEVERSION} for identification of the binding,
  • *
*
    - *
  • {@link #PROPERTY_SCENE_NAME} for defining the name of a scene, + *
  • {@link #PROPERTY_SCENE_NAME} for defining the name of a scene,
  • *
*
    *
  • {@link #PROPERTY_ACTUATOR_SERIALNUMBER} for defining the serial number of an actuator, a rollershutter and a - * window, - *
  • {@link #PROPERTY_ACTUATOR_NAME} for defining the name of an actuator, a rollershutter and a window, - *
  • {@link #PROPERTY_ACTUATOR_INVERTED} for modifying the value of a Channel, + * window,
  • + *
  • {@link #PROPERTY_ACTUATOR_NAME} for defining the name of an actuator, a rollershutter and a window,
  • + *
  • {@link #PROPERTY_ACTUATOR_INVERTED} for modifying the value of a Channel,
  • *
*
    - *
  • {@link #PROPERTY_VSHUTTER_SCENELEVELS} for defining a virtual shutter. - *
  • {@link #PROPERTY_VSHUTTER_CURRENTLEVEL} for defining a virtual shutter. + *
  • {@link #PROPERTY_VSHUTTER_SCENELEVELS} for defining a virtual shutter.
  • + *
  • {@link #PROPERTY_VSHUTTER_CURRENTLEVEL} for defining a virtual shutter.
  • *
* * @author Guenther Schreiner - Initial contribution @@ -42,6 +42,8 @@ public class VeluxBindingProperties { public static final String PROPERTY_BINDING_BUNDLEVERSION = "bundleVersion"; + public static final String PROPERTY_BINDING_NOOFBRIDGES = "numberOfBridges"; + public static final String PROPERTY_BINDING_NOOFTHINGS = "numberOfThings"; public static final String PROPERTY_SCENE_NAME = "sceneName"; public static final String PROPERTY_SCENE_VELOCITY = "velocity"; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxHandlerFactory.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxHandlerFactory.java index dd633ce6992ba..a8732421877cf 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxHandlerFactory.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxHandlerFactory.java @@ -12,24 +12,30 @@ */ package org.openhab.binding.velux.internal; +import java.util.HashSet; import java.util.Hashtable; +import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.config.discovery.DiscoveryService; +import org.eclipse.smarthome.core.i18n.LocaleProvider; +import org.eclipse.smarthome.core.i18n.TranslationProvider; import org.eclipse.smarthome.core.thing.Bridge; 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.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.discovery.VeluxDiscoveryService; -import org.openhab.binding.velux.handler.VeluxBindingHandler; -import org.openhab.binding.velux.handler.VeluxBridgeHandler; -import org.openhab.binding.velux.handler.VeluxHandler; +import org.openhab.binding.velux.internal.discovery.VeluxDiscoveryService; +import org.openhab.binding.velux.internal.handler.VeluxBindingHandler; +import org.openhab.binding.velux.internal.handler.VeluxBridgeHandler; +import org.openhab.binding.velux.internal.handler.VeluxHandler; +import org.openhab.binding.velux.internal.utils.Localization; import org.osgi.framework.ServiceRegistration; +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; @@ -47,89 +53,119 @@ public class VeluxHandlerFactory extends BaseThingHandlerFactory { // Class internal private @Nullable ServiceRegistration discoveryServiceReg; - private @Nullable VeluxBindingHandler veluxBindingHandler = null; - private Integer veluxBridgeCount = 0; + private Set veluxBindingHandlers = new HashSet(); + private Set veluxBridgeHandlers = new HashSet(); + private Set veluxHandlers = new HashSet(); + + private Localization localization; // Private private void registerDeviceDiscoveryService(VeluxBridgeHandler bridgeHandler) { - VeluxDiscoveryService discoveryService = new VeluxDiscoveryService(bridgeHandler); + VeluxDiscoveryService discoveryService = new VeluxDiscoveryService(bridgeHandler, localization); discoveryServiceReg = bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable()); } private synchronized void unregisterDeviceDiscoveryService() { - logger.trace("unregisterDeviceDiscoveryService({}) called."); - + logger.trace("unregisterDeviceDiscoveryService() called."); if (discoveryServiceReg != null) { discoveryServiceReg.unregister(); discoveryServiceReg = null; } } + private @Nullable ThingHandler createBindingHandler(Thing thing) { + logger.trace("createBindingHandler({}) called for thing named '{}'.", thing.getUID(), thing.getLabel()); + VeluxBindingHandler veluxBindingHandler = new VeluxBindingHandler(thing, localization); + veluxBindingHandlers.add(veluxBindingHandler); + return veluxBindingHandler; + } + + private @Nullable ThingHandler createBridgeHandler(Thing thing) { + logger.trace("createBridgeHandler({}) called for thing named '{}'.", thing.getUID(), thing.getLabel()); + VeluxBridgeHandler veluxBridgeHandler = new VeluxBridgeHandler((Bridge) thing, localization); + veluxBridgeHandlers.add(veluxBridgeHandler); + registerDeviceDiscoveryService(veluxBridgeHandler); + return veluxBridgeHandler; + } + + private @Nullable ThingHandler createThingHandler(Thing thing) { + logger.trace("createThingHandler({}) called for thing named '{}'.", thing.getUID(), thing.getLabel()); + VeluxHandler veluxHandler = new VeluxHandler(thing, localization); + veluxHandlers.add(veluxHandler); + return veluxHandler; + } + + private void updateBindingState() { + veluxBindingHandlers.forEach((VeluxBindingHandler veluxBindingHandler) -> { + veluxBindingHandler.updateBindingState(veluxBridgeHandlers.size(), veluxHandlers.size()); + }); + } + + // Constructor + + @Activate + public VeluxHandlerFactory(final @Reference LocaleProvider localeProvider, + final @Reference TranslationProvider i18nProvider) { + this.localization = new Localization(localeProvider, i18nProvider); + } + // Utility methods @Override public boolean supportsThingType(ThingTypeUID thingTypeUID) { - boolean result = VeluxBindingConstants.SUPPORTED_THINGS_BRIDGE.contains(thingTypeUID) - || VeluxBindingConstants.SUPPORTED_THINGS_ITEMS.contains(thingTypeUID) - || VeluxBindingConstants.SUPPORTED_THINGS_BINDING.contains(thingTypeUID); + boolean result = VeluxBindingConstants.SUPPORTED_THINGS_BINDING.contains(thingTypeUID) + || VeluxBindingConstants.SUPPORTED_THINGS_BRIDGE.contains(thingTypeUID) + || VeluxBindingConstants.SUPPORTED_THINGS_ITEMS.contains(thingTypeUID); logger.trace("supportsThingType({}) called and returns {}.", thingTypeUID, result); return result; } @Override protected @Nullable ThingHandler createHandler(Thing thing) { - logger.trace("createHandler({}) called.", thing.getLabel()); - + ThingHandler resultHandler = null; ThingTypeUID thingTypeUID = thing.getThingTypeUID(); - // Handle Binding creation as 1st choice + // Handle Binding creation if (VeluxBindingConstants.SUPPORTED_THINGS_BINDING.contains(thingTypeUID)) { - logger.trace("createHandler(): Creating a Handler for thing '{}'.", thing.getUID()); - veluxBindingHandler = new VeluxBindingHandler(thing); - return veluxBindingHandler; - } - - else - // Handle Bridge creation as 2nd choice + resultHandler = createBindingHandler(thing); + } else + // Handle Bridge creation if (VeluxBindingConstants.SUPPORTED_THINGS_BRIDGE.contains(thingTypeUID)) { - logger.trace("createHandler(): Creating a VeluxBridgeHandler for thing '{}'.", thing.getUID()); - VeluxBridgeHandler handler = new VeluxBridgeHandler((Bridge) thing); - veluxBridgeCount++; - synchronized (this) { - if (veluxBindingHandler != null) { - veluxBindingHandler.updateNoOfBridges(veluxBridgeCount); - } - } - registerDeviceDiscoveryService(handler); - return handler; - } - - else + resultHandler = createBridgeHandler(thing); + } else // Handle creation of Things behind the Bridge if (VeluxBindingConstants.SUPPORTED_THINGS_ITEMS.contains(thingTypeUID)) { - logger.trace("createHandler(): Creating a VeluxHandler for thing '{}'.", thing.getUID()); - return new VeluxHandler(thing); + resultHandler = createThingHandler(thing); } else { - logger.warn("ThingHandler not found for {}.", thingTypeUID); - return null; + logger.warn("createHandler({}) failed: ThingHandler not found for {}.", thingTypeUID, thing.getLabel()); } + updateBindingState(); + return resultHandler; } @Override protected void removeHandler(ThingHandler thingHandler) { - logger.trace("removeHandler({}) called.", thingHandler.toString()); + ThingTypeUID thingTypeUID = thingHandler.getThing().getThingTypeUID(); - if (thingHandler.getThing().getThingTypeUID().equals(VeluxBindingConstants.THING_TYPE_BRIDGE)) { - veluxBridgeCount--; + // Handle Binding removal + if (VeluxBindingConstants.SUPPORTED_THINGS_BINDING.contains(thingTypeUID)) { + logger.trace("removeHandler() removing information element '{}'.", thingHandler.toString()); + veluxBindingHandlers.remove(thingHandler); + } else + // Handle Bridge removal + if (VeluxBindingConstants.SUPPORTED_THINGS_BRIDGE.contains(thingTypeUID)) { + logger.trace("removeHandler() removing bridge '{}'.", thingHandler.toString()); + veluxBridgeHandlers.remove(thingHandler); unregisterDeviceDiscoveryService(); - synchronized (this) { - if (veluxBindingHandler != null) { - veluxBindingHandler.updateNoOfBridges(veluxBridgeCount); - } - } + } else + // Handle removal of Things behind the Bridge + if (VeluxBindingConstants.SUPPORTED_THINGS_ITEMS.contains(thingTypeUID)) { + logger.trace("removeHandler() removing thing '{}'.", thingHandler.toString()); + veluxHandlers.remove(thingHandler); } + updateBindingState(); super.removeHandler(thingHandler); } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxItemType.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxItemType.java index 474f89fd4548b..9832387ab8492 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxItemType.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxItemType.java @@ -24,7 +24,6 @@ import org.eclipse.smarthome.core.library.items.StringItem; import org.eclipse.smarthome.core.library.items.SwitchItem; import org.eclipse.smarthome.core.thing.ThingTypeUID; -import org.openhab.binding.velux.VeluxBindingConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -83,7 +82,7 @@ public enum VeluxItemType { BINDING_INFORMATION(VeluxBindingConstants.THING_TYPE_BINDING, VeluxBindingConstants.CHANNEL_BINDING_INFORMATION, TypeFlavor.READONLY_VOLATILE_STRING), // BRIDGE_STATUS(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_STATUS, TypeFlavor.READONLY_VOLATILE_STRING), - BRIDGE_TIMESTAMP(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_TIMESTAMP, TypeFlavor.READONLY_VOLATILE_NUMBER), + BRIDGE_DOWNTIME(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_DOWNTIME, TypeFlavor.READONLY_VOLATILE_NUMBER), BRIDGE_RELOAD(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_RELOAD, TypeFlavor.INITIATOR), BRIDGE_DO_DETECTION(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_DO_DETECTION, TypeFlavor.INITIATOR), BRIDGE_FIRMWARE(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_FIRMWARE, TypeFlavor.READONLY_STATIC_STRING), @@ -99,15 +98,16 @@ public enum VeluxItemType { // ACTUATOR_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION, TypeFlavor.MANIPULATOR_SHUTTER), ACTUATOR_STATE(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_STATE, TypeFlavor.MANIPULATOR_SWITCH), -//TODO: waiting for feedback from Velux: API does not work properly. -// ACTUATOR_SILENTMODE(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_SILENTMODE, TypeFlavor.WRITEONLY_VOLATILE_SWITCH), - ACTUATOR_LIMITATION(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_LIMITATION, TypeFlavor.MANIPULATOR_SHUTTER), + ACTUATOR_LIMIT_MINIMUM(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MINIMUM,TypeFlavor.MANIPULATOR_SHUTTER), + ACTUATOR_LIMIT_MAXIMUM(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MAXIMUM,TypeFlavor.MANIPULATOR_SHUTTER), // ROLLERSHUTTER_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_ROLLERSHUTTER,VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION, TypeFlavor.MANIPULATOR_SHUTTER), - ROLLERSHUTTER_LIMITATION(VeluxBindingConstants.THING_TYPE_VELUX_ROLLERSHUTTER,VeluxBindingConstants.CHANNEL_ACTUATOR_LIMITATION,TypeFlavor.MANIPULATOR_SHUTTER), + ROLLERSHUTTER_LIMIT_MINIMUM(VeluxBindingConstants.THING_TYPE_VELUX_ROLLERSHUTTER,VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MINIMUM,TypeFlavor.MANIPULATOR_SHUTTER), + ROLLERSHUTTER_LIMIT_MAXIMUM(VeluxBindingConstants.THING_TYPE_VELUX_ROLLERSHUTTER,VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MAXIMUM,TypeFlavor.MANIPULATOR_SHUTTER), // WINDOW_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_WINDOW, VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION, TypeFlavor.MANIPULATOR_SHUTTER), - WINDOW_LIMITATION(VeluxBindingConstants.THING_TYPE_VELUX_WINDOW, VeluxBindingConstants.CHANNEL_ACTUATOR_LIMITATION, TypeFlavor.MANIPULATOR_SHUTTER), + WINDOW_LIMIT_MINIMUM(VeluxBindingConstants.THING_TYPE_VELUX_WINDOW, VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MINIMUM,TypeFlavor.MANIPULATOR_SHUTTER), + WINDOW_LIMIT_MAXIMUM(VeluxBindingConstants.THING_TYPE_VELUX_WINDOW, VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MAXIMUM,TypeFlavor.MANIPULATOR_SHUTTER), // SCENE_ACTION(VeluxBindingConstants.THING_TYPE_VELUX_SCENE, VeluxBindingConstants.CHANNEL_SCENE_ACTION, TypeFlavor.INITIATOR), SCENE_SILENTMODE(VeluxBindingConstants.THING_TYPE_VELUX_SCENE, VeluxBindingConstants.CHANNEL_SCENE_SILENTMODE, TypeFlavor.WRITEONLY_VOLATILE_SWITCH), @@ -157,7 +157,6 @@ private enum TypeFlavor { * Used to define an UNUSABLE entry. */ UNUSABLE, - } private ThingTypeUID thingIdentifier; @@ -212,7 +211,7 @@ private enum TypeFlavor { this.itemIsWritable = false; this.itemIsExecutable = false; this.itemIsToBeRefreshed = true; - this.itemsRefreshDivider = REFRESH_EACH_MINUTE; + this.itemsRefreshDivider = REFRESH_EVERY_CYCLE; break; case WRITEONLY_VOLATILE_SWITCH: this.itemClass = SwitchItem.class; @@ -265,7 +264,7 @@ private enum TypeFlavor { this.itemIsWritable = false; this.itemIsExecutable = false; this.itemIsToBeRefreshed = false; - this.itemsRefreshDivider = 1; + this.itemsRefreshDivider = REFRESH_ONCE_A_DAY; } } @@ -363,8 +362,7 @@ public int getRefreshDivider() { * * @return veluxItemType of type VeluxItemType describing the appropriate enum. */ - public VeluxItemType getByString(String itemTypeName) { // NO_UCD (unused code) - logger.trace("getByString({}) called.", itemTypeName); + public VeluxItemType getByString(String itemTypeName) { try { return VeluxItemType.valueOf(itemTypeName); } catch (IllegalArgumentException e) { @@ -398,7 +396,7 @@ public static VeluxItemType getByThingAndChannel(ThingTypeUID thingIdentifier, S * * @return veluxItemType of type VeluxItemType describing the appropriate enum. */ - public static String[] getThingIdentifiers() { // NO_UCD (unused code) + public static String[] getThingIdentifiers() { LOGGER.trace("getThingIdentifiers() called."); Set> uniqueSet = new HashSet>(); for (VeluxItemType v : VeluxItemType.values()) { @@ -415,7 +413,7 @@ public static String[] getThingIdentifiers() { // NO_UCD (unused code) * * @return veluxItemType of type VeluxItemType describing the appropriate enum. */ - public static String[] getChannelIdentifiers(ThingTypeUID thingIdentifier) { // NO_UCD (unused code) + public static String[] getChannelIdentifiers(ThingTypeUID thingIdentifier) { LOGGER.trace("getChannelIdentifiers() called."); Set> uniqueSet = new HashSet>(); for (VeluxItemType v : VeluxItemType.values()) { @@ -450,12 +448,11 @@ private static boolean isModulo(int a, int b) { */ public static boolean isToBeRefreshedNow(int refreshCycleCounter, ThingTypeUID thingIdentifier, String channelIdentifier) { - LOGGER.trace("isToBeRefreshedNow({},{},{}) called.", refreshCycleCounter, thingIdentifier, channelIdentifier); - VeluxItemType itemType = getByThingAndChannel(thingIdentifier, channelIdentifier); if (itemType == VeluxItemType.UNKNOWN) { - LOGGER.trace("isToBeRefreshedNow(): returning false, as item is not found."); + LOGGER.warn("isToBeRefreshedNow({},{},{}): returning false, as item is not found.", refreshCycleCounter, + thingIdentifier, channelIdentifier); return false; } @@ -463,7 +460,7 @@ public static boolean isToBeRefreshedNow(int refreshCycleCounter, ThingTypeUID t || (itemType.isToBeRefreshed())) { if ((refreshCycleCounter == REFRESH_CYCLE_FIRST_TIME) || (isModulo(refreshCycleCounter, itemType.getRefreshDivider()))) { - LOGGER.trace("isToBeRefreshedNow(): returning false, as item is to be refreshed, now."); + LOGGER.trace("isToBeRefreshedNow(): returning true, as item is to be refreshed, now."); return true; } else { LOGGER.trace("isToBeRefreshedNow(): returning false, as refresh cycle has not yet come for this item."); diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxRSBindingConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxRSBindingConfig.java index a4ca1e2812f2e..21c7d3f640033 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxRSBindingConfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxRSBindingConfig.java @@ -18,7 +18,6 @@ import java.util.TreeMap; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.VeluxBindingConstants; //import org.openhab.model.item.binding.BindingConfigParseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -132,7 +131,7 @@ public VeluxRSBindingConfig(VeluxItemType bindingItemType, String channelValue, * @param channelValue * The optional configuration type of the Velux binding. */ - public VeluxRSBindingConfig(VeluxItemType bindingItemType, String channelValue) { // NO_UCD (unused code) + public VeluxRSBindingConfig(VeluxItemType bindingItemType, String channelValue) { super(bindingItemType, channelValue); logger.trace("VeluxRSBindingConfig(constructor:{},{}) called.", bindingItemType, channelValue); veluxRollershutterBindingParser(channelValue); @@ -205,7 +204,7 @@ public String getSceneName() { * @return sceneName * A String describing the next scene. */ - public String getSceneName(Integer level) { // NO_UCD (use private) + public String getSceneName(Integer level) { logger.trace("getSceneName({}) called.", level); logger.trace("getSceneName() returning {}.", mapDescending.get(level)); return mapDescending.get(level); diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridge.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridge.java similarity index 95% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridge.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridge.java index 5cc53904f76c4..285470ff668b0 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridge.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridge.java @@ -10,16 +10,16 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge; +package org.openhab.binding.velux.internal.bridge; import java.util.Collections; import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.BridgeAPI; -import org.openhab.binding.velux.bridge.common.BridgeCommunicationProtocol; -import org.openhab.binding.velux.bridge.common.Login; -import org.openhab.binding.velux.bridge.common.Logout; +import org.openhab.binding.velux.internal.bridge.common.BridgeAPI; +import org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol; +import org.openhab.binding.velux.internal.bridge.common.Login; +import org.openhab.binding.velux.internal.bridge.common.Logout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -235,7 +235,7 @@ public synchronized boolean bridgeCommunicate(BridgeCommunicationProtocol commun * * @return timestamp (default zero). */ - public synchronized long lastCommunication() { + public long lastCommunication() { logger.trace("lastCommunication() returns zero."); return 0L; } @@ -249,7 +249,7 @@ public synchronized long lastCommunication() { * * @return timestamp (default zero). */ - public synchronized long lastSuccessfulCommunication() { + public long lastSuccessfulCommunication() { logger.trace("lastSuccessfulCommunication() returns zero."); return 0L; } @@ -261,7 +261,7 @@ public synchronized long lastSuccessfulCommunication() { * For protocol-specific implementations this method has to be overwritten along the inheritance i.e. * with the protocol-specific class implementations. * - * @return bridgeAPI of type {@link org.openhab.binding.velux.bridge.common.BridgeAPI BridgeAPI}. + * @return bridgeAPI of type {@link org.openhab.binding.velux.internal.bridge.common.BridgeAPI BridgeAPI}. */ public abstract BridgeAPI bridgeAPI(); diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeActuators.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeActuators.java similarity index 76% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeActuators.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeActuators.java index ea9519d2b68f7..e4c65d5fa761b 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeActuators.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeActuators.java @@ -10,14 +10,14 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge; +package org.openhab.binding.velux.internal.bridge; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.GetProduct; -import org.openhab.binding.velux.bridge.common.GetProducts; -import org.openhab.binding.velux.things.VeluxExistingProducts; -import org.openhab.binding.velux.things.VeluxKLFAPI; -import org.openhab.binding.velux.things.VeluxProduct; +import org.openhab.binding.velux.internal.bridge.common.GetProduct; +import org.openhab.binding.velux.internal.bridge.common.GetProducts; +import org.openhab.binding.velux.internal.things.VeluxExistingProducts; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI; +import org.openhab.binding.velux.internal.things.VeluxProduct; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,7 +48,8 @@ public class VeluxBridgeActuators { /** * Limitation of Discovery on parts of the System table * - * Whereas the parameter {@link org.openhab.binding.velux.things.VeluxKLFAPI#KLF_SYSTEMTABLE_MAX} represents the + * Whereas the parameter {@link org.openhab.binding.velux.internal.things.VeluxKLFAPI#KLF_SYSTEMTABLE_MAX} + * represents the * maximum size of the system table in general, for speed up of the discovery process * a binding-internal limitation of number of possible devices is introduced. */ @@ -63,7 +64,8 @@ public class VeluxBridgeActuators { *
  • existingProducts ({@link VeluxExistingProducts}). * */ - public class Channel { // NO_UCD (use private) + @NonNullByDefault + public class Channel { public VeluxExistingProducts existingProducts = new VeluxExistingProducts(); } @@ -97,31 +99,24 @@ public Channel getChannel() { /** * Login into bridge, retrieve all products and logout from bridge based * on a well-prepared environment of a {@link VeluxBridgeProvider}. The results - * are stored within {@link org.openhab.binding.velux.things.VeluxExistingProducts + * are stored within {@link org.openhab.binding.velux.internal.things.VeluxExistingProducts * VeluxExistingProducts}. * * @param bridge Initialized Velux bridge (communication) handler. * @return true if successful, and false otherwise. */ - @SuppressWarnings("null") public boolean getProducts(VeluxBridge bridge) { logger.trace("getProducts() called."); GetProducts bcp = bridge.bridgeAPI().getProducts(); - if (!bridge.bridgeInstance.veluxBridgeConfiguration().isBulkRetrievalEnabled || (bcp == null)) { + GetProduct bcpSbS = bridge.bridgeAPI().getProduct(); + if ((bcpSbS != null) && !bridge.bridgeInstance.veluxBridgeConfiguration().isBulkRetrievalEnabled) { logger.trace("getProducts() working on step-by-step retrieval."); - GetProduct bcpSbS = bridge.bridgeAPI().getProduct(); for (int nodeId = 0; nodeId < VeluxKLFAPI.KLF_SYSTEMTABLE_MAX && nodeId < VELUXBINDING_SYSTEMTABLE_MAX; nodeId++) { - logger.trace("getProducts() {}.", new String(new char[80]).replace('\0', '*')); - try { - Thread.sleep(2000); - } catch (InterruptedException ie) { - logger.trace("io() wait interrupted."); - } logger.trace("getProducts() working on product number {}.", nodeId); bcpSbS.setProductId(nodeId); - if ((bridge.bridgeCommunicate(bcpSbS)) && (bcpSbS.isCommunicationSuccessful())) { + if (bridge.bridgeCommunicate(bcpSbS) && bcpSbS.isCommunicationSuccessful()) { VeluxProduct veluxProduct = bcpSbS.getProduct(); if (bcpSbS.isCommunicationSuccessful()) { logger.debug("getProducts() found product {}.", veluxProduct); @@ -131,11 +126,9 @@ public boolean getProducts(VeluxBridge bridge) { } } } - logger.debug("getProducts() finally has found products {}.", channel.existingProducts); - return true; } else { logger.trace("getProducts() working on bulk retrieval."); - if ((bridge.bridgeCommunicate(bcp)) && (bcp.isCommunicationSuccessful())) { + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { for (VeluxProduct product : bcp.getProducts()) { logger.trace("getProducts() found product {} (type {}).", product.getProductName(), product.getProductType()); @@ -147,13 +140,13 @@ public boolean getProducts(VeluxBridge bridge) { channel.existingProducts.update(product); } } - logger.debug("getProducts() finally has found products {}.", channel.existingProducts); - return true; } else { logger.trace("getProducts() finished with failure."); return false; } } + logger.debug("getProducts() finally has found products {}.", channel.existingProducts); + return true; } /** @@ -164,12 +157,12 @@ public boolean getProducts(VeluxBridge bridge) { * @return true if at least one product was found, and false otherwise. */ public boolean autoRefresh(VeluxBridge bridge) { - logger.trace("autoRefresh() called."); - if (channel.existingProducts.getNoMembers() == 0) { + int numberOfActuators = channel.existingProducts.getNoMembers(); + if (numberOfActuators == 0) { logger.trace("autoRefresh(): is about to fetch existing products."); getProducts(bridge); } - return (channel.existingProducts.getNoMembers() > 0); + return (numberOfActuators > 0); } } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeDetectProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDetectProducts.java similarity index 81% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeDetectProducts.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDetectProducts.java index 758c04f37f384..b4b83775fc26a 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeDetectProducts.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDetectProducts.java @@ -10,13 +10,13 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge; +package org.openhab.binding.velux.internal.bridge; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.RunProductDiscovery; -import org.openhab.binding.velux.bridge.common.GetDeviceStatus; -import org.openhab.binding.velux.things.VeluxGwState; -import org.openhab.binding.velux.things.VeluxGwState.VeluxGatewaySubState; +import org.openhab.binding.velux.internal.bridge.common.GetDeviceStatus; +import org.openhab.binding.velux.internal.bridge.common.RunProductDiscovery; +import org.openhab.binding.velux.internal.things.VeluxGwState; +import org.openhab.binding.velux.internal.things.VeluxGwState.VeluxGatewaySubState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,10 +37,6 @@ public class VeluxBridgeDetectProducts { private final Logger logger = LoggerFactory.getLogger(VeluxBridgeDetectProducts.class); - // Type definitions, class-internal variables - - private static long waitMSecs = 2000L; - // Class access methods /** @@ -75,12 +71,6 @@ public boolean detectProducts(VeluxBridge bridge) { } else { logger.info("detectProducts() unknown devicestatus ({}) received.", deviceStatus); } - logger.trace("detectProducts() about to wait {} msecs.", waitMSecs); - try { - Thread.sleep(waitMSecs); - } catch (InterruptedException ie) { - logger.trace("detectProducts() wait interrupted."); - } } } else { logger.trace("detectProducts() activate detection finished with failure."); diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeDeviceCheckLostNodes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceCheckLostNodes.java similarity index 93% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeDeviceCheckLostNodes.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceCheckLostNodes.java index aee96df01c5b0..1a51d05345217 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeDeviceCheckLostNodes.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceCheckLostNodes.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge; +package org.openhab.binding.velux.internal.bridge; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.RunProductSearch; +import org.openhab.binding.velux.internal.bridge.common.RunProductSearch; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeDeviceStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceStatus.java similarity index 92% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeDeviceStatus.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceStatus.java index 3d04630a15e87..c9723d885462f 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeDeviceStatus.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceStatus.java @@ -10,13 +10,13 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge; +package org.openhab.binding.velux.internal.bridge; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.StringType; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.bridge.common.GetDeviceStatus; -import org.openhab.binding.velux.things.VeluxGwState; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.GetDeviceStatus; +import org.openhab.binding.velux.internal.things.VeluxGwState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,10 +48,11 @@ public class VeluxBridgeDeviceStatus { *
  • {@link #gwStateDescription} containing the verbose gateway state. * */ + @NonNullByDefault public class Channel { public boolean isRetrieved = false; public StringType gwState = new StringType(VeluxBindingConstants.UNKNOWN); - public StringType gwStateDescription = new StringType(VeluxBindingConstants.UNKNOWN); // NO_UCD (use private) + public StringType gwStateDescription = new StringType(VeluxBindingConstants.UNKNOWN); } private Channel channel; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeGetFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetFirmware.java similarity index 94% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeGetFirmware.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetFirmware.java index ca5b9c8b2dd74..e8bcf0210f9df 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeGetFirmware.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetFirmware.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge; +package org.openhab.binding.velux.internal.bridge; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.StringType; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.bridge.common.GetFirmware; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.GetFirmware; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,6 +46,7 @@ public class VeluxBridgeGetFirmware { *
  • firmwareVersion (human readable String). * */ + @NonNullByDefault public class Channel { public boolean isRetrieved = false; public StringType firmwareVersion = new StringType(VeluxBindingConstants.UNKNOWN); diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeGetHouseStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetHouseStatus.java similarity index 92% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeGetHouseStatus.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetHouseStatus.java index 6fc9fe7e3fe92..1126f52e3857d 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeGetHouseStatus.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetHouseStatus.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge; +package org.openhab.binding.velux.internal.bridge; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.GetHouseStatus; +import org.openhab.binding.velux.internal.bridge.common.GetHouseStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,7 +46,7 @@ public class VeluxBridgeGetHouseStatus { * @return true if successful or false otherwise. */ public boolean evaluateState(VeluxBridge bridge) { - logger.trace("evaluateState({}) called."); + logger.trace("evaluateState() called."); boolean success = false; GetHouseStatus bcp = bridge.bridgeAPI().getHouseStatus(); diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetLimitation.java new file mode 100644 index 0000000000000..e6dcfb886a8c6 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetLimitation.java @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2010-2019 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.velux.internal.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.GetProductLimitation; +import org.openhab.binding.velux.internal.things.VeluxProductPosition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeGetLimitation} represents a complete set of transactions + * for retrieval of the limitation of an actuator defined on the Velux bridge. + *

    + * It therefore provides the methods + *

      + *
    • {@link VeluxBridgeGetLimitation#getMinimumLimitation} for querying the lower limitation of an actuator,
    • + *
    • {@link VeluxBridgeGetLimitation#getMaximumLimitation} for querying the high limitation of an actuator.
    • + *
    + * + * Any parameters are controlled by {@link org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration}. + * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeGetLimitation { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeGetLimitation.class); + + // Private Objects + + private VeluxProductPosition limitationResult = VeluxProductPosition.UNKNOWN; + + // Class access methods + + /** + * Login into bridge, instruct the bridge to pass a command towards an actuator based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @param nodeId Number of Actuator to be modified. + * @return true if successful, and false otherwise. + */ + public boolean getMinimumLimitation(VeluxBridge bridge, int nodeId) { + logger.trace("getMinimumLimitation(nodeId={}) called.", nodeId); + + boolean success = false; + GetProductLimitation bcp = bridge.bridgeAPI().getProductLimitation(); + if (bcp != null) { + bcp.setActuatorIdAndLimitationType(nodeId, true); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + success = true; + limitationResult = new VeluxProductPosition(bcp.getLimitation()); + } + } + logger.debug("getMinimumLimitation() finished {}.", (success ? "successfully" : "with failure")); + return success; + } + + /** + * Login into bridge, instruct the bridge to pass a command towards an actuator based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @param nodeId Number of Actuator to be modified. + * @return true if successful, and false otherwise. + */ + public boolean getMaximumLimitation(VeluxBridge bridge, int nodeId) { + logger.trace("getMaximumLimitation(nodeId={}) called.", nodeId); + + boolean success = false; + GetProductLimitation bcp = bridge.bridgeAPI().getProductLimitation(); + if (bcp != null) { + bcp.setActuatorIdAndLimitationType(nodeId, false); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + success = true; + limitationResult = new VeluxProductPosition(bcp.getLimitation()); + } + } + logger.debug("getMaximumLimitation() finished {}.", (success ? "successfully" : "with failure")); + return success; + } + + /** + * Return the limitation value. + * + * @return limitationResult of type VeluxProductPosition. + */ + public VeluxProductPosition getLimitation() { + return limitationResult; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeInstance.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeInstance.java similarity index 87% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeInstance.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeInstance.java index 37ffcf342f6d0..76b43fac14c06 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeInstance.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeInstance.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge; +package org.openhab.binding.velux.internal.bridge; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration; -import org.openhab.binding.velux.things.VeluxExistingProducts; -import org.openhab.binding.velux.things.VeluxExistingScenes; +import org.openhab.binding.velux.internal.things.VeluxExistingProducts; +import org.openhab.binding.velux.internal.things.VeluxExistingScenes; /** * This interface is implemented by classes that deal with a specific Velux bridge and its configuration. @@ -53,14 +53,14 @@ public interface VeluxBridgeInstance { public VeluxBridgeConfiguration veluxBridgeConfiguration(); /** - * Information retrieved by {@link org.openhab.binding.velux.bridge.VeluxBridgeActuators#getProducts} + * Information retrieved by {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeActuators#getProducts} * * @return VeluxExistingProducts containing all registered products, or null in case of any error. */ public VeluxExistingProducts existingProducts(); /** - * Information retrieved by {@link org.openhab.binding.velux.bridge.VeluxBridgeScenes#getScenes} + * Information retrieved by {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeScenes#getScenes} * * @return VeluxExistingScenes containing all registered scenes, or null in case of any error. */ diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeLANConfig.java similarity index 95% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeLANConfig.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeLANConfig.java index 155cf3cdcaefc..e5422b2410f7e 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeLANConfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeLANConfig.java @@ -10,13 +10,13 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge; +package org.openhab.binding.velux.internal.bridge; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.library.types.StringType; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.bridge.common.GetLANConfig; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.GetLANConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,6 +50,7 @@ public class VeluxBridgeLANConfig { *
  • enabledDHCP. * */ + @NonNullByDefault public class Channel { public boolean isRetrieved = false; public StringType openHABipAddress = new StringType(VeluxBindingConstants.UNKNOWN); diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeProvider.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeProvider.java similarity index 90% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeProvider.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeProvider.java index 6b077af298184..710bf4bc02315 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeProvider.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeProvider.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge; +package org.openhab.binding.velux.internal.bridge; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.velux.bridge.common.BridgeAPI; -import org.openhab.binding.velux.bridge.common.BridgeCommunicationProtocol; +import org.openhab.binding.velux.internal.bridge.common.BridgeAPI; +import org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol; /** * This interface is implemented by classes that provide general communication with the Velux bridge. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeRunProductCommand.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunProductCommand.java similarity index 92% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeRunProductCommand.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunProductCommand.java index 7ff002512f1c5..ae2e0390e4e1d 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeRunProductCommand.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunProductCommand.java @@ -10,11 +10,11 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge; +package org.openhab.binding.velux.internal.bridge; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.RunProductCommand; -import org.openhab.binding.velux.things.VeluxProductPosition; +import org.openhab.binding.velux.internal.bridge.common.RunProductCommand; +import org.openhab.binding.velux.internal.things.VeluxProductPosition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeRunScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunScene.java similarity index 95% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeRunScene.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunScene.java index 0c7511b32d94c..28925bb55059d 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeRunScene.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunScene.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge; +package org.openhab.binding.velux.internal.bridge; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.RunScene; +import org.openhab.binding.velux.internal.bridge.common.RunScene; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeScenes.java similarity index 90% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeScenes.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeScenes.java index 73d689cda1423..7eadbab313d89 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeScenes.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeScenes.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge; +package org.openhab.binding.velux.internal.bridge; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.GetScenes; -import org.openhab.binding.velux.things.VeluxExistingScenes; -import org.openhab.binding.velux.things.VeluxScene; +import org.openhab.binding.velux.internal.bridge.common.GetScenes; +import org.openhab.binding.velux.internal.things.VeluxExistingScenes; +import org.openhab.binding.velux.internal.things.VeluxScene; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,10 +26,9 @@ *

    * It provides the following methods: *

      - *
    • {@link #getScenes} for retrieval of information. - *
    • {@link #getChannel} for accessing the retrieved information. - *
    • {@link #autoRefresh} for retrieval of information in case of an - * empty list of actuators. + *
    • {@link #getScenes} for retrieval of information.
    • + *
    • {@link #getChannel} for accessing the retrieved information.
    • + *
    • {@link #autoRefresh} for retrieval of information in case of an empty list of actuators.
    • *
    * * @see VeluxScene @@ -50,6 +49,7 @@ public class VeluxBridgeScenes { *
  • existingScenes ({@link VeluxExistingScenes}). * */ + @NonNullByDefault public class Channel { public VeluxExistingScenes existingScenes = new VeluxExistingScenes(); } @@ -83,7 +83,7 @@ public Channel getChannel() { /** * Login into bridge, retrieve all scenes and logout from bridge based * on a well-prepared environment of a {@link VeluxBridgeProvider}. The results - * are stored within a public structure {@link org.openhab.binding.velux.things.VeluxExistingScenes + * are stored within a public structure {@link org.openhab.binding.velux.internal.things.VeluxExistingScenes * VeluxExistingScenes}. * * @param bridge Initialized Velux bridge (communication) handler. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeSetHouseStatusMonitor.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetHouseStatusMonitor.java similarity index 94% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeSetHouseStatusMonitor.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetHouseStatusMonitor.java index ad9b77cabb048..07593ba6699fe 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeSetHouseStatusMonitor.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetHouseStatusMonitor.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge; +package org.openhab.binding.velux.internal.bridge; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.SetHouseStatusMonitor; +import org.openhab.binding.velux.internal.bridge.common.SetHouseStatusMonitor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetLimitation.java new file mode 100644 index 0000000000000..7dd39657a4d79 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetLimitation.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2010-2019 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.velux.internal.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.SetProductLimitation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeSetLimitation} represents a complete set of transactions + * for modifying the limitation of an actuator defined on the Velux bridge. + *

    + * It therefore provides the methods + *

      + *
    • {@link VeluxBridgeSetLimitation#setMinimumLimitation} for modifying the lower limitation of an actuator,
    • + *
    • {@link VeluxBridgeSetLimitation#setMaximumLimitation} for modifying the high limitation of an actuator.
    • + *
    + * Any parameters are controlled by {@link org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration}. + * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeSetLimitation { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeSetLimitation.class); + + // Class access methods + + /** + * Login into bridge, modify the scene parameters and logout from bridge based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @param nodeId Number of Actuator to be modified. + * @param limitationMinimum new value for minimum limit. + * @return true if successful, and false otherwise. + */ + public boolean setMinimumLimitation(VeluxBridge bridge, int nodeId, int limitationMinimum) { + logger.trace("setMinimumLimitation(nodeId={}, limitation={}) called.", nodeId, limitationMinimum); + + SetProductLimitation bcp = bridge.bridgeAPI().setProductLimitation(); + if (bcp == null) { + logger.info("setMinimumLimitation(): aborting processing as there is handler available."); + return false; + } + bcp.setActuatorIdAndMinimumLimitation(nodeId, limitationMinimum); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + logger.trace("setMinimumLimitation() finished successfully."); + return true; + } + logger.trace("setMinimumLimitation() finished with failure."); + return false; + } + + /** + * Login into bridge, modify the scene parameters and logout from bridge based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @param nodeId Number of Actuator to be modified. + * @param limitationMaximum new value for maximum limit. + * @return true if successful, and false otherwise. + */ + public boolean setMaximumLimitation(VeluxBridge bridge, int nodeId, int limitationMaximum) { + logger.trace("setMaximumLimitation(nodeId={}, limitation={}) called.", nodeId, limitationMaximum); + + SetProductLimitation bcp = bridge.bridgeAPI().setProductLimitation(); + if (bcp == null) { + logger.info("setMaximumLimitation(): aborting processing as there is handler available."); + return false; + } + bcp.setActuatorIdAndMaximumLimitation(nodeId, limitationMaximum); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + logger.trace("setMaximumLimitation() finished successfully."); + return true; + } + logger.trace("setMaximumLimitation() finished with failure."); + return false; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeSetSceneVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetSceneVelocity.java similarity index 91% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeSetSceneVelocity.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetSceneVelocity.java index 6cd5f344e366e..e205d45eec476 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeSetSceneVelocity.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetSceneVelocity.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge; +package org.openhab.binding.velux.internal.bridge; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.SetSceneVelocity; +import org.openhab.binding.velux.internal.bridge.common.SetSceneVelocity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,7 +52,7 @@ public boolean setSilentMode(VeluxBridge bridge, int sceneNo, boolean silentMode SetSceneVelocity bcp = bridge.bridgeAPI().setSceneVelocity(); bcp.setMode(sceneNo, silentMode); if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { - logger.info("setSilentMode() finished successfully."); + logger.trace("setSilentMode() finished successfully."); return true; } logger.trace("setSilentMode() finished with failure."); diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeWLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeWLANConfig.java similarity index 94% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeWLANConfig.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeWLANConfig.java index cd03557f0fec3..b03128dbf1ef1 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/VeluxBridgeWLANConfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeWLANConfig.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge; +package org.openhab.binding.velux.internal.bridge; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.StringType; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.bridge.common.GetWLANConfig; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.GetWLANConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,6 +48,7 @@ public class VeluxBridgeWLANConfig { *
  • wlanPassword. * */ + @NonNullByDefault public class Channel { public boolean isRetrieved = false; public StringType openHABwlanSSID = new StringType(VeluxBindingConstants.UNKNOWN); diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/BridgeAPI.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeAPI.java similarity index 75% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/BridgeAPI.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeAPI.java index 10f51ea99c691..a2a3a48ca79ca 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/BridgeAPI.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeAPI.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.common; +package org.openhab.binding.velux.internal.bridge.common; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -43,10 +43,9 @@ *
  • {@link #runProductIdentification} for human-oriented identification a device behind the bridge (i.e. * by winking or switching on-and-off),
  • *
  • {@link #runProductSearch} for searching for lost products on the bridge,
  • - *
  • {@link #runScene} for manipulation of a set of devices behind the bridge which are tied together as - * scene,
  • - *
  • {@link #setHouseStatusMonitor} for activation or deactivation of the house monitoring mode to be - * informed about device state changes recognized by the bridge,
  • + *
  • {@link #runScene} for manipulation of a set of devices behind the bridge which are tied together as scene,
  • + *
  • {@link #setHouseStatusMonitor} for activation or deactivation of the house monitoring mode to be informed about + * device state changes recognized by the bridge,
  • *
  • {@link #setSceneVelocity} for changes the velocity of a scene defined on the bridge (i.e. silent or * fast mode).
  • * @@ -62,40 +61,48 @@ @NonNullByDefault public interface BridgeAPI { - public Login login(); + Login login(); - public Logout logout(); + Logout logout(); - public @Nullable SetHouseStatusMonitor setHouseStatusMonitor(); + @Nullable + SetHouseStatusMonitor setHouseStatusMonitor(); - public @Nullable GetHouseStatus getHouseStatus(); + @Nullable + GetHouseStatus getHouseStatus(); - public RunProductDiscovery runProductDiscovery(); + RunProductDiscovery runProductDiscovery(); - public RunProductSearch runProductSearch(); + RunProductSearch runProductSearch(); - public RunProductIdentification runProductIdentification(); + RunProductIdentification runProductIdentification(); - public GetDeviceStatus getDeviceStatus(); + GetDeviceStatus getDeviceStatus(); - public GetFirmware getFirmware(); + GetFirmware getFirmware(); - public GetLANConfig getLANConfig(); + GetLANConfig getLANConfig(); - public GetWLANConfig getWLANConfig(); + GetWLANConfig getWLANConfig(); - public GetProducts getProducts(); + GetProducts getProducts(); - public @Nullable GetProduct getProduct(); + @Nullable + GetProduct getProduct(); - public @Nullable GetProductLimitation getProductLimitation(); + @Nullable + GetProductLimitation getProductLimitation(); - public @Nullable RunProductCommand runProductCommand(); + @Nullable + SetProductLimitation setProductLimitation(); - public GetScenes getScenes(); + @Nullable + RunProductCommand runProductCommand(); - public SetSceneVelocity setSceneVelocity(); + GetScenes getScenes(); - public RunScene runScene(); + SetSceneVelocity setSceneVelocity(); + + RunScene runScene(); } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/BridgeCommunicationProtocol.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeCommunicationProtocol.java similarity index 96% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/BridgeCommunicationProtocol.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeCommunicationProtocol.java index 2f4a056d73a7f..fbd0d5c3ef9e3 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/BridgeCommunicationProtocol.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeCommunicationProtocol.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.common; +package org.openhab.binding.velux.internal.bridge.common; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetDeviceStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetDeviceStatus.java similarity index 91% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetDeviceStatus.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetDeviceStatus.java index d3b5beb7b63a5..9a4b4243772c6 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetDeviceStatus.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetDeviceStatus.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.common; +package org.openhab.binding.velux.internal.bridge.common; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.things.VeluxGwState; +import org.openhab.binding.velux.internal.things.VeluxGwState; /** * Common bridge communication message scheme supported by the Velux bridge. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetFirmware.java similarity index 91% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetFirmware.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetFirmware.java index 53cd3517b1faa..728075fddc77a 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetFirmware.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetFirmware.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.common; +package org.openhab.binding.velux.internal.bridge.common; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.things.VeluxGwFirmware; +import org.openhab.binding.velux.internal.things.VeluxGwFirmware; /** * Common bridge communication message scheme supported by the Velux bridge. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetHouseStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetHouseStatus.java similarity index 94% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetHouseStatus.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetHouseStatus.java index f0f8058376391..eb1207be9d78c 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetHouseStatus.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetHouseStatus.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.common; +package org.openhab.binding.velux.internal.bridge.common; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetLANConfig.java similarity index 91% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetLANConfig.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetLANConfig.java index b357656d8625f..9ebdb032273bf 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetLANConfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetLANConfig.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.common; +package org.openhab.binding.velux.internal.bridge.common; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.things.VeluxGwLAN; +import org.openhab.binding.velux.internal.things.VeluxGwLAN; /** * Common bridge communication message scheme supported by the Velux bridge. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetProduct.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProduct.java similarity index 92% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetProduct.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProduct.java index 6f430c131318f..d133c69538f92 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetProduct.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProduct.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.common; +package org.openhab.binding.velux.internal.bridge.common; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.things.VeluxProduct; +import org.openhab.binding.velux.internal.things.VeluxProduct; /** * Common bridge communication message scheme supported by the Velux bridge. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetProductLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProductLimitation.java similarity index 90% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetProductLimitation.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProductLimitation.java index a2c007c288975..915f8b47deaab 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetProductLimitation.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProductLimitation.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.common; +package org.openhab.binding.velux.internal.bridge.common; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -37,9 +37,9 @@ public abstract class GetProductLimitation implements BridgeCommunicationProtoco * Set the intended node identifier to be queried * * @param nodeId Gateway internal node identifier (zero to 199). - * @param limitationMinimum Minimum or Maximum. + * @param getLimitationMinimum true, if we query for Minimum. */ - public abstract void setActuatorIdAndLimitationType(int nodeId, boolean limitationMinimum); + public abstract void setActuatorIdAndLimitationType(int nodeId, boolean getLimitationMinimum); /** * Retrieval of information about the selected product diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProducts.java similarity index 91% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetProducts.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProducts.java index 1105ce943142b..6b18c8e872b59 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetProducts.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProducts.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.common; +package org.openhab.binding.velux.internal.bridge.common; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.things.VeluxProduct; +import org.openhab.binding.velux.internal.things.VeluxProduct; /** * Common bridge communication message scheme supported by the Velux bridge. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetScenes.java similarity index 91% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetScenes.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetScenes.java index c0518664fb6fc..5d5391c86cdf4 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetScenes.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetScenes.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.common; +package org.openhab.binding.velux.internal.bridge.common; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.things.VeluxScene; +import org.openhab.binding.velux.internal.things.VeluxScene; /** * Common bridge communication message scheme supported by the Velux bridge. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetWLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetWLANConfig.java similarity index 91% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetWLANConfig.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetWLANConfig.java index 441232adaf4c2..b2bb33fa8d805 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/GetWLANConfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetWLANConfig.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.common; +package org.openhab.binding.velux.internal.bridge.common; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.things.VeluxGwWLAN; +import org.openhab.binding.velux.internal.things.VeluxGwWLAN; /** * Common bridge communication message scheme supported by the Velux bridge. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/Login.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Login.java similarity index 96% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/Login.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Login.java index a8d7cc2651987..52ad72e9a6ad2 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/Login.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Login.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.common; +package org.openhab.binding.velux.internal.bridge.common; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/Logout.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Logout.java similarity index 94% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/Logout.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Logout.java index 7af29948ac675..905ba230098c1 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/Logout.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Logout.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.common; +package org.openhab.binding.velux.internal.bridge.common; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductCommand.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductCommand.java similarity index 96% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductCommand.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductCommand.java index 636086e2ddf01..bcca33381090e 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductCommand.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductCommand.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.common; +package org.openhab.binding.velux.internal.bridge.common; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductDiscovery.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductDiscovery.java similarity index 94% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductDiscovery.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductDiscovery.java index 94fa3c91aeeeb..757000021c13e 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductDiscovery.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductDiscovery.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.common; +package org.openhab.binding.velux.internal.bridge.common; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductIdentification.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductIdentification.java similarity index 95% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductIdentification.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductIdentification.java index 56571208722b7..3e3f8888c79fb 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductIdentification.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductIdentification.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.common; +package org.openhab.binding.velux.internal.bridge.common; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductSearch.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductSearch.java similarity index 94% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductSearch.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductSearch.java index f37989c8353e6..80d7a37d1e9b4 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunProductSearch.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductSearch.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.common; +package org.openhab.binding.velux.internal.bridge.common; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunScene.java similarity index 96% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunScene.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunScene.java index 31cc7166b73b5..3f8e97d7fad54 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/RunScene.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunScene.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.common; +package org.openhab.binding.velux.internal.bridge.common; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/SetHouseStatusMonitor.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetHouseStatusMonitor.java similarity index 96% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/SetHouseStatusMonitor.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetHouseStatusMonitor.java index e7d115f6fd46b..f36b85a75d09a 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/SetHouseStatusMonitor.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetHouseStatusMonitor.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.common; +package org.openhab.binding.velux.internal.bridge.common; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetProductLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetProductLimitation.java new file mode 100644 index 0000000000000..98187c32911cc --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetProductLimitation.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2010-2019 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.velux.internal.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

    + * Message semantic will be defined by the implementations according to the different comm paths. + *

    + * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

      + *
    • {@link #setActuatorIdAndMinimumLimitation} for defining the intended actuator and the minimum limitation + * value,
    • + *
    • {@link #setActuatorIdAndMaximumLimitation} for defining the intended actuator and the maximum limitation + * value.
    • + *
    + * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class SetProductLimitation implements BridgeCommunicationProtocol { + + /** + * Set the intended node identifier to be queried + * + * @param nodeId Gateway internal node identifier (zero to 199). + * @param limitationMinimum Minimum Restriction value. + */ + public abstract void setActuatorIdAndMinimumLimitation(int nodeId, int limitationMinimum); + + /** + * Set the intended node identifier to be queried + * + * @param nodeId Gateway internal node identifier (zero to 199). + * @param limitationMaximum Maximum Restriction value. + */ + public abstract void setActuatorIdAndMaximumLimitation(int nodeId, int limitationMaximum); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/SetSceneVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetSceneVelocity.java similarity index 95% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/SetSceneVelocity.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetSceneVelocity.java index 1d2bbff39a0f5..b4036d490269c 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/SetSceneVelocity.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetSceneVelocity.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.common; +package org.openhab.binding.velux.internal.bridge.common; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/package-info.java similarity index 90% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/package-info.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/package-info.java index 92c0d7d95fc39..ccae7199ad06c 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/common/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/package-info.java @@ -15,4 +15,4 @@ * * @author Guenther Schreiner - Initial contribution */ -package org.openhab.binding.velux.bridge.common; +package org.openhab.binding.velux.internal.bridge.common; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetDeviceStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetDeviceStatus.java similarity index 83% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetDeviceStatus.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetDeviceStatus.java index 3911b6a38fbad..2aafbe7a378a5 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetDeviceStatus.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetDeviceStatus.java @@ -10,18 +10,18 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.json; +package org.openhab.binding.velux.internal.bridge.json; import java.util.HashMap; import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.bridge.common.GetDeviceStatus; -import org.openhab.binding.velux.things.VeluxGwState; -import org.openhab.binding.velux.things.VeluxGwState.VeluxGatewayState; -import org.openhab.binding.velux.things.VeluxGwState.VeluxGatewaySubState; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.GetDeviceStatus; +import org.openhab.binding.velux.internal.things.VeluxGwState; +import org.openhab.binding.velux.internal.things.VeluxGwState.VeluxGatewayState; +import org.openhab.binding.velux.internal.things.VeluxGwState.VeluxGatewaySubState; /** * Specific bridge communication message supported by the Velux bridge. @@ -30,8 +30,8 @@ *

    * * It defines information how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the {@link org.openhab.binding.velux.bridge.json.JsonBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeCommunicationProtocol * BridgeCommunicationProtocol}. * * @author Guenther Schreiner - Initial contribution. @@ -50,7 +50,7 @@ class JCgetDeviceStatus extends GetDeviceStatus implements JsonBridgeCommunicati */ /** - * Bridge I/O Request message used by {@link org.openhab.binding.velux.bridge.json.JsonVeluxBridge JsonVeluxBridge} + * Bridge I/O Request message used by {@link org.openhab.binding.velux.internal.bridge.json.JsonVeluxBridge JsonVeluxBridge} * for serializing. * * Resulting JSON: @@ -62,6 +62,7 @@ class JCgetDeviceStatus extends GetDeviceStatus implements JsonBridgeCommunicati * NOTE: the gateway software is extremely sensitive to this exact JSON structure. * Any modifications (like omitting empty params) will lead to an gateway error. */ + @NonNullByDefault private static class Request { @SuppressWarnings("unused") @@ -92,6 +93,7 @@ public Request() { * } * */ + @NonNullByDefault private static class Response { @SuppressWarnings("unused") private String token = VeluxBindingConstants.UNKNOWN; @@ -155,9 +157,9 @@ public VeluxGwState getState() { String deviceStatus = this.getDeviceStatus(); byte stateValue = (byte) VeluxGatewayState.GW_S_GWM.getStateValue(); byte subStateValue; - if (deviceStatus.equals("discovering")) { + if ("discovering".equals(deviceStatus)) { subStateValue = (byte) VeluxGatewaySubState.GW_SS_P1.getStateValue(); - } else if (deviceStatus.equals("IDLE")) { + } else if ("IDLE".equals(deviceStatus)) { subStateValue = (byte) VeluxGatewaySubState.GW_SS_IDLE.getStateValue(); } else { subStateValue = (byte) VeluxGatewaySubState.GW_SS_P2.getStateValue(); diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetFirmware.java similarity index 85% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetFirmware.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetFirmware.java index 8422fc1551f72..95239620b1782 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetFirmware.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetFirmware.java @@ -10,15 +10,15 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.json; +package org.openhab.binding.velux.internal.bridge.json; import java.util.HashMap; import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.bridge.common.GetFirmware; -import org.openhab.binding.velux.things.VeluxGwFirmware; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.GetFirmware; +import org.openhab.binding.velux.internal.things.VeluxGwFirmware; /** * Specific bridge communication message supported by the Velux bridge. @@ -27,8 +27,8 @@ *

    * * It defines information how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the {@link org.openhab.binding.velux.bridge.json.JsonBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeCommunicationProtocol * BridgeCommunicationProtocol}. * * @author Guenther Schreiner - Initial contribution. @@ -47,7 +47,7 @@ class JCgetFirmware extends GetFirmware implements JsonBridgeCommunicationProtoc */ /** - * Bridge I/O Request message used by {@link org.openhab.binding.velux.bridge.json.JsonVeluxBridge JsonVeluxBridge} + * Bridge I/O Request message used by {@link org.openhab.binding.velux.internal.bridge.json.JsonVeluxBridge JsonVeluxBridge} * for serializing. *

    * Resulting JSON: @@ -56,6 +56,7 @@ class JCgetFirmware extends GetFirmware implements JsonBridgeCommunicationProtoc * {"action":"getFirmware","params":{}} * */ + @NonNullByDefault private static class Request { @SuppressWarnings("unused") @@ -75,6 +76,7 @@ public Request() { *

    * Used within structure {@link JCgetFirmware} to describe the software of the Bridge. */ + @NonNullByDefault private static class BCfirmwareVersion { /* * "version": "0.1.1.0.41.0" @@ -83,7 +85,7 @@ private static class BCfirmwareVersion { } /** - * Bridge I/O Response message used by {@link org.openhab.binding.velux.bridge.json.JsonVeluxBridge} for + * Bridge I/O Response message used by {@link org.openhab.binding.velux.internal.bridge.json.JsonVeluxBridge} for * deserializing with including component access methods *

    * Expected JSON (sample): @@ -98,6 +100,7 @@ private static class BCfirmwareVersion { * } * */ + @NonNullByDefault private static class Response { @SuppressWarnings("unused") private String token = VeluxBindingConstants.UNKNOWN; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetLANConfig.java similarity index 88% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetLANConfig.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetLANConfig.java index 03bcad2b2309b..b385850bfc710 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetLANConfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetLANConfig.java @@ -10,16 +10,16 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.json; +package org.openhab.binding.velux.internal.bridge.json; import java.util.HashMap; import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.bridge.common.BridgeCommunicationProtocol; -import org.openhab.binding.velux.bridge.common.GetLANConfig; -import org.openhab.binding.velux.things.VeluxGwLAN; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol; +import org.openhab.binding.velux.internal.bridge.common.GetLANConfig; +import org.openhab.binding.velux.internal.things.VeluxGwLAN; /** * Specific bridge communication message supported by the Velux bridge. @@ -28,8 +28,8 @@ *

    * * It defines information how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the {@link org.openhab.binding.velux.bridge.json.JsonBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeCommunicationProtocol * BridgeCommunicationProtocol}. * * @author Guenther Schreiner - Initial contribution. @@ -57,6 +57,7 @@ class JCgetLANConfig extends GetLANConfig implements BridgeCommunicationProtocol * {"action":"get","params":{}} * */ + @NonNullByDefault private static class Request { @SuppressWarnings("unused") @@ -80,6 +81,7 @@ public Request() { * {"ipAddress":"192.168.45.9","subnetMask":"255.255.255.0","defaultGateway":"192.168.45.129","dhcp":false} * */ + @NonNullByDefault private static class BCLANConfig { private String ipAddress = VeluxBindingConstants.UNKNOWN; private String subnetMask = VeluxBindingConstants.UNKNOWN; @@ -109,6 +111,7 @@ public String toString() { * } * */ + @NonNullByDefault private static class Response { @SuppressWarnings("unused") private String token = VeluxBindingConstants.UNKNOWN; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetProducts.java similarity index 84% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetProducts.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetProducts.java index 2a9b5f1c8c612..f89eeeee8bd24 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetProducts.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetProducts.java @@ -10,18 +10,18 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.json; +package org.openhab.binding.velux.internal.bridge.json; import java.util.HashMap; import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.bridge.common.GetProducts; -import org.openhab.binding.velux.things.VeluxProduct; -import org.openhab.binding.velux.things.VeluxProduct.ProductBridgeIndex; -import org.openhab.binding.velux.things.VeluxProductName; -import org.openhab.binding.velux.things.VeluxProductType; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.GetProducts; +import org.openhab.binding.velux.internal.things.VeluxProduct; +import org.openhab.binding.velux.internal.things.VeluxProductName; +import org.openhab.binding.velux.internal.things.VeluxProductType; +import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex; /** * Specific bridge communication message supported by the Velux bridge. @@ -30,8 +30,8 @@ *

    * * It defines information how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the {@link org.openhab.binding.velux.bridge.json.JsonBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeCommunicationProtocol * BridgeCommunicationProtocol}. * * @author Guenther Schreiner - Initial contribution. @@ -61,6 +61,7 @@ class JCgetProducts extends GetProducts implements JsonBridgeCommunicationProtoc * ] * */ + @NonNullByDefault private class BCproduct { private String name = VeluxBindingConstants.UNKNOWN; @SuppressWarnings("unused") @@ -74,7 +75,7 @@ private class BCproduct { } /** - * Bridge I/O Request message used by {@link org.openhab.binding.velux.bridge.json.JsonVeluxBridge JsonVeluxBridge} + * Bridge I/O Request message used by {@link org.openhab.binding.velux.internal.bridge.json.JsonVeluxBridge JsonVeluxBridge} * for serializing. *

    * Resulting JSON: @@ -83,6 +84,7 @@ private class BCproduct { * {"action":"get","params":{}} * */ + @NonNullByDefault private static class Request { @SuppressWarnings("unused") @@ -98,7 +100,7 @@ public Request() { } /** - * Bridge I/O Response message used by {@link org.openhab.binding.velux.bridge.VeluxBridge VeluxBridge} for + * Bridge I/O Response message used by {@link org.openhab.binding.velux.internal.bridge.VeluxBridge VeluxBridge} for * deserialization with including component access methods *

    * Expected JSON (sample): @@ -124,6 +126,7 @@ public Request() { * } * */ + @NonNullByDefault private static class Response { @SuppressWarnings("unused") private String token = VeluxBindingConstants.UNKNOWN; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetScenes.java similarity index 88% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetScenes.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetScenes.java index 36a011e4e9d13..dc2ae33d133f2 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetScenes.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetScenes.java @@ -10,18 +10,18 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.json; +package org.openhab.binding.velux.internal.bridge.json; import java.util.HashMap; import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.bridge.common.GetScenes; -import org.openhab.binding.velux.things.VeluxProductName; -import org.openhab.binding.velux.things.VeluxProductReference; -import org.openhab.binding.velux.things.VeluxProductState; -import org.openhab.binding.velux.things.VeluxScene; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.GetScenes; +import org.openhab.binding.velux.internal.things.VeluxProductName; +import org.openhab.binding.velux.internal.things.VeluxProductReference; +import org.openhab.binding.velux.internal.things.VeluxProductState; +import org.openhab.binding.velux.internal.things.VeluxScene; /** * Specific bridge communication message supported by the Velux bridge. @@ -30,8 +30,8 @@ *

    * * It defines information how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the {@link org.openhab.binding.velux.bridge.json.JsonBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeCommunicationProtocol * BridgeCommunicationProtocol}. * * @author Guenther Schreiner - Initial contribution. @@ -59,6 +59,7 @@ class JCgetScenes extends GetScenes implements JsonBridgeCommunicationProtocol { * "status": 0 * */ + @NonNullByDefault private static class BCproductState { private int typeId; private String name = VeluxBindingConstants.UNKNOWN; @@ -90,6 +91,7 @@ private static class BCproductState { * }, * */ + @NonNullByDefault private static class BCscene { private String name = VeluxBindingConstants.UNKNOWN; private int id; @@ -98,7 +100,7 @@ private static class BCscene { } /** - * Bridge I/O Request message used by {@link org.openhab.binding.velux.bridge.json.JsonVeluxBridge JsonVeluxBridge} + * Bridge I/O Request message used by {@link org.openhab.binding.velux.internal.bridge.json.JsonVeluxBridge JsonVeluxBridge} * for serializing. *

    * Resulting JSON: @@ -107,6 +109,7 @@ private static class BCscene { * {"action":"get","params":{}} * */ + @NonNullByDefault private static class Request { @SuppressWarnings("unused") @@ -146,6 +149,7 @@ public Request() { * } * */ + @NonNullByDefault private static class Response { @SuppressWarnings("unused") private String token = VeluxBindingConstants.UNKNOWN; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetWLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetWLANConfig.java similarity index 88% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetWLANConfig.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetWLANConfig.java index 79f1f9d2f248a..0c6ea2414b36b 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCgetWLANConfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetWLANConfig.java @@ -10,15 +10,15 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.json; +package org.openhab.binding.velux.internal.bridge.json; import java.util.HashMap; import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.bridge.common.GetWLANConfig; -import org.openhab.binding.velux.things.VeluxGwWLAN; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.GetWLANConfig; +import org.openhab.binding.velux.internal.things.VeluxGwWLAN; /** * Specific bridge communication message supported by the Velux bridge. @@ -27,8 +27,8 @@ *

    * * It defines information how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the {@link org.openhab.binding.velux.bridge.json.JsonBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeCommunicationProtocol * BridgeCommunicationProtocol}. * * @author Guenther Schreiner - Initial contribution. @@ -47,7 +47,7 @@ class JCgetWLANConfig extends GetWLANConfig implements JsonBridgeCommunicationPr */ /** - * Bridge I/O Request message used by {@link org.openhab.binding.velux.bridge.json.JsonVeluxBridge JsonVeluxBridge} + * Bridge I/O Request message used by {@link org.openhab.binding.velux.internal.bridge.json.JsonVeluxBridge JsonVeluxBridge} * for serializing. *

    * Resulting JSON: @@ -56,6 +56,7 @@ class JCgetWLANConfig extends GetWLANConfig implements JsonBridgeCommunicationPr * {"action":"wifi","params":{}} * */ + @NonNullByDefault private static class Request { @SuppressWarnings("unused") @@ -79,6 +80,7 @@ public Request() { * {"password":"Esf56mxqFY","name":"VELUX_KLF_847C"} * */ + @NonNullByDefault private static class BCWLANConfig { private String password = VeluxBindingConstants.UNKNOWN; @@ -107,6 +109,7 @@ public String toString() { * } * */ + @NonNullByDefault private static class Response { @SuppressWarnings("unused") private String token = VeluxBindingConstants.UNKNOWN; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JClogin.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JClogin.java similarity index 88% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JClogin.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JClogin.java index 164f86d3f51d7..d9ec484b96bc3 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JClogin.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JClogin.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.json; +package org.openhab.binding.velux.internal.bridge.json; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.bridge.common.Login; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.Login; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,8 +26,8 @@ *

    * * It defines information how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the {@link org.openhab.binding.velux.bridge.json.JsonBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeCommunicationProtocol * BridgeCommunicationProtocol}. * * @author Guenther Schreiner - Initial contribution. @@ -45,6 +45,7 @@ class JClogin extends Login implements JsonBridgeCommunicationProtocol { /* * Message Objects */ + @NonNullByDefault private static class ParamsLogin { @SuppressWarnings("unused") private String password = VeluxBindingConstants.UNKNOWN; @@ -60,6 +61,7 @@ private static class ParamsLogin { * {"action":"login","params":{"password":"PASSWORD"}} * */ + @NonNullByDefault private static class Request { @SuppressWarnings("unused") @@ -81,6 +83,7 @@ public Request() { * '{"token": "PHPnfLda71xfGlxoYEOTGQ==", "result": true, "deviceStatus": "IDLE", "data": {}, "errors": [] }' * */ + @NonNullByDefault private static class Response { private String token = VeluxBindingConstants.UNKNOWN; private boolean result; @@ -106,11 +109,6 @@ public JClogin() { logger.trace("JClogin(constructor) called."); } - @Override - public void finalize() { - logger.trace("finalize({}) called.", this); - } - /* * Methods required for interface {@link BridgeCommunicationProtocol}. */ @@ -161,7 +159,7 @@ public String[] getErrors() { @Override public void setPassword(String thisPassword) { - logger.trace("setPassword({}) called.", thisPassword); + logger.trace("setPassword() called."); request.params.password = thisPassword; } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JClogout.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JClogout.java similarity index 88% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JClogout.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JClogout.java index 4c7dae4a3d861..759899c0ca645 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JClogout.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JClogout.java @@ -10,15 +10,15 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.json; +package org.openhab.binding.velux.internal.bridge.json; import java.util.HashMap; import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.bridge.common.Logout; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.Logout; /** * Specific bridge communication message supported by the Velux bridge. @@ -27,8 +27,8 @@ *

    * * It defines information how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the {@link org.openhab.binding.velux.bridge.json.JsonBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeCommunicationProtocol * BridgeCommunicationProtocol}. * * @author Guenther Schreiner - Initial contribution. @@ -51,6 +51,7 @@ class JClogout extends Logout implements JsonBridgeCommunicationProtocol { * {"action":"logout","params":{}} * */ + @NonNullByDefault private static class Request { @SuppressWarnings("unused") @@ -74,6 +75,7 @@ public Request() { * '{"token": "PHPnfLda71xfGlxoYEOTGQ==", "result": true, "deviceStatus": "IDLE", "data": {}, "errors": [] }' * */ + @NonNullByDefault private static class Response { @SuppressWarnings("unused") private String token = VeluxBindingConstants.UNKNOWN; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunProductDiscovery.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductDiscovery.java similarity index 91% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunProductDiscovery.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductDiscovery.java index 52dc3bc6d638f..036d72f5df987 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunProductDiscovery.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductDiscovery.java @@ -10,15 +10,15 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.json; +package org.openhab.binding.velux.internal.bridge.json; import java.util.HashMap; import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.bridge.common.RunProductDiscovery; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.RunProductDiscovery; /** * Specific bridge communication message supported by the Velux bridge. @@ -27,7 +27,7 @@ *

    * * It defines information how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} * as described by the {@link JsonBridgeCommunicationProtocol}. * * @author Guenther Schreiner - Initial contribution. @@ -58,6 +58,7 @@ class JCrunProductDiscovery extends RunProductDiscovery implements JsonBridgeCom * NOTE: the gateway software is extremely sensitive to this exact JSON structure. * Any modifications (like omitting empty params) will lead to an gateway error. */ + @NonNullByDefault private static class Request { @SuppressWarnings("unused") @@ -88,6 +89,7 @@ public Request() { * } * */ + @NonNullByDefault private static class Response { @SuppressWarnings("unused") private String token = VeluxBindingConstants.UNKNOWN; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunProductIdentification.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductIdentification.java similarity index 92% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunProductIdentification.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductIdentification.java index 8a644df0484fe..1be68bfb6231a 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunProductIdentification.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductIdentification.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.json; +package org.openhab.binding.velux.internal.bridge.json; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.bridge.common.RunProductIdentification; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.RunProductIdentification; /** * Specific bridge communication message supported by the Velux bridge. @@ -24,7 +24,7 @@ *

    * * It defines information how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} * as described by the {@link JsonBridgeCommunicationProtocol}. * * @author Guenther Schreiner - Initial contribution. @@ -46,6 +46,7 @@ class JCrunProductIdentification extends RunProductIdentification implements Jso /* * Message Objects */ + @NonNullByDefault private static class ParamsIdentifyProduct { @SuppressWarnings("unused") private int id; @@ -67,6 +68,7 @@ private ParamsIdentifyProduct(int id, int time) { * {"action":"identify","params":{"id":23,"time":254}} * */ + @NonNullByDefault private static class Request { @SuppressWarnings("unused") private String action; @@ -96,6 +98,7 @@ public Request() { * } * */ + @NonNullByDefault private static class Response { @SuppressWarnings("unused") private String token = VeluxBindingConstants.UNKNOWN; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunProductSearch.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductSearch.java similarity index 86% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunProductSearch.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductSearch.java index 1e3b565a7d37f..24c44089a1f25 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunProductSearch.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductSearch.java @@ -10,14 +10,14 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.json; +package org.openhab.binding.velux.internal.bridge.json; import java.util.HashMap; import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.bridge.common.RunProductSearch; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.RunProductSearch; /** * Specific bridge communication message supported by the Velux bridge. @@ -27,8 +27,8 @@ * Implementing the abstract class {@link RunProductSearch}. *

    * It defines information how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the {@link org.openhab.binding.velux.bridge.json.JsonBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeCommunicationProtocol * BridgeCommunicationProtocol}. * * @author Guenther Schreiner - Initial contribution. @@ -47,7 +47,7 @@ class JCrunProductSearch extends RunProductSearch implements JsonBridgeCommunica */ /** - * Bridge I/O Request message used by {@link org.openhab.binding.velux.bridge.json.JsonVeluxBridge JsonVeluxBridge} + * Bridge I/O Request message used by {@link org.openhab.binding.velux.internal.bridge.json.JsonVeluxBridge JsonVeluxBridge} * for serializing. * * Resulting JSON: @@ -59,6 +59,7 @@ class JCrunProductSearch extends RunProductSearch implements JsonBridgeCommunica * NOTE: the gateway software is extremely sensitive to this exact JSON structure. * Any modifications (like omitting empty params) will lead to an gateway error. */ + @NonNullByDefault private static class Request { @SuppressWarnings("unused") @@ -74,7 +75,7 @@ public Request() { } /** - * Bridge I/O Response message used by {@link org.openhab.binding.velux.bridge.json.JsonVeluxBridge} for + * Bridge I/O Response message used by {@link org.openhab.binding.velux.internal.bridge.json.JsonVeluxBridge} for * deserializing with including component access methods * * Expected JSON (sample): @@ -89,6 +90,7 @@ public Request() { * } * */ + @NonNullByDefault private static class Response { @SuppressWarnings("unused") private String token = VeluxBindingConstants.UNKNOWN; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunScene.java similarity index 88% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunScene.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunScene.java index 7ca78793be913..272e38e69a684 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCrunScene.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunScene.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.json; +package org.openhab.binding.velux.internal.bridge.json; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.bridge.common.RunScene; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.RunScene; /** * Specific bridge communication message supported by the Velux bridge. @@ -24,8 +24,8 @@ *

    * * It defines information how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the {@link org.openhab.binding.velux.bridge.json.JsonBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeCommunicationProtocol * BridgeCommunicationProtocol}. * * @author Guenther Schreiner - Initial contribution. @@ -42,6 +42,7 @@ class JCrunScene extends RunScene implements JsonBridgeCommunicationProtocol { /* * Message Objects */ + @NonNullByDefault private static class ParamsRunScene { @SuppressWarnings("unused") private int id; @@ -57,6 +58,7 @@ private static class ParamsRunScene { * {"action":"run","params":{"id":9}} * */ + @NonNullByDefault private static class Request { @SuppressWarnings("unused") private String action; @@ -84,6 +86,7 @@ public Request() { * } * */ + @NonNullByDefault private static class Response { @SuppressWarnings("unused") private String token = VeluxBindingConstants.UNKNOWN; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCsetSceneVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCsetSceneVelocity.java similarity index 89% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCsetSceneVelocity.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCsetSceneVelocity.java index a338f8407de19..efaa3d4867bc8 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JCsetSceneVelocity.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCsetSceneVelocity.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.json; +package org.openhab.binding.velux.internal.bridge.json; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.bridge.common.SetSceneVelocity; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.SetSceneVelocity; /** * Specific bridge communication message supported by the Velux bridge. @@ -24,8 +24,8 @@ *

    * * It defines information how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the {@link org.openhab.binding.velux.bridge.json.JsonBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeCommunicationProtocol * BridgeCommunicationProtocol}. * * @author Guenther Schreiner - Initial contribution. @@ -45,6 +45,7 @@ class JCsetSceneVelocity extends SetSceneVelocity implements JsonBridgeCommunica /* * Message Objects */ + @NonNullByDefault private static class ParamsRunScene { @SuppressWarnings("unused") private int id; @@ -67,6 +68,7 @@ private ParamsRunScene(int id, boolean silent) { * {"action":"setSilentMode","params":{"id":9,"silent":false}}} * */ + @NonNullByDefault private static class Request { @SuppressWarnings("unused") private String action; @@ -95,6 +97,7 @@ public Request() { * } * */ + @NonNullByDefault private static class Response { @SuppressWarnings("unused") private String token = VeluxBindingConstants.UNKNOWN; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JsonBridgeAPI.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonBridgeAPI.java similarity index 79% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JsonBridgeAPI.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonBridgeAPI.java index a31be34a1ac03..e9fee7a503608 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JsonBridgeAPI.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonBridgeAPI.java @@ -10,30 +10,31 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.json; +package org.openhab.binding.velux.internal.bridge.json; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.velux.bridge.VeluxBridgeInstance; -import org.openhab.binding.velux.bridge.common.BridgeAPI; -import org.openhab.binding.velux.bridge.common.GetDeviceStatus; -import org.openhab.binding.velux.bridge.common.GetFirmware; -import org.openhab.binding.velux.bridge.common.GetHouseStatus; -import org.openhab.binding.velux.bridge.common.GetLANConfig; -import org.openhab.binding.velux.bridge.common.GetProduct; -import org.openhab.binding.velux.bridge.common.GetProductLimitation; -import org.openhab.binding.velux.bridge.common.GetProducts; -import org.openhab.binding.velux.bridge.common.GetScenes; -import org.openhab.binding.velux.bridge.common.GetWLANConfig; -import org.openhab.binding.velux.bridge.common.Login; -import org.openhab.binding.velux.bridge.common.Logout; -import org.openhab.binding.velux.bridge.common.RunProductCommand; -import org.openhab.binding.velux.bridge.common.RunProductDiscovery; -import org.openhab.binding.velux.bridge.common.RunProductIdentification; -import org.openhab.binding.velux.bridge.common.RunProductSearch; -import org.openhab.binding.velux.bridge.common.RunScene; -import org.openhab.binding.velux.bridge.common.SetHouseStatusMonitor; -import org.openhab.binding.velux.bridge.common.SetSceneVelocity; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeInstance; +import org.openhab.binding.velux.internal.bridge.common.BridgeAPI; +import org.openhab.binding.velux.internal.bridge.common.GetDeviceStatus; +import org.openhab.binding.velux.internal.bridge.common.GetFirmware; +import org.openhab.binding.velux.internal.bridge.common.GetHouseStatus; +import org.openhab.binding.velux.internal.bridge.common.GetLANConfig; +import org.openhab.binding.velux.internal.bridge.common.GetProduct; +import org.openhab.binding.velux.internal.bridge.common.GetProductLimitation; +import org.openhab.binding.velux.internal.bridge.common.GetProducts; +import org.openhab.binding.velux.internal.bridge.common.GetScenes; +import org.openhab.binding.velux.internal.bridge.common.GetWLANConfig; +import org.openhab.binding.velux.internal.bridge.common.Login; +import org.openhab.binding.velux.internal.bridge.common.Logout; +import org.openhab.binding.velux.internal.bridge.common.RunProductCommand; +import org.openhab.binding.velux.internal.bridge.common.RunProductDiscovery; +import org.openhab.binding.velux.internal.bridge.common.RunProductIdentification; +import org.openhab.binding.velux.internal.bridge.common.RunProductSearch; +import org.openhab.binding.velux.internal.bridge.common.RunScene; +import org.openhab.binding.velux.internal.bridge.common.SetHouseStatusMonitor; +import org.openhab.binding.velux.internal.bridge.common.SetProductLimitation; +import org.openhab.binding.velux.internal.bridge.common.SetSceneVelocity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -100,7 +101,7 @@ class JsonBridgeAPI implements BridgeAPI { * Constructor. *

    * Inherits the initialization of the binding-wide instance for dealing for common information and - * initializes the handler {@link org.openhab.binding.velux.bridge.json.JsonVeluxBridge#bridgeAPI + * initializes the handler {@link org.openhab.binding.velux.internal.bridge.json.JsonVeluxBridge#bridgeAPI * JsonVeluxBridge.bridgeAPI} * to pass the interface methods. * @@ -140,6 +141,11 @@ public GetLANConfig getLANConfig() { return null; } + @Override + public @Nullable SetProductLimitation setProductLimitation() { + return null; + } + @Override public GetProducts getProducts() { return GETPRODUCTS; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JsonBridgeCommunicationProtocol.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonBridgeCommunicationProtocol.java similarity index 86% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JsonBridgeCommunicationProtocol.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonBridgeCommunicationProtocol.java index 6225240a8e6f0..eebdc72f8bd60 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JsonBridgeCommunicationProtocol.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonBridgeCommunicationProtocol.java @@ -10,23 +10,23 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.json; +package org.openhab.binding.velux.internal.bridge.json; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.BridgeCommunicationProtocol; +import org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol; /** * Common JSON-based bridge communication message scheme supported by the Velux bridge. *

    * This bridge communication is an extension of the common - * {@link org.openhab.binding.velux.bridge.common.BridgeCommunicationProtocol BridgeCommunicationProtocol}. + * {@link org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol BridgeCommunicationProtocol}. *

    * Message semantic will be defined by the implementation of the separate message classes, - * which are defined within {@link org.openhab.binding.velux.bridge.json.JsonBridgeAPI JsonBridgeAPI}. + * which are defined within {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeAPI JsonBridgeAPI}. *

    * The implementations will define the information which to send query and receive answer * through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider}. + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider}. *

    * (Methods in this interface for the appropriate interaction: *

      diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JsonVeluxBridge.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonVeluxBridge.java similarity index 84% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JsonVeluxBridge.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonVeluxBridge.java index 72cb26b4f65ba..728015b3cdd94 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/JsonVeluxBridge.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonVeluxBridge.java @@ -10,19 +10,21 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.json; +package org.openhab.binding.velux.internal.bridge.json; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.Properties; import java.util.TreeSet; import org.apache.commons.io.IOUtils; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.VeluxBridge; -import org.openhab.binding.velux.bridge.VeluxBridgeInstance; -import org.openhab.binding.velux.bridge.common.BridgeAPI; -import org.openhab.binding.velux.bridge.common.BridgeCommunicationProtocol; +import org.eclipse.smarthome.io.net.http.HttpUtil; +import org.openhab.binding.velux.internal.bridge.VeluxBridge; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeInstance; +import org.openhab.binding.velux.internal.bridge.common.BridgeAPI; +import org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -69,7 +71,7 @@ public class JsonVeluxBridge extends VeluxBridge { /** * Handler passing the interface methods to other classes. - * Can be accessed via method {@link org.openhab.binding.velux.bridge.common.BridgeAPI BridgeAPI}. + * Can be accessed via method {@link org.openhab.binding.velux.internal.bridge.common.BridgeAPI BridgeAPI}. * */ private BridgeAPI bridgeAPI; @@ -136,7 +138,7 @@ protected boolean bridgeDirectCommunicate(BridgeCommunicationProtocol communicat * @return timestamp in milliseconds. */ @Override - public synchronized long lastCommunication() { + public long lastCommunication() { return lastCommunicationInMSecs; } @@ -147,7 +149,7 @@ public synchronized long lastCommunication() { * @return timestamp in milliseconds. */ @Override - public synchronized long lastSuccessfulCommunication() { + public long lastSuccessfulCommunication() { return lastSuccessfulCommunicationInMSecs; } @@ -200,9 +202,11 @@ private synchronized boolean bridgeDirectCommunicate(JsonBridgeCommunicationProt /** * Base level communication with the Velux bridge. * + * @param This describes the request type parameter. + * @param This describes the response type parameter. * @param url as String describing the Service Access Point location i.e. http://localhost/api . * @param authentication as String providing the Authentication token to be passed with the request header. - * @param Request as Object representing the structure of the message request body to be converted into + * @param request as Object representing the structure of the message request body to be converted into * JSON. * @param classOfResponse as Class representing the expected structure of the message response body to be converted * from JSON. @@ -213,28 +217,26 @@ private synchronized boolean bridgeDirectCommunicate(JsonBridgeCommunicationProt * @throws java.io.IOException in case of continuous communication I/O failures. * @throws JsonSyntaxException in case of unusual communication failures. */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - private Object io(String url, String authentication, Object Request, Class classOfResponse) + private T io(String url, String authentication, U request, Class classOfResponse) throws JsonSyntaxException, IOException { /** Local handles */ int retryCount = 0; - IOException lastIOE; lastCommunicationInMSecs = System.currentTimeMillis(); do { try { Gson gson = new Gson(); - String jsonRequest = gson.toJson(Request); + String jsonRequest = gson.toJson(request); logger.trace("io() to {} using request {}.", url, jsonRequest); Properties headerItems = new Properties(); if (authentication.length() > 0) { headerItems.setProperty("Authorization", String.format("Bearer %s", authentication)); } - InputStream content = IOUtils.toInputStream(jsonRequest, "UTF-8"); + InputStream content = IOUtils.toInputStream(jsonRequest, StandardCharsets.UTF_8.name()); - String jsonResponse = org.eclipse.smarthome.io.net.http.HttpUtil.executeUrl("PUT", url, headerItems, - content, "application/json", this.bridgeInstance.veluxBridgeConfiguration().timeoutMsecs); + String jsonResponse = HttpUtil.executeUrl("PUT", url, headerItems, content, "application/json", + this.bridgeInstance.veluxBridgeConfiguration().timeoutMsecs); if (jsonResponse == null) { throw new IOException("transport error"); } @@ -245,21 +247,15 @@ private Object io(String url, String authentication, Object Request, Class class } catch (InterruptedException ie) { logger.trace("io() wait interrupted."); } - logger.trace("io() got response {}.", jsonResponse.replaceAll("\\p{C}", ".")); jsonResponse = jsonResponse.replaceAll("^.+,\n", ""); logger.trace("io() cleaned response {}.", jsonResponse); - try { - Object response = gson.fromJson(jsonResponse, classOfResponse); - lastCommunicationInMSecs = lastSuccessfulCommunicationInMSecs = System.currentTimeMillis(); - return response; - } catch (JsonSyntaxException jse) { - logger.info("io(): Exception occurred on deserialization: {}, aborting.", jse.getMessage()); - throw jse; - } + T response = gson.fromJson(jsonResponse, classOfResponse); + lastCommunicationInMSecs = lastSuccessfulCommunicationInMSecs = System.currentTimeMillis(); + return response; + } catch (IOException ioe) { logger.trace("io(): Exception occurred during I/O: {}.", ioe.getMessage()); - lastIOE = ioe; // Error Retries with Exponential Backoff long waitTime = ((long) Math.pow(2, retryCount) * this.bridgeInstance.veluxBridgeConfiguration().timeoutMsecs); @@ -269,18 +265,24 @@ private Object io(String url, String authentication, Object Request, Class class } catch (InterruptedException ie) { logger.trace("io() wait interrupted."); } + } catch (JsonSyntaxException jse) { + logger.info("io(): Exception occurred on deserialization: {}, aborting.", jse.getMessage()); + throw jse; } + } while (retryCount++ < this.bridgeInstance.veluxBridgeConfiguration().retries); - logger.info("io(): socket I/O failed ({} times).", this.bridgeInstance.veluxBridgeConfiguration().retries); - throw lastIOE; + throw new IOException(String.format("io(): socket I/O failed (%d times).", + this.bridgeInstance.veluxBridgeConfiguration().retries)); } /** * Initializes an authenticated communication with the {@link JsonVeluxBridge Velux bridge}. * + * @param This describes the request type parameter. + * @param This describes the response type parameter. * @param url as String describing the Service Access Point location i.e. http://localhost/api . * @param authentication as String providing the Authentication token to be passed with the request header. - * @param Request as Object representing the structure of the message request body to be converted into + * @param request as Object representing the structure of the message request body to be converted into * JSON. * @param classOfResponse as Class representing the expected structure of the message response body to be converted * from JSON. @@ -290,17 +292,18 @@ private Object io(String url, String authentication, Object Request, Class class * @throws java.io.IOException in case of continuous communication I/O failures. * @throws JsonSyntaxException in case of unusual communication failures. */ - @SuppressWarnings("rawtypes") - private Object ioAuthenticated(String url, String authentication, Object Request, Class classOfResponse) + private T ioAuthenticated(String url, String authentication, U request, Class classOfResponse) throws JsonSyntaxException, IOException { - return io(url, authentication, Request, classOfResponse); + return io(url, authentication, request, classOfResponse); } /** * Initializes an unauthenticated communication with the {@link JsonVeluxBridge Velux bridge}. * + * @param This describes the request type parameter. + * @param This describes the response type parameter. * @param url as String describing the Service Access Point location i.e. http://localhost/api . - * @param Request as Object representing the structure of the message request body to be converted into + * @param request as Object representing the structure of the message request body to be converted into * JSON. * @param classOfResponse as Class representing the expected structure of the message response body to be converted * from JSON. @@ -311,10 +314,9 @@ private Object ioAuthenticated(String url, String authentication, Object Request * @throws java.io.IOException in case of continuous communication I/O failures. * @throws JsonSyntaxException in case of unusual communication failures. */ - @SuppressWarnings("rawtypes") - private Object ioUnauthenticated(String url, Object Request, Class classOfResponse) + private T ioUnauthenticated(String url, U request, Class classOfResponse) throws JsonSyntaxException, IOException { - return io(url, "", Request, classOfResponse); + return io(url, "", request, classOfResponse); } } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/package-info.java similarity index 90% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/package-info.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/package-info.java index 524662922782c..101cedf512abb 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/json/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/package-info.java @@ -15,4 +15,4 @@ * * @author Guenther Schreiner - Initial contribution */ -package org.openhab.binding.velux.bridge.json; +package org.openhab.binding.velux.internal.bridge.json; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/package-info.java similarity index 92% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/package-info.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/package-info.java index cad2e9548328b..e3629db152f8b 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/package-info.java @@ -16,4 +16,4 @@ * * @author Guenther Schreiner - Initial contribution */ -package org.openhab.binding.velux.bridge; +package org.openhab.binding.velux.internal.bridge; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetDeviceStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetDeviceStatus.java similarity index 87% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetDeviceStatus.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetDeviceStatus.java index 9da451bba237f..456710b4fea6b 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetDeviceStatus.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetDeviceStatus.java @@ -10,15 +10,15 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip; +package org.openhab.binding.velux.internal.bridge.slip; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.GetDeviceStatus; -import org.openhab.binding.velux.bridge.slip.util.KLF200Response; -import org.openhab.binding.velux.bridge.slip.util.Packet; -import org.openhab.binding.velux.things.VeluxGwState; -import org.openhab.binding.velux.things.VeluxKLFAPI.Command; -import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.bridge.common.GetDeviceStatus; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxGwState; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,8 +30,8 @@ * itself. *

      * As 3rd level class it defines informations how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the interface {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol * SlipBridgeCommunicationProtocol}. *

      * Methods in addition to the mentioned interface: diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetFirmware.java similarity index 88% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetFirmware.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetFirmware.java index 45532b8d3b2eb..b7ee30794919f 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetFirmware.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetFirmware.java @@ -10,15 +10,15 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip; +package org.openhab.binding.velux.internal.bridge.slip; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.GetFirmware; -import org.openhab.binding.velux.bridge.slip.util.KLF200Response; -import org.openhab.binding.velux.bridge.slip.util.Packet; -import org.openhab.binding.velux.things.VeluxGwFirmware; -import org.openhab.binding.velux.things.VeluxKLFAPI.Command; -import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.bridge.common.GetFirmware; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxGwFirmware; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,8 +30,8 @@ * itself. *

      * As 3rd level class it defines informations how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol * SlipBridgeCommunicationProtocol}. *

      * Methods in addition to the mentioned interface: diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetHouseStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetHouseStatus.java similarity index 90% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetHouseStatus.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetHouseStatus.java index 42d76696d1050..0ad387c7bd882 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetHouseStatus.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetHouseStatus.java @@ -10,15 +10,15 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip; +package org.openhab.binding.velux.internal.bridge.slip; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.BridgeCommunicationProtocol; -import org.openhab.binding.velux.bridge.common.GetHouseStatus; -import org.openhab.binding.velux.bridge.slip.util.KLF200Response; -import org.openhab.binding.velux.bridge.slip.util.Packet; -import org.openhab.binding.velux.things.VeluxKLFAPI.Command; -import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol; +import org.openhab.binding.velux.internal.bridge.common.GetHouseStatus; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,8 +29,8 @@ * Common Message semantic: Communication from the bridge and storing returned information within the class itself. *

      * As 3rd level class it defines informations how to receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the interface {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol * SlipBridgeCommunicationProtocol}. *

      * Methods in addition to the mentioned interface: diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLANConfig.java similarity index 86% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetLANConfig.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLANConfig.java index 583d2196b3d22..7466ad6d23380 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetLANConfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLANConfig.java @@ -10,15 +10,15 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip; +package org.openhab.binding.velux.internal.bridge.slip; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.GetLANConfig; -import org.openhab.binding.velux.bridge.slip.util.KLF200Response; -import org.openhab.binding.velux.bridge.slip.util.Packet; -import org.openhab.binding.velux.things.VeluxGwLAN; -import org.openhab.binding.velux.things.VeluxKLFAPI.Command; -import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.bridge.common.GetLANConfig; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxGwLAN; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,8 +30,8 @@ * itself. *

      * As 3rd level class it defines informations how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol * SlipBridgeCommunicationProtocol}. *

      * Methods in addition to the mentioned interface: diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLimitation.java similarity index 59% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetLimitation.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLimitation.java index 6b8dc3ba88916..c1cde60af7e20 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetLimitation.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLimitation.java @@ -10,16 +10,16 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip; +package org.openhab.binding.velux.internal.bridge.slip; import java.util.Random; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.GetProductLimitation; -import org.openhab.binding.velux.bridge.slip.util.KLF200Response; -import org.openhab.binding.velux.bridge.slip.util.Packet; -import org.openhab.binding.velux.things.VeluxKLFAPI.Command; -import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.bridge.common.GetProductLimitation; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,7 +31,7 @@ * itself. *

      * As 3rd level class it defines informations how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} * as described by the interface {@link SlipBridgeCommunicationProtocol}. *

      * Methods in addition to the mentioned interface: @@ -57,8 +57,6 @@ class SCgetLimitation extends GetProductLimitation implements SlipBridgeCommunic */ private int reqSessionID = 0; - private int reqCommandOriginator = 8; // SAAC - private int reqPriorityLevel = 5; // Comfort Level 2 private int reqIndexArrayCount = 1; // One node will be addressed private int reqIndexArray01 = 1; // This is the node private int reqParameterID = 0; // MP = Main parameter @@ -79,6 +77,8 @@ class SCgetLimitation extends GetProductLimitation implements SlipBridgeCommunic private boolean success = false; private boolean finished = false; + private int limitationValue = 0; + /* * =========================================================== * Constructor Method @@ -111,18 +111,14 @@ public CommandNumber getRequestCommand() { @Override public byte[] getRequestDataAsArrayOfBytes() { - Packet request = new Packet(new byte[27]); + Packet request = new Packet(new byte[25]); reqSessionID = (reqSessionID + 1) & 0xffff; request.setTwoByteValue(0, reqSessionID); - request.setOneByteValue(2, reqCommandOriginator); - request.setOneByteValue(3, reqPriorityLevel); - request.setOneByteValue(4, reqIndexArrayCount); - request.setOneByteValue(5, reqIndexArray01); - request.setOneByteValue(25, reqParameterID); - request.setOneByteValue(26, reqLimitationType); + request.setOneByteValue(2, reqIndexArrayCount); + request.setOneByteValue(3, reqIndexArray01); + request.setOneByteValue(23, reqParameterID); + request.setOneByteValue(24, reqLimitationType); logger.trace("getRequestDataAsArrayOfBytes(): ntfSessionID={}.", reqSessionID); - logger.trace("getRequestDataAsArrayOfBytes(): reqCommandOriginator={}.", reqCommandOriginator); - logger.trace("getRequestDataAsArrayOfBytes(): reqPriorityLevel={}.", reqPriorityLevel); logger.trace("getRequestDataAsArrayOfBytes(): reqIndexArrayCount={}.", reqIndexArrayCount); logger.trace("getRequestDataAsArrayOfBytes(): reqIndexArray01={}.", reqIndexArray01); logger.trace("getRequestDataAsArrayOfBytes(): reqParameterID={}.", reqParameterID); @@ -162,13 +158,9 @@ public void setResponse(short responseCommand, byte[] thisResponseData, boolean finished = true; break; } - if (!isSequentialEnforced) { - finished = true; - } break; case GW_LIMITATION_STATUS_NTF: - finished = true; if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 10)) { break; } @@ -186,13 +178,87 @@ public void setResponse(short responseCommand, byte[] thisResponseData, boolean break; } - logger.info("setResponse(): nodeId={}.", ntfNodeID); - logger.info("setResponse(): ntfParameterID={}.", ntfParameterID); - logger.info("setResponse(): ntfMinValue={}.", ntfMinValue); - logger.info("setResponse(): ntfMaxValue={}.", ntfMaxValue); - logger.info("setResponse(): ntfLimitationOriginator={}.", ntfLimitationOriginator); - logger.info("setResponse(): ntfLimitationTime={}.", ntfLimitationTime); + logger.trace("setResponse(): nodeId={}.", ntfNodeID); + logger.trace("setResponse(): ntfParameterID={}.", ntfParameterID); + logger.trace("setResponse(): ntfMinValue={}.", ntfMinValue); + logger.trace("setResponse(): ntfMaxValue={}.", ntfMaxValue); + logger.trace("setResponse(): ntfLimitationOriginator={}.", ntfLimitationOriginator); + logger.trace("setResponse(): ntfLimitationTime={}.", ntfLimitationTime); + + // Determine the returned value + limitationValue = (reqLimitationType == 0) ? ntfMinValue : ntfMaxValue; + logger.debug("setResponse(): {} limitation for node {} is {}.", + (reqLimitationType == 0) ? "minimum" : "maximum", reqIndexArray01, limitationValue); + + success = true; + if (!isSequentialEnforced) { + logger.trace( + "setResponse(): skipping wait for more packets as sequential processing is not enforced."); + finished = true; + } + break; + + case GW_COMMAND_RUN_STATUS_NTF: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 13)) { + finished = true; + break; + } + ntfSessionID = responseData.getTwoByteValue(0); + int ntfStatusiD = responseData.getOneByteValue(2); + int ntfIndex = responseData.getOneByteValue(3); + int ntfNodeParameter = responseData.getOneByteValue(4); + int ntfParameterValue = responseData.getTwoByteValue(5); + int ntfRunStatus = responseData.getOneByteValue(7); + int ntfStatusReply = responseData.getOneByteValue(8); + int ntfInformationCode = responseData.getFourByteValue(9); + // Extracting information items + logger.trace("setResponse(): ntfSessionID={} (requested {}).", ntfSessionID, reqSessionID); + logger.trace("setResponse(): ntfStatusiD={}.", ntfStatusiD); + logger.trace("setResponse(): ntfIndex={}.", ntfIndex); + logger.trace("setResponse(): ntfNodeParameter={}.", ntfNodeParameter); + logger.trace("setResponse(): ntfParameterValue={}.", ntfParameterValue); + logger.trace("setResponse(): ntfRunStatus={}.", ntfRunStatus); + logger.trace("setResponse(): ntfStatusReply={}.", ntfStatusReply); + logger.trace("setResponse(): ntfInformationCode={}.", ntfInformationCode); + + if (!KLF200Response.check4matchingSessionID(logger, ntfSessionID, reqSessionID)) { + finished = true; + } + switch (ntfRunStatus) { + case 0: + logger.debug("setResponse(): returned ntfRunStatus: EXECUTION_COMPLETED."); + success = true; + break; + case 1: + logger.info("setResponse(): returned ntfRunStatus: EXECUTION_FAILED."); + finished = true; + break; + case 2: + logger.debug("setResponse(): returned ntfRunStatus: EXECUTION_ACTIVE."); + break; + default: + logger.warn("setResponse(): returned ntfRunStatus={} (not defined).", ntfRunStatus); + finished = true; + break; + } + if (!isSequentialEnforced) { + logger.trace( + "setResponse(): skipping wait for more packets as sequential processing is not enforced."); + success = true; + finished = true; + } + break; + case GW_SESSION_FINISHED_NTF: + finished = true; + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 2)) { + break; + } + int finishedNtfSessionID = responseData.getTwoByteValue(0); + if (!KLF200Response.check4matchingSessionID(logger, finishedNtfSessionID, reqSessionID)) { + break; + } + logger.debug("setResponse(): finishedNtfSessionID={}.", finishedNtfSessionID); success = true; break; @@ -228,7 +294,7 @@ public void setActuatorIdAndLimitationType(int nodeId, boolean limitationMinimum @Override public int getLimitation() { - return 0; + return limitationValue; } } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetProduct.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProduct.java similarity index 92% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetProduct.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProduct.java index 4d948c76aa1b9..d008e50751bd2 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetProduct.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProduct.java @@ -10,19 +10,19 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip; +package org.openhab.binding.velux.internal.bridge.slip; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.GetProduct; -import org.openhab.binding.velux.bridge.slip.util.KLF200Response; -import org.openhab.binding.velux.bridge.slip.util.Packet; -import org.openhab.binding.velux.things.VeluxKLFAPI.Command; -import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; -import org.openhab.binding.velux.things.VeluxProduct; -import org.openhab.binding.velux.things.VeluxProduct.ProductBridgeIndex; -import org.openhab.binding.velux.things.VeluxProductName; -import org.openhab.binding.velux.things.VeluxProductSerialNo; -import org.openhab.binding.velux.things.VeluxProductType; +import org.openhab.binding.velux.internal.bridge.common.GetProduct; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxProduct; +import org.openhab.binding.velux.internal.things.VeluxProductName; +import org.openhab.binding.velux.internal.things.VeluxProductSerialNo; +import org.openhab.binding.velux.internal.things.VeluxProductType; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,8 +34,8 @@ * itself. *

      * As 3rd level class it defines informations how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol * SlipBridgeCommunicationProtocol}. *

      * Methods in addition to the mentioned interface: diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProducts.java similarity index 92% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetProducts.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProducts.java index 1cf191c5c50ec..3cdd513e3075e 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetProducts.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProducts.java @@ -10,19 +10,19 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip; +package org.openhab.binding.velux.internal.bridge.slip; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.GetProducts; -import org.openhab.binding.velux.bridge.slip.util.KLF200Response; -import org.openhab.binding.velux.bridge.slip.util.Packet; -import org.openhab.binding.velux.things.VeluxKLFAPI.Command; -import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; -import org.openhab.binding.velux.things.VeluxProduct; -import org.openhab.binding.velux.things.VeluxProduct.ProductBridgeIndex; -import org.openhab.binding.velux.things.VeluxProductName; -import org.openhab.binding.velux.things.VeluxProductSerialNo; -import org.openhab.binding.velux.things.VeluxProductType; +import org.openhab.binding.velux.internal.bridge.common.GetProducts; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxProduct; +import org.openhab.binding.velux.internal.things.VeluxProductName; +import org.openhab.binding.velux.internal.things.VeluxProductSerialNo; +import org.openhab.binding.velux.internal.things.VeluxProductType; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,8 +34,8 @@ * itself. *

      * As 3rd level class it defines informations how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol * SlipBridgeCommunicationProtocol}. *

      * Methods in addition to the mentioned interface: diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetScenes.java similarity index 89% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetScenes.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetScenes.java index e0cfb0cf8f9e5..6f5a2d32d61de 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetScenes.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetScenes.java @@ -10,16 +10,16 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip; +package org.openhab.binding.velux.internal.bridge.slip; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.GetScenes; -import org.openhab.binding.velux.bridge.slip.util.KLF200Response; -import org.openhab.binding.velux.bridge.slip.util.Packet; -import org.openhab.binding.velux.things.VeluxKLFAPI.Command; -import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; -import org.openhab.binding.velux.things.VeluxProductState; -import org.openhab.binding.velux.things.VeluxScene; +import org.openhab.binding.velux.internal.bridge.common.GetScenes; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxProductState; +import org.openhab.binding.velux.internal.things.VeluxScene; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,8 +31,8 @@ * itself. *

      * As 3rd level class it defines informations how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the interface {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol * SlipBridgeCommunicationProtocol}. *

      * Methods in addition to the mentioned interface: diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetWLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetWLANConfig.java similarity index 87% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetWLANConfig.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetWLANConfig.java index 49db153eb4cfb..c54be98b632c9 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCgetWLANConfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetWLANConfig.java @@ -10,15 +10,15 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip; +package org.openhab.binding.velux.internal.bridge.slip; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.velux.bridge.common.GetWLANConfig; -import org.openhab.binding.velux.bridge.slip.util.Packet; -import org.openhab.binding.velux.things.VeluxGwWLAN; -import org.openhab.binding.velux.things.VeluxKLFAPI.Command; -import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.bridge.common.GetWLANConfig; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxGwWLAN; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,7 +30,7 @@ * itself. *

      * As 3rd level class it defines informations how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} * as described by the interface {@link SlipBridgeCommunicationProtocol}. *

      * Methods in addition to the mentioned interface: diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SClogin.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogin.java similarity index 84% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SClogin.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogin.java index 682a4769143cd..08e4aeb26d6f6 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SClogin.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogin.java @@ -10,15 +10,15 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip; +package org.openhab.binding.velux.internal.bridge.slip; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.bridge.common.Login; -import org.openhab.binding.velux.bridge.slip.util.KLF200Response; -import org.openhab.binding.velux.bridge.slip.util.Packet; -import org.openhab.binding.velux.things.VeluxKLFAPI.Command; -import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.Login; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,8 +30,8 @@ * itself. *

      * As 3rd level class it defines informations how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the interface {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol * SlipBridgeCommunicationProtocol}. *

      * Methods in addition to the mentioned interface: @@ -118,6 +118,8 @@ public void setResponse(short responseCommand, byte[] thisResponseData, boolean success = true; break; case 1: + logger.warn("{} bridge connection successfully established but login failed.", + VeluxBindingConstants.BINDING_ID); logger.debug("setResponse(): returned status: The request failed."); break; default: @@ -150,14 +152,14 @@ public boolean isCommunicationSuccessful() { @Override public void setPassword(String thisPassword) { - logger.trace("setPassword({}) called.", thisPassword); + logger.trace("setPassword({}) called.", thisPassword.replaceAll(".", "*")); reqPassword = thisPassword; return; } @Override public String getAuthToken() { - logger.trace("getAuthToken() called, returning {}.", reqPassword); + logger.trace("getAuthToken() called, returning {}.", reqPassword.replaceAll(".", "*")); return reqPassword; } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SClogout.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogout.java similarity index 84% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SClogout.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogout.java index 189f7aec06ff9..cf73e71ba3058 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SClogout.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogout.java @@ -10,13 +10,13 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip; +package org.openhab.binding.velux.internal.bridge.slip; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.Logout; -import org.openhab.binding.velux.bridge.slip.util.KLF200Response; -import org.openhab.binding.velux.things.VeluxKLFAPI.Command; -import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.bridge.common.Logout; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,8 +28,8 @@ * itself. *

      * As 3rd level class it defines informations how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the interface {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol * SlipBridgeCommunicationProtocol}. *

      * There are no methods in addition to the mentioned interface. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductCommand.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductCommand.java similarity index 95% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductCommand.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductCommand.java index ad21d50b0e5c6..030ce4368b268 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductCommand.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductCommand.java @@ -10,16 +10,16 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip; +package org.openhab.binding.velux.internal.bridge.slip; import java.util.Random; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.RunProductCommand; -import org.openhab.binding.velux.bridge.slip.util.KLF200Response; -import org.openhab.binding.velux.bridge.slip.util.Packet; -import org.openhab.binding.velux.things.VeluxKLFAPI.Command; -import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.bridge.common.RunProductCommand; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,8 +31,8 @@ * itself. *

      * As 3rd level class it defines informations how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the interface {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol * SlipBridgeCommunicationProtocol}. *

      * Methods in addition to the mentioned interface: diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductDiscovery.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductDiscovery.java similarity index 88% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductDiscovery.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductDiscovery.java index f3a7228b5bad0..3cbcf681e53f5 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductDiscovery.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductDiscovery.java @@ -10,14 +10,14 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip; +package org.openhab.binding.velux.internal.bridge.slip; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.RunProductDiscovery; -import org.openhab.binding.velux.bridge.slip.util.KLF200Response; -import org.openhab.binding.velux.bridge.slip.util.Packet; -import org.openhab.binding.velux.things.VeluxKLFAPI.Command; -import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.bridge.common.RunProductDiscovery; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,8 +28,8 @@ *

      * * It defines informations how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol * SlipBridgeCommunicationProtocol}. * * @author Guenther Schreiner - Initial contribution. @@ -43,7 +43,7 @@ * Implementing the protocol-independent class {@link RunProductDiscovery}. *

      * As 3rd level class it defines informations how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} * as described by the interface {@link SlipBridgeCommunicationProtocol}. *

      * There are no methods in addition to the mentioned interface. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductIdentification.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductIdentification.java similarity index 91% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductIdentification.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductIdentification.java index 59aaa546f4563..e37d5a4fc054b 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductIdentification.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductIdentification.java @@ -10,16 +10,16 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip; +package org.openhab.binding.velux.internal.bridge.slip; import java.util.Random; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.RunProductIdentification; -import org.openhab.binding.velux.bridge.slip.util.KLF200Response; -import org.openhab.binding.velux.bridge.slip.util.Packet; -import org.openhab.binding.velux.things.VeluxKLFAPI.Command; -import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.bridge.common.RunProductIdentification; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,8 +31,8 @@ * itself. *

      * As 3rd level class it defines informations how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol * SlipBridgeCommunicationProtocol}. *

      * Methods in addition to the mentioned interface: diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductSearch.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductSearch.java similarity index 87% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductSearch.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductSearch.java index d8ca427f00ccf..496ed06678a10 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunProductSearch.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductSearch.java @@ -10,14 +10,14 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip; +package org.openhab.binding.velux.internal.bridge.slip; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.RunProductSearch; -import org.openhab.binding.velux.bridge.slip.util.Packet; -import org.openhab.binding.velux.things.VeluxGwState; -import org.openhab.binding.velux.things.VeluxKLFAPI.Command; -import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.bridge.common.RunProductSearch; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxGwState; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,7 +31,7 @@ * Implementing the protocol-independent class {@link RunProductSearch}. *

      * As 3rd level class it defines informations how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} * as described by the interface {@link SlipBridgeCommunicationProtocol}. *

      * Methods in addition to the mentioned interface: diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunScene.java similarity index 95% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunScene.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunScene.java index 7ebc62a0dc98c..5a8b0923bfdbf 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCrunScene.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunScene.java @@ -10,16 +10,16 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip; +package org.openhab.binding.velux.internal.bridge.slip; import java.util.Random; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.RunScene; -import org.openhab.binding.velux.bridge.slip.util.KLF200Response; -import org.openhab.binding.velux.bridge.slip.util.Packet; -import org.openhab.binding.velux.things.VeluxKLFAPI.Command; -import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.bridge.common.RunScene; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,8 +31,8 @@ * itself. *

      * As 3rd level class it defines informations how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the interface {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol * SlipBridgeCommunicationProtocol}. *

      * Methods in addition to the mentioned interface: diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCsetHouseStatusMonitor.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetHouseStatusMonitor.java similarity index 88% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCsetHouseStatusMonitor.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetHouseStatusMonitor.java index 55dbe4f57c31f..27044aaaa086d 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCsetHouseStatusMonitor.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetHouseStatusMonitor.java @@ -10,14 +10,14 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip; +package org.openhab.binding.velux.internal.bridge.slip; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.SetHouseStatusMonitor; -import org.openhab.binding.velux.bridge.slip.util.KLF200Response; -import org.openhab.binding.velux.bridge.slip.util.Packet; -import org.openhab.binding.velux.things.VeluxKLFAPI.Command; -import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.bridge.common.SetHouseStatusMonitor; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,8 +29,8 @@ * itself. *

      * As 3rd level class it defines informations how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the interface {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol * SlipBridgeCommunicationProtocol}. *

      * Methods in addition to the mentioned interface: diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetLimitation.java new file mode 100644 index 0000000000000..7cc55c04e5abe --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetLimitation.java @@ -0,0 +1,320 @@ +/** + * Copyright (c) 2010-2019 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.velux.internal.bridge.slip; + +import java.util.Random; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.GetProductLimitation; +import org.openhab.binding.velux.internal.bridge.common.SetProductLimitation; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.things.VeluxProductPosition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Retrieve Product + *

      + * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

      + * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link SlipBridgeCommunicationProtocol}. + *

      + * Methods in addition to the mentioned interface: + *

        + *
      • {@link #setActuatorIdAndMinimumLimitation(int,int)} to set the lower limitation of one specific product.
      • + *
      • {@link #setActuatorIdAndMaximumLimitation(int,int)} to set the higher limitation of one specific product.
      • + *
      + * + * @see GetProductLimitation + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCsetLimitation extends SetProductLimitation implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCsetLimitation.class); + + private static final String DESCRIPTION = "Modify Actuator Limitation"; + private static final Command COMMAND = Command.GW_SET_LIMITATION_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private int reqSessionID = 0; + private int reqCommandOriginator = 8; // SAAC + private int reqPriorityLevel = 5; // Comfort Level 2 + private int reqIndexArrayCount = 1; // One node will be addressed + private int reqIndexArray01 = 1; // This is the node + private int reqParameterID = 0; // MP = Main parameter + private int reqLimitationValueMin = VeluxProductPosition.VPP_VELUX_IGNORE; // will be set lateron + private int reqLimitationValueMax = VeluxProductPosition.VPP_VELUX_IGNORE; // will be set lateron + private int reqLimitationTime = 0; // 0 = 30 seconds, 1 = 60 seconds, ..., 253 = unlimited, 254 = clear entry for + // the Master, 255 = clear all + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Constructor Method + */ + + public SCsetLimitation() { + logger.debug("SCsetLimitation(Constructor) called."); + Random rand = new Random(); + reqSessionID = rand.nextInt(0x0fff); + logger.debug("SCsetLimitation(): starting sessions with the random number {}.", reqSessionID); + } + + /* + * =========================================================== + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + Packet request = new Packet(new byte[31]); + reqSessionID = (reqSessionID + 1) & 0xffff; + request.setTwoByteValue(0, reqSessionID); + request.setOneByteValue(2, reqCommandOriginator); + request.setOneByteValue(3, reqPriorityLevel); + request.setOneByteValue(4, reqIndexArrayCount); + request.setOneByteValue(5, reqIndexArray01); + request.setOneByteValue(25, reqParameterID); + request.setTwoByteValue(26, reqLimitationValueMin); + request.setTwoByteValue(28, reqLimitationValueMax); + request.setOneByteValue(30, reqLimitationTime); + logger.trace("getRequestDataAsArrayOfBytes(): ntfSessionID={}.", reqSessionID); + logger.trace("getRequestDataAsArrayOfBytes(): reqCommandOriginator={}.", reqCommandOriginator); + logger.trace("getRequestDataAsArrayOfBytes(): reqPriorityLevel={}.", reqPriorityLevel); + logger.trace("getRequestDataAsArrayOfBytes(): reqIndexArrayCount={}.", reqIndexArrayCount); + logger.trace("getRequestDataAsArrayOfBytes(): reqIndexArray01={}.", reqIndexArray01); + logger.trace("getRequestDataAsArrayOfBytes(): reqParameterID={}.", reqParameterID); + logger.trace("getRequestDataAsArrayOfBytes(): reqLimitationValueMin={}.", reqLimitationValueMin); + logger.trace("getRequestDataAsArrayOfBytes(): reqLimitationValueMax={}.", reqLimitationValueMax); + logger.trace("getRequestDataAsArrayOfBytes(): reqLimitationTime={}.", reqLimitationTime); + requestData = request.toByteArray(); + logger.trace("getRequestDataAsArrayOfBytes() data is {}.", new Packet(requestData).toString()); + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_SET_LIMITATION_CFM: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 3)) { + finished = true; + break; + } + int cfmSessionID = responseData.getTwoByteValue(0); + int cfmStatus = responseData.getOneByteValue(2); + switch (cfmStatus) { + case 0: + logger.info("setResponse(): returned status: Error – Command rejected."); + finished = true; + break; + case 1: + logger.debug("setResponse(): returned status: OK - Command is accepted."); + if (!KLF200Response.check4matchingSessionID(logger, cfmSessionID, reqSessionID)) { + finished = true; + } + break; + default: + logger.warn("setResponse(): returned status={} (not defined).", cfmStatus); + finished = true; + break; + } + break; + + case GW_LIMITATION_STATUS_NTF: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 10)) { + break; + } + // Extracting information items + int ntfSessionID = responseData.getTwoByteValue(0); + int ntfNodeID = responseData.getOneByteValue(2); + int ntfParameterID = responseData.getOneByteValue(3); + int ntfMinValue = responseData.getTwoByteValue(4); + int ntfMaxValue = responseData.getTwoByteValue(6); + int ntfLimitationOriginator = responseData.getOneByteValue(8); + int ntfLimitationTime = responseData.getOneByteValue(9); + logger.trace("setResponse(): nodeId={}.", ntfNodeID); + logger.trace("setResponse(): ntfParameterID={}.", ntfParameterID); + logger.trace("setResponse(): ntfMinValue={}.", ntfMinValue); + logger.trace("setResponse(): ntfMaxValue={}.", ntfMaxValue); + logger.trace("setResponse(): ntfLimitationOriginator={}.", ntfLimitationOriginator); + logger.trace("setResponse(): ntfLimitationTime={}.", ntfLimitationTime); + + if (!KLF200Response.check4matchingSessionID(logger, ntfSessionID, reqSessionID)) { + finished = true; + break; + } + success = true; + if (!isSequentialEnforced) { + logger.trace( + "setResponse(): skipping wait for more packets as sequential processing is not enforced."); + finished = true; + } + break; + + case GW_COMMAND_RUN_STATUS_NTF: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 13)) { + finished = true; + break; + } + ntfSessionID = responseData.getTwoByteValue(0); + int ntfStatusiD = responseData.getOneByteValue(2); + int ntfIndex = responseData.getOneByteValue(3); + int ntfNodeParameter = responseData.getOneByteValue(4); + int ntfParameterValue = responseData.getTwoByteValue(5); + int ntfRunStatus = responseData.getOneByteValue(7); + int ntfStatusReply = responseData.getOneByteValue(8); + int ntfInformationCode = responseData.getFourByteValue(9); + // Extracting information items + logger.trace("setResponse(): ntfSessionID={} (requested {}).", ntfSessionID, reqSessionID); + logger.trace("setResponse(): ntfStatusiD={}.", ntfStatusiD); + logger.trace("setResponse(): ntfIndex={}.", ntfIndex); + logger.trace("setResponse(): ntfNodeParameter={}.", ntfNodeParameter); + logger.trace("setResponse(): ntfParameterValue={}.", ntfParameterValue); + logger.trace("setResponse(): ntfRunStatus={}.", ntfRunStatus); + logger.trace("setResponse(): ntfStatusReply={}.", ntfStatusReply); + logger.trace("setResponse(): ntfInformationCode={}.", ntfInformationCode); + + if (!KLF200Response.check4matchingSessionID(logger, ntfSessionID, reqSessionID)) { + finished = true; + } + switch (ntfRunStatus) { + case 0: + logger.debug("setResponse(): returned ntfRunStatus: EXECUTION_COMPLETED."); + success = true; + break; + case 1: + logger.info("setResponse(): returned ntfRunStatus: EXECUTION_FAILED."); + finished = true; + break; + case 2: + logger.debug("setResponse(): returned ntfRunStatus: EXECUTION_ACTIVE."); + break; + default: + logger.warn("setResponse(): returned ntfRunStatus={} (not defined).", ntfRunStatus); + finished = true; + break; + } + if (!isSequentialEnforced) { + logger.trace( + "setResponse(): skipping wait for more packets as sequential processing is not enforced."); + success = true; + finished = true; + } + break; + + case GW_SESSION_FINISHED_NTF: + finished = true; + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 2)) { + break; + } + int finishedNtfSessionID = responseData.getTwoByteValue(0); + if (!KLF200Response.check4matchingSessionID(logger, finishedNtfSessionID, reqSessionID)) { + break; + } + logger.debug("setResponse(): finishedNtfSessionID={}.", finishedNtfSessionID); + success = true; + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to the interface {@link BridgeCommunicationProtocol} + * and the abstract class {@link SetProductLimitation} + */ + + @Override + public void setActuatorIdAndMinimumLimitation(int nodeId, int limitation) { + logger.trace("setActuatorIdAndLimitationTypeAndLimitation({},{}) called.", nodeId, limitation); + reqIndexArray01 = nodeId; + reqLimitationValueMin = limitation; + reqLimitationValueMax = VeluxProductPosition.VPP_VELUX_IGNORE; + return; + } + + @Override + public void setActuatorIdAndMaximumLimitation(int nodeId, int limitation) { + logger.trace("setActuatorIdAndLimitationTypeAndLimitation({},{}) called.", nodeId, limitation); + reqIndexArray01 = nodeId; + reqLimitationValueMin = VeluxProductPosition.VPP_VELUX_IGNORE; + reqLimitationValueMax = limitation; + return; + } + + public void setActuatorIdAndResetLimitation(int nodeId) { + logger.trace("setActuatorIdAndResetLimitation({}) called.", nodeId); + reqIndexArray01 = nodeId; + reqLimitationValueMin = VeluxProductPosition.VPP_VELUX_IGNORE; + reqLimitationValueMax = VeluxProductPosition.VPP_VELUX_IGNORE; + return; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCsetSceneVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetSceneVelocity.java similarity index 90% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCsetSceneVelocity.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetSceneVelocity.java index e204f61a30fd0..4b808650feb53 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SCsetSceneVelocity.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetSceneVelocity.java @@ -10,15 +10,15 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip; +package org.openhab.binding.velux.internal.bridge.slip; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.SetSceneVelocity; -import org.openhab.binding.velux.bridge.slip.util.KLF200Response; -import org.openhab.binding.velux.bridge.slip.util.Packet; -import org.openhab.binding.velux.things.VeluxKLFAPI.Command; -import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; -import org.openhab.binding.velux.things.VeluxProductVelocity; +import org.openhab.binding.velux.internal.bridge.common.SetSceneVelocity; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxProductVelocity; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,8 +30,8 @@ * itself. *

      * As 3rd level class it defines informations how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the interface {@link org.openhab.binding.velux.bridge.slip.SlipBridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol * SlipBridgeCommunicationProtocol}. *

      * Methods in addition to the mentioned interface: diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SlipBridgeAPI.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeAPI.java similarity index 80% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SlipBridgeAPI.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeAPI.java index 8f1fc9d945ba8..f51cff941415a 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SlipBridgeAPI.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeAPI.java @@ -10,30 +10,31 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip; +package org.openhab.binding.velux.internal.bridge.slip; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.velux.bridge.VeluxBridgeInstance; -import org.openhab.binding.velux.bridge.common.BridgeAPI; -import org.openhab.binding.velux.bridge.common.GetDeviceStatus; -import org.openhab.binding.velux.bridge.common.GetFirmware; -import org.openhab.binding.velux.bridge.common.GetHouseStatus; -import org.openhab.binding.velux.bridge.common.GetLANConfig; -import org.openhab.binding.velux.bridge.common.GetProduct; -import org.openhab.binding.velux.bridge.common.GetProductLimitation; -import org.openhab.binding.velux.bridge.common.GetProducts; -import org.openhab.binding.velux.bridge.common.GetScenes; -import org.openhab.binding.velux.bridge.common.GetWLANConfig; -import org.openhab.binding.velux.bridge.common.Login; -import org.openhab.binding.velux.bridge.common.Logout; -import org.openhab.binding.velux.bridge.common.RunProductCommand; -import org.openhab.binding.velux.bridge.common.RunProductDiscovery; -import org.openhab.binding.velux.bridge.common.RunProductIdentification; -import org.openhab.binding.velux.bridge.common.RunProductSearch; -import org.openhab.binding.velux.bridge.common.RunScene; -import org.openhab.binding.velux.bridge.common.SetHouseStatusMonitor; -import org.openhab.binding.velux.bridge.common.SetSceneVelocity; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeInstance; +import org.openhab.binding.velux.internal.bridge.common.BridgeAPI; +import org.openhab.binding.velux.internal.bridge.common.GetDeviceStatus; +import org.openhab.binding.velux.internal.bridge.common.GetFirmware; +import org.openhab.binding.velux.internal.bridge.common.GetHouseStatus; +import org.openhab.binding.velux.internal.bridge.common.GetLANConfig; +import org.openhab.binding.velux.internal.bridge.common.GetProduct; +import org.openhab.binding.velux.internal.bridge.common.GetProductLimitation; +import org.openhab.binding.velux.internal.bridge.common.GetProducts; +import org.openhab.binding.velux.internal.bridge.common.GetScenes; +import org.openhab.binding.velux.internal.bridge.common.GetWLANConfig; +import org.openhab.binding.velux.internal.bridge.common.Login; +import org.openhab.binding.velux.internal.bridge.common.Logout; +import org.openhab.binding.velux.internal.bridge.common.RunProductCommand; +import org.openhab.binding.velux.internal.bridge.common.RunProductDiscovery; +import org.openhab.binding.velux.internal.bridge.common.RunProductIdentification; +import org.openhab.binding.velux.internal.bridge.common.RunProductSearch; +import org.openhab.binding.velux.internal.bridge.common.RunScene; +import org.openhab.binding.velux.internal.bridge.common.SetHouseStatusMonitor; +import org.openhab.binding.velux.internal.bridge.common.SetProductLimitation; +import org.openhab.binding.velux.internal.bridge.common.SetSceneVelocity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -99,6 +100,7 @@ class SlipBridgeAPI implements BridgeAPI { private static final RunProductSearch RUNPRODUCTSEARCH = new SCrunProductSearch(); private static final RunScene RUNSCENE = new SCrunScene(); private static final SetHouseStatusMonitor SETHOUSESTATUSMONITOR = new SCsetHouseStatusMonitor(); + private static final SetProductLimitation SETPRODUCTLIMITATION = new SCsetLimitation(); private static final SetSceneVelocity SETSCENEVELOCITY = new SCsetSceneVelocity(); /** @@ -144,6 +146,11 @@ public GetLANConfig getLANConfig() { return GETPRODUCTLIMITATION; } + @Override + public @Nullable SetProductLimitation setProductLimitation() { + return SETPRODUCTLIMITATION; + } + @Override public GetProducts getProducts() { return GETPRODUCTS; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SlipBridgeCommunicationProtocol.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeCommunicationProtocol.java similarity index 93% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SlipBridgeCommunicationProtocol.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeCommunicationProtocol.java index 663b2240e0d1a..64840287066e2 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SlipBridgeCommunicationProtocol.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeCommunicationProtocol.java @@ -10,11 +10,11 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip; +package org.openhab.binding.velux.internal.bridge.slip; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.common.BridgeCommunicationProtocol; -import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; /** * Protocol specific bridge communication supported by the Velux bridge: diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SlipVeluxBridge.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipVeluxBridge.java similarity index 83% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SlipVeluxBridge.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipVeluxBridge.java index 52394c05942bd..ac8fc3bff931d 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/SlipVeluxBridge.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipVeluxBridge.java @@ -10,23 +10,25 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip; +package org.openhab.binding.velux.internal.bridge.slip; import java.text.ParseException; import java.util.TreeSet; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.VeluxBridge; -import org.openhab.binding.velux.bridge.VeluxBridgeInstance; -import org.openhab.binding.velux.bridge.common.BridgeAPI; -import org.openhab.binding.velux.bridge.common.BridgeCommunicationProtocol; -import org.openhab.binding.velux.bridge.slip.io.Connection; -import org.openhab.binding.velux.bridge.slip.util.Packet; -import org.openhab.binding.velux.bridge.slip.util.SlipEncoding; -import org.openhab.binding.velux.bridge.slip.util.SlipRFC1055; -import org.openhab.binding.velux.things.VeluxKLFAPI.Command; -import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; -import org.openhab.binding.velux.things.VeluxProduct.ProductBridgeIndex; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.VeluxBridge; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeInstance; +import org.openhab.binding.velux.internal.bridge.common.BridgeAPI; +import org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol; +import org.openhab.binding.velux.internal.bridge.slip.io.Connection; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.bridge.slip.utils.SlipEncoding; +import org.openhab.binding.velux.internal.bridge.slip.utils.SlipRFC1055; +import org.openhab.binding.velux.internal.development.Threads; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,7 +49,7 @@ *

        *
      • {@link #bridgeDirectCommunicate} for SLIP-based communication.
      • *
      • {@link #bridgeAPI} for return all defined protocol-specific implementations which are provided by - * {@link org.openhab.binding.velux.bridge.slip.SlipBridgeAPI SlipBridgeAPI}.
      • + * {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeAPI SlipBridgeAPI}. *
      * * @author Guenther Schreiner - Initial contribution. @@ -56,6 +58,26 @@ public class SlipVeluxBridge extends VeluxBridge { private final Logger logger = LoggerFactory.getLogger(SlipVeluxBridge.class); + /* + * *************************** + * ***** Private Objects ***** + */ + + /** + * Timeout for sequence of one request with (optional multiple) responses. + *

      + * This can only happen if there is an unexpected (undocumented) occurrence of events + * which has to be discussed along the Velux API documentation. + */ + static final long COMMUNICATION_TIMEOUT_MSECS = 60000L; + /** + * Wait interval within sequence after the request and no immediate response. + *

      + * This can happen if the bridge is busy due to a specific command to query and collect information from other + * devices via the io-homecontrol protocol. + */ + static final long COMMUNICATION_RETRY_MSECS = 5000L; + /* * *************************** * ***** Private Objects ***** @@ -66,7 +88,7 @@ public class SlipVeluxBridge extends VeluxBridge { /** * Handler passing the interface methods to other classes. - * Can be accessed via method {@link org.openhab.binding.velux.bridge.common.BridgeAPI BridgeAPI}. + * Can be accessed via method {@link org.openhab.binding.velux.internal.bridge.common.BridgeAPI BridgeAPI}. */ private BridgeAPI bridgeAPI; @@ -143,7 +165,7 @@ protected boolean bridgeDirectCommunicate(BridgeCommunicationProtocol communicat * @return timestamp in milliseconds. */ @Override - public synchronized long lastCommunication() { + public long lastCommunication() { return connection.lastCommunication(); } @@ -154,7 +176,7 @@ public synchronized long lastCommunication() { * @return timestamp in milliseconds. */ @Override - public synchronized long lastSuccessfulCommunication() { + public long lastSuccessfulCommunication() { return connection.lastSuccessfulCommunication(); } @@ -175,6 +197,9 @@ private synchronized boolean bridgeDirectCommunicate(SlipBridgeCommunicationProt useAuthentication ? "" : "un"); assert this.bridgeInstance.veluxBridgeConfiguration().protocol.contentEquals("slip"); + + long communicationStartInMSecs = System.currentTimeMillis(); + boolean isSequentialEnforced = this.bridgeInstance.veluxBridgeConfiguration().isSequentialEnforced; boolean isProtocolTraceEnabled = this.bridgeInstance.veluxBridgeConfiguration().isProtocolTraceEnabled; @@ -184,11 +209,22 @@ private synchronized boolean bridgeDirectCommunicate(SlipBridgeCommunicationProt // For further use at different logging statements String commandString = Command.get(command).toString(); - logger.debug("bridgeDirectCommunicate({},{}authenticated) called.", commandString, - useAuthentication ? "" : "un"); + if (isProtocolTraceEnabled) { + Threads.findDeadlocked(); + } + + logger.debug("bridgeDirectCommunicate({},{}authenticated) initiated by {}.", commandString, + useAuthentication ? "" : "un", Thread.currentThread()); boolean success = false; communication: do { + if (communicationStartInMSecs + COMMUNICATION_TIMEOUT_MSECS < System.currentTimeMillis()) { + logger.warn( + "{} bridgeDirectCommunicate({}): communication handshake failed (unexpected sequence of requests/responses).", + VeluxBindingConstants.BINDING_VALUES_SEPARATOR, communication.name()); + break; + } + // Special handling if (Command.get(command) == Command.GW_OPENHAB_CLOSE) { logger.trace("bridgeDirectCommunicate(): special command: shutting down connection."); @@ -219,8 +255,11 @@ private synchronized boolean bridgeDirectCommunicate(SlipBridgeCommunicationProt logger.trace("bridgeDirectCommunicate(): transportEncoding={}.", t.toString()); sendBytes = new SlipRFC1055().encode(t.toMessage()); } - do { + if (communicationStartInMSecs + COMMUNICATION_TIMEOUT_MSECS < System.currentTimeMillis()) { + logger.warn("bridgeDirectCommunicate(): receive takes too long. Please report to maintainer."); + break communication; + } byte[] receivedPacket; try { if (sendBytes.length > 0) { @@ -326,8 +365,8 @@ private synchronized boolean bridgeDirectCommunicate(SlipBridgeCommunicationProt case GW_COMMAND_REMAINING_TIME_NTF: case GW_SESSION_FINISHED_NTF: if (!isSequentialEnforced) { - logger.trace("bridgeDirectCommunicate(): response ignored due to activated parallelism."); - logger.trace("bridgeDirectCommunicate(): continue with receiving."); + logger.trace( + "bridgeDirectCommunicate(): response ignored due to activated parallelism, continue with receiving."); continue; } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/io/Connection.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/Connection.java similarity index 92% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/io/Connection.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/Connection.java index 6f2d14a468e9f..49bc055116149 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/io/Connection.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/Connection.java @@ -10,14 +10,14 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip.io; +package org.openhab.binding.velux.internal.bridge.slip.io; import java.io.IOException; import java.net.ConnectException; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.bridge.VeluxBridgeInstance; -import org.openhab.binding.velux.bridge.slip.util.Packet; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeInstance; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -77,6 +77,8 @@ public synchronized byte[] io(VeluxBridgeInstance bridgeInstance, byte[] request throws ConnectException, IOException { logger.trace("io() called."); + lastCommunicationInMSecs = System.currentTimeMillis(); + /** Local handles */ int retryCount = 0; IOException lastIOE = new IOException("Unexpected I/O exception."); @@ -88,17 +90,15 @@ public synchronized byte[] io(VeluxBridgeInstance bridgeInstance, byte[] request // From configuration String host = bridgeInstance.veluxBridgeConfiguration().ipAddress; int port = bridgeInstance.veluxBridgeConfiguration().tcpPort; + int timeoutMsecs = bridgeInstance.veluxBridgeConfiguration().timeoutMsecs; logger.trace("io(): connecting to {}:{}.", host, port); connectivity = new SSLconnection(host, port); + connectivity.setTimeout(timeoutMsecs); } catch (ConnectException ce) { - // ToDo: eliminate obsolete lines - // logger.warn("io(): raised a non-recoverable error during connection setup: - // {}.",ce.getMessage()); - // throw ce; throw new ConnectException(String .format("raised a non-recoverable error during connection setup: %s", ce.getMessage())); - } catch (Exception e) { + } catch (IOException e) { logger.warn("io(): raised an error during connection setup: {}.", e.getMessage()); lastIOE = new IOException(String.format("error during connection setup: %s.", e.getMessage())); continue; @@ -114,8 +114,8 @@ public synchronized byte[] io(VeluxBridgeInstance bridgeInstance, byte[] request if (connectivity.isReady()) { connectivity.send(request); } - } catch (Exception e) { - logger.warn("io(): raised an error during sending: {}.", e.getMessage()); + } catch (IOException e) { + logger.info("io(): raised an error during sending: {}.", e.getMessage()); break; } @@ -145,7 +145,7 @@ public synchronized byte[] io(VeluxBridgeInstance bridgeInstance, byte[] request logger.trace("io() finished."); return packet; } catch (IOException ioe) { - logger.warn("io(): Exception occurred during I/O: {}.", ioe.getMessage()); + logger.info("io(): Exception occurred during I/O: {}.", ioe.getMessage()); lastIOE = ioe; // Error Retries with Exponential Backoff long waitTime = ((long) Math.pow(2, retryCount) @@ -231,7 +231,7 @@ public synchronized void resetConnection() { connectivity.close(); } } catch (IOException e) { - logger.warn("resetConnection(): raised an error during connection close: {}.", e.getMessage()); + logger.info("resetConnection(): raised an error during connection close: {}.", e.getMessage()); } logger.trace("resetConnection(): clearing authentication token."); } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/DataInputStreamWithTimeout.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/DataInputStreamWithTimeout.java new file mode 100644 index 0000000000000..317cedd947728 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/DataInputStreamWithTimeout.java @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2010-2019 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.velux.internal.bridge.slip.io; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * This is an extension of {@link java.io.DataInputStream}, which adds timeouts to receive operation. + *

      + * A data input stream lets an application read primitive Java data + * types from an underlying input stream in a machine-independent + * way. An application uses a data output stream to write data that + * can later be read by a data input stream. + *

      + * For an in-depth discussion, see: + * https://stackoverflow.com/questions/804951/is-it-possible-to-read-from-a-inputstream-with-a-timeout + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class DataInputStreamWithTimeout extends DataInputStream { + + /* + * *************************** + * ***** Private Objects ***** + */ + + /** + * Executor for asynchronous read command + */ + ExecutorService executor = Executors.newFixedThreadPool(2); + + /** + * Creates a DataInputStreamWithTimeout that uses the specified + * underlying DataInputStream. + * + * @param in the specified input stream + */ + public DataInputStreamWithTimeout(InputStream in) { + super(in); + } + + /** + * Reads up to len bytes of data from the contained + * input stream into an array of bytes. An attempt is made to read + * as many as len bytes, but a smaller number may be read, + * possibly zero. The number of bytes actually read is returned as an + * integer. + * + *

      + * This method blocks until input data is available, end of file is + * detected, or an exception is thrown until the given timeout. + * + *

      + * If len is zero, then no bytes are read and + * 0 is returned; otherwise, there is an attempt to read at + * least one byte. If no byte is available because the stream is at end of + * file, the value -1 is returned; otherwise, at least one + * byte is read and stored into b. + * + *

      + * The first byte read is stored into element b[off], the + * next one into b[off+1], and so on. The number of bytes read + * is, at most, equal to len. Let k be the number of + * bytes actually read; these bytes will be stored in elements + * b[off] through b[off+k-1], + * leaving elements b[off+k] through + * b[off+len-1] unaffected. + * + *

      + * In every case, elements b[0] through + * b[off] and elements b[off+len] through + * b[b.length-1] are unaffected. + * + * @param b the buffer into which the data is read. + * @param off the start offset in the destination array b + * @param len the maximum number of bytes read. + * @param timeoutMSecs the maximum duration of this read before throwing a TimeoutException. + * @return the total number of bytes read into the buffer, or + * -1 if there is no more data because the end + * of the stream has been reached. + * @exception NullPointerException If b is null. + * @exception IndexOutOfBoundsException If off is negative, + * len is negative, or len is greater than + * b.length - off + * @exception IOException if the first byte cannot be read for any reason + * other than end of file, the stream has been closed and the underlying + * input stream does not support reading after close, or another I/O + * error occurs. Additionally it will occur when the timeout happens. + * @see java.io.DataInputStream#read + */ + public synchronized int read(byte b[], int off, int len, int timeoutMSecs) throws IOException { + // Definition of Method which encapsulates the Read of data + Callable readTask = new Callable() { + @Override + public Integer call() throws IOException { + return in.read(b, off, len); + } + }; + try { + Future future = executor.submit(readTask); + return future.get(timeoutMSecs, TimeUnit.MILLISECONDS); + } catch (RejectedExecutionException e) { + throw new IOException("executor failed", e); + } catch (ExecutionException e) { + throw new IOException("execution failed", e); + } catch (InterruptedException e) { + throw new IOException("read interrupted", e); + } catch (TimeoutException e) { + throw new IOException("read timeout", e); + } + + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/io/SSLconnection.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/SSLconnection.java similarity index 91% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/io/SSLconnection.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/SSLconnection.java index a0ac986266b22..df8f564333241 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/io/SSLconnection.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/SSLconnection.java @@ -10,9 +10,8 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip.io; +package org.openhab.binding.velux.internal.bridge.slip.io; -import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ConnectException; @@ -27,7 +26,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.internal.VeluxBindingConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,6 +43,7 @@ *

    • {@link SSLconnection#available} for observation whether there are bytes available,
    • *
    • {@link SSLconnection#receive} for receiving a message from the bridge,
    • *
    • {@link SSLconnection#close} for tearing down the connection.
    • + *
    • {@link SSLconnection#setTimeout} for adapting communication parameters.
    • *
    * * @author Guenther Schreiner - Initial contribution. @@ -65,7 +65,8 @@ class SSLconnection { private boolean ready = false; private @Nullable SSLSocket socket; private @Nullable DataOutputStream dOut; - private @Nullable DataInputStream dIn; + private @Nullable DataInputStreamWithTimeout dIn; + private int ioTimeoutMSecs = 60000; /** * Fake trust manager to suppress any certificate errors, @@ -129,7 +130,7 @@ public void checkServerTrusted(X509Certificate @Nullable [] arg0, @Nullable Stri if (socketX != null) { socketX.startHandshake(); dOut = new DataOutputStream(socketX.getOutputStream()); - dIn = new DataInputStream(socketX.getInputStream()); + dIn = new DataInputStreamWithTimeout(socketX.getInputStream()); ready = true; socket = socketX; } @@ -202,7 +203,7 @@ synchronized byte[] receive() throws IOException { } byte[] message = new byte[CONNECTION_BUFFER_SIZE]; @SuppressWarnings("null") - int messageLength = dIn.read(message, 0, message.length); + int messageLength = dIn.read(message, 0, message.length, ioTimeoutMSecs); byte[] packet = new byte[messageLength]; System.arraycopy(message, 0, packet, 0, messageLength); if (logger.isTraceEnabled()) { @@ -225,7 +226,7 @@ synchronized void close() throws IOException { ready = false; logger.info("Shutting down Velux bridge connection."); // Just for avoidance of Potential null pointer access - DataInputStream dInX = dIn; + DataInputStreamWithTimeout dInX = dIn; if (dInX != null) { dInX.close(); } @@ -242,4 +243,14 @@ synchronized void close() throws IOException { logger.trace("close() finished."); } + /** + * Parameter modification. + * + * @param timeoutMSecs the maximum duration in milliseconds for read operations. + */ + void setTimeout(int timeoutMSecs) { + logger.debug("setTimeout() set timeout to {} milliseconds.", timeoutMSecs); + ioTimeoutMSecs = timeoutMSecs; + } + } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/io/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/package-info.java similarity index 89% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/io/package-info.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/package-info.java index 1a1994d1e7ad5..6f714906883ae 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/io/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/package-info.java @@ -15,4 +15,4 @@ * * @author Guenther Schreiner - Initial contribution */ -package org.openhab.binding.velux.bridge.slip.io; +package org.openhab.binding.velux.internal.bridge.slip.io; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/package-info.java similarity index 90% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/package-info.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/package-info.java index 5c005546541ad..63549dfde14ce 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/package-info.java @@ -15,4 +15,4 @@ * * @author Guenther Schreiner - Initial contribution */ -package org.openhab.binding.velux.bridge.slip; +package org.openhab.binding.velux.internal.bridge.slip; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/KLF200Response.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/KLF200Response.java similarity index 97% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/KLF200Response.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/KLF200Response.java index 37a636dc3f651..4e0873f016013 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/KLF200Response.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/KLF200Response.java @@ -10,11 +10,11 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip.util; +package org.openhab.binding.velux.internal.bridge.slip.utils; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.things.VeluxKLFAPI.Command; -import org.openhab.binding.velux.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; import org.slf4j.Logger; /** diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/Packet.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/Packet.java similarity index 89% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/Packet.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/Packet.java index 5de338248fd39..8150f784baada 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/Packet.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/Packet.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip.util; +package org.openhab.binding.velux.internal.bridge.slip.utils; import java.util.Arrays; @@ -63,7 +63,8 @@ public class Packet { */ /** - * Constructor: Create a {@link org.openhab.binding.velux.bridge.slip.util.Packet Packet} out of a sequence of + * Constructor: Create a {@link org.openhab.binding.velux.internal.bridge.slip.utils.Packet Packet} out of a + * sequence of * bytes. * * @param thisData Packet as Array of bytes. @@ -78,17 +79,18 @@ public Packet(byte[] thisData) { */ /** - * Returns the length of the {@link org.openhab.binding.velux.bridge.slip.util.Packet Packet}.. + * Returns the length of the {@link org.openhab.binding.velux.internal.bridge.slip.utils.Packet Packet}.. * * @return packetLength * of type int. */ - public int length() { // NO_UCD (unused code) + public int length() { return data.length; } /** - * Returns the complete {@link org.openhab.binding.velux.bridge.slip.util.Packet Packet} as sequence of bytes. + * Returns the complete {@link org.openhab.binding.velux.internal.bridge.slip.utils.Packet Packet} as sequence of + * bytes. * * @return packet * of type Array-of-byte. @@ -98,7 +100,8 @@ public byte[] toByteArray() { } /** - * Returns a part of the {@link org.openhab.binding.velux.bridge.slip.util.Packet Packet} as sequence of bytes + * Returns a part of the {@link org.openhab.binding.velux.internal.bridge.slip.utils.Packet Packet} as sequence of + * bytes * starting at position (n) up to the position (n+length-1). * * @param position Position (n) within the packet. @@ -110,13 +113,13 @@ public byte[] getByteArray(int position, int length) { } /** - * Returns the complete {@link org.openhab.binding.velux.bridge.slip.util.Packet Packet} + * Returns the complete {@link org.openhab.binding.velux.internal.bridge.slip.utils.Packet Packet} * as human-readable sequence of hex bytes each separated by the given separator. * * @param separator as of Type String. * @return packetString of type String. */ - public String toString(String separator) { // NO_UCD (use default) + public String toString(String separator) { StringBuilder sb = new StringBuilder(); for (byte b : this.data) { sb.append(String.format("%02X", b)); @@ -129,7 +132,7 @@ public String toString(String separator) { // NO_UCD (use default) } /** - * Returns the complete {@link org.openhab.binding.velux.bridge.slip.util.Packet Packet} + * Returns the complete {@link org.openhab.binding.velux.internal.bridge.slip.utils.Packet Packet} * as human-readable sequence of hex bytes each separated by a blank. * * @return packetString of type String. @@ -209,7 +212,7 @@ public int getFourByteValue(int position) { * @param position Position (n) within the packet. * @param value of type int. */ - public void setFourByteValue(int position, int value) { // NO_UCD (unused code) + public void setFourByteValue(int position, int value) { data[position] = (byte) ((value >>> 24) & 0xFF); data[position + 1] = (byte) ((value >>> 16) & 0xFF); data[position + 2] = (byte) ((value >>> 8) & 0xFF); @@ -240,7 +243,7 @@ public String getString(int position, int length) { * @param position Position (n) within the packet. * @param text of type String. */ - public void setString(int position, String text) { // NO_UCD (unused code) + public void setString(int position, String text) { System.arraycopy(text, 0, data, 0, text.length()); } @@ -259,7 +262,7 @@ static String shortToString(int oneByte) { return String.format("%02X", oneByte); } - static int byteArrayToInt(byte[] data) { // NO_UCD (unused code) + static int byteArrayToInt(byte[] data) { return data[0] << 24 | (data[1] & 0xff) << 16 | (data[2] & 0xff) << 8 | (data[3] & 0xff); } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/SlipEncoding.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/SlipEncoding.java similarity index 95% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/SlipEncoding.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/SlipEncoding.java index 7c9f77170b7f2..d55108a5dfea8 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/SlipEncoding.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/SlipEncoding.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip.util; +package org.openhab.binding.velux.internal.bridge.slip.utils; import java.nio.ByteBuffer; @@ -24,8 +24,8 @@ * Module semantic: encoding and decoding of frames according to RFC 1055. *

    * It defines informations how to send query and receive answer through the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the {@link org.openhab.binding.velux.bridge.common.BridgeCommunicationProtocol + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol * BridgeCommunicationProtocol}. *

    * Methods available: diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/SlipRFC1055.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/SlipRFC1055.java similarity index 97% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/SlipRFC1055.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/SlipRFC1055.java index fe29672cce9bc..57970eb45a6ac 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/SlipRFC1055.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/SlipRFC1055.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.bridge.slip.util; +package org.openhab.binding.velux.internal.bridge.slip.utils; import java.text.ParseException; @@ -73,7 +73,7 @@ public byte[] encode(byte[] payload) { } packet[packetIndex++] = SLIP_BYTE_END; assert (packetIndex == packet.length); - logger.trace("encode() provides transfer encoding: {}.", new Packet(packet).toString()); + logger.trace("encode() provides transfer encoding: {}.", new Packet(packet)); return packet; } @@ -93,11 +93,9 @@ public byte[] decode(byte[] packet) throws ParseException { if (packet[0] != SLIP_BYTE_END) { throw new ParseException("Unexpected byte at 1st position", 0); } - ; if (packet[packet.length - 1] != SLIP_BYTE_END) { throw new ParseException("Unexpected byte at last position", 0); } - ; int additional = -2; for (int i = 0; i < packet.length; i++) { if (packet[i] == SLIP_BYTE_ESC) { @@ -121,7 +119,7 @@ public byte[] decode(byte[] packet) throws ParseException { payload[packetIndex++] = packet[i]; } } - logger.trace("decode() provides payload: {}.", new Packet(payload).toString()); + logger.trace("decode() provides payload: {}.", new Packet(payload)); return payload; } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/package-info.java similarity index 88% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/package-info.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/package-info.java index 6932e73866d3c..d31d2d31d1c91 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/bridge/slip/util/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/package-info.java @@ -15,4 +15,4 @@ * * @author Guenther Schreiner - Initial contribution */ -package org.openhab.binding.velux.bridge.slip.util; +package org.openhab.binding.velux.internal.bridge.slip.utils; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxBridgeConfiguration.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxBridgeConfiguration.java index 0d5cf1c0e1f62..4aea401bb6b3c 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxBridgeConfiguration.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxBridgeConfiguration.java @@ -17,7 +17,7 @@ /** * The {@link VeluxBridgeConfiguration} is a wrapper for * configuration settings needed to access the - * {@link org.openhab.binding.velux.bridge.VeluxBridgeProvider} + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider} * device. *

    * It contains the factory default values as well. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxThingConfiguration.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxThingConfiguration.java index b0750481cca56..924a17903c4ed 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxThingConfiguration.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxThingConfiguration.java @@ -14,8 +14,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.config.core.Configuration; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.things.VeluxProductSerialNo; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.things.VeluxProductSerialNo; /** * The {@link VeluxThingConfiguration} is a wrapper for @@ -46,7 +46,7 @@ public class VeluxThingConfiguration extends Configuration { * */ @SuppressWarnings("unused") - private String sceneName; + private String sceneName = VeluxBindingConstants.UNKNOWN; /** * {@link #serial} of type {@link String}, identifying a io-homecontrol device by its serial number (i.e. @@ -62,7 +62,7 @@ public class VeluxThingConfiguration extends Configuration { * */ @SuppressWarnings("unused") - private String serial; + private String serial = VeluxProductSerialNo.UNKNOWN; /** * {@link #name} of type {@link String}, identifying a io-homecontrol device by its registration name especially * for somfy as they do not provide a valid serial number. @@ -71,7 +71,7 @@ public class VeluxThingConfiguration extends Configuration { *

    */ @SuppressWarnings("unused") - private String name; + private String name = VeluxBindingConstants.UNKNOWN; /** * {@link #inverted} of type {@link Boolean}, inverts each Channel value. This means 0% will be handled as 100%, * and vice versa, 100% will be handled as 0%. @@ -80,7 +80,7 @@ public class VeluxThingConfiguration extends Configuration { *

    */ @SuppressWarnings("unused") - private Boolean inverted; + private Boolean inverted = false; /** * {@link #velocity} of type {@link String}, describes the intended speed of action. * Possible values are defined within VeluxProductVelocity. @@ -89,7 +89,7 @@ public class VeluxThingConfiguration extends Configuration { *

    */ @SuppressWarnings("unused") - private String velocity; + private String velocity = VeluxBindingConstants.UNKNOWN; /** * {@link #sceneLevels} of type {@link String}, identifying a number of Velux scenes which act together as a virtual @@ -108,7 +108,7 @@ public class VeluxThingConfiguration extends Configuration { * */ @SuppressWarnings("unused") - private String sceneLevels; + private String sceneLevels = VeluxBindingConstants.UNKNOWN; /** * {@link #currentLevel} of type {@link int}, which represents the current shutter level. *

    @@ -116,25 +116,6 @@ public class VeluxThingConfiguration extends Configuration { *

    */ @SuppressWarnings("unused") - private int currentLevel; - - /** - * Default values - should not be modified - */ - public VeluxThingConfiguration() { - // - sceneName = VeluxBindingConstants.UNKNOWN; - // - serial = VeluxProductSerialNo.UNKNOWN; - name = VeluxBindingConstants.UNKNOWN; - inverted = false; - velocity = VeluxBindingConstants.UNKNOWN; - // - sceneLevels = VeluxBindingConstants.UNKNOWN; - currentLevel = 0; - } + private int currentLevel = 0; } -/* - * end-of-internal/config/VeluxThingConfiguration.java - */ diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/development/Threads.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/development/Threads.java new file mode 100644 index 0000000000000..e406d4d52793e --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/development/Threads.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2010-2019 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.velux.internal.development; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This is a helper class for dealing with multiple threads and synchronization. + * + * It provides the following methods: + *
      + *
    • {@link #findDeadlocked} to print the current locking situation.
    • + *
    + *

    + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class Threads { + private static final Logger LOGGER = LoggerFactory.getLogger(Threads.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + /** + * Suppress default constructor for creating a non-instantiable class. + */ + private Threads() { + throw new AssertionError(); + } + + // Class access methods + + /** + * Print the current established locks with the associated threads. + *

    + * Finds cycles of threads that are in deadlock waiting to acquire + * object monitors or ownable synchronizers. + * + * Threads are deadlocked in a cycle waiting for a lock of + * these two types if each thread owns one lock while + * trying to acquire another lock already held + * by another thread in the cycle. + *

    + * This method is designed for troubleshooting use, but not for + * synchronization control. It might be an expensive operation. + * + * @see ThreadMXBean#findDeadlockedThreads + */ + public static void findDeadlocked() { + ThreadMXBean tmx = ManagementFactory.getThreadMXBean(); + long[] ids = tmx.findDeadlockedThreads(); + if (ids != null) { + ThreadInfo[] infos = tmx.getThreadInfo(ids, true, true); + LOGGER.warn("findDeadlocked() The following threads are deadlocked:"); + for (ThreadInfo ti : infos) { + LOGGER.warn("findDeadlocked(): {}.", ti); + } + LOGGER.warn("findDeadlocked() done."); + } + } +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/development/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/development/package-info.java new file mode 100644 index 0000000000000..0f247bf197d8e --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/development/package-info.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2010-2019 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 + */ +/** + * Classes to support development of bindings. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.internal.development; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/discovery/VeluxDiscoveryService.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/VeluxDiscoveryService.java similarity index 66% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/discovery/VeluxDiscoveryService.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/VeluxDiscoveryService.java index 6042cfcbd8ac9..0038fe50b086e 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/discovery/VeluxDiscoveryService.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/VeluxDiscoveryService.java @@ -10,9 +10,9 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.discovery; +package org.openhab.binding.velux.internal.discovery; -import static org.openhab.binding.velux.VeluxBindingConstants.*; +import static org.openhab.binding.velux.internal.VeluxBindingConstants.*; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -20,15 +20,19 @@ import org.eclipse.smarthome.config.discovery.DiscoveryResult; import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; import org.eclipse.smarthome.config.discovery.DiscoveryService; +import org.eclipse.smarthome.core.i18n.LocaleProvider; +import org.eclipse.smarthome.core.i18n.LocationProvider; +import org.eclipse.smarthome.core.i18n.TranslationProvider; import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.ThingUID; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.VeluxBindingProperties; -import org.openhab.binding.velux.handler.VeluxBridgeHandler; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.VeluxBindingProperties; +import org.openhab.binding.velux.internal.handler.VeluxBridgeHandler; +import org.openhab.binding.velux.internal.things.VeluxProduct; +import org.openhab.binding.velux.internal.things.VeluxProductSerialNo; +import org.openhab.binding.velux.internal.things.VeluxScene; +import org.openhab.binding.velux.internal.utils.Localization; import org.openhab.binding.velux.internal.utils.ManifestInformation; -import org.openhab.binding.velux.things.VeluxProduct; -import org.openhab.binding.velux.things.VeluxProductSerialNo; -import org.openhab.binding.velux.things.VeluxScene; import org.osgi.service.component.annotations.Component; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,7 +43,8 @@ * * @author Guenther Schreiner - Initial contribution. */ -// ToDo: check whether an immediate activation is preferable. +// +// To-be-discussed: check whether an immediate activation is preferable. // Might be activated by: // @Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.velux") // @@ -50,13 +55,13 @@ public class VeluxDiscoveryService extends AbstractDiscoveryService implements R private static final int DISCOVER_TIMEOUT_SECONDS = 300; + private Localization localization = Localization.UNKNOWN; private static @Nullable VeluxBridgeHandler bridgeHandler = null; /** - * Initializes the {@link VeluxDiscoveryService} without a {@link VeluxBridgeHandler}. + * Constructor *

    - * This is the main entry point for the OSGI DiscoveryService. - * + * Initializes the {@link VeluxDiscoveryService} without any further information. */ public VeluxDiscoveryService() { super(VeluxBindingConstants.SUPPORTED_THINGS_ITEMS, DISCOVER_TIMEOUT_SECONDS); @@ -64,24 +69,45 @@ public VeluxDiscoveryService() { } /** + * Constructor + *

    * Initializes the {@link VeluxDiscoveryService} with a reference to the well-prepared environment with a * {@link VeluxBridgeHandler}. * * @param bridge Initialized Velux bridge handler. + * @param localizationHandler Initialized localization handler. */ - public VeluxDiscoveryService(VeluxBridgeHandler bridge) { + public VeluxDiscoveryService(VeluxBridgeHandler bridge, Localization localizationHandler) { super(VeluxBindingConstants.SUPPORTED_THINGS_ITEMS, DISCOVER_TIMEOUT_SECONDS); - logger.trace("VeluxDiscoveryService(bridge={}) just initialized.", bridge); - - synchronized (this) { - if (bridgeHandler == null) { - logger.trace("VeluxDiscoveryService(): registering bridge {} for lateron use for Discovery.", bridge); - } else if (!bridge.equals(bridgeHandler)) { - logger.trace("VeluxDiscoveryService(): replacing already registered bridge {} by {}.", bridgeHandler, - bridge); - } - bridgeHandler = bridge; + logger.trace("VeluxDiscoveryService(bridge={},locale={},i18n={}) just initialized.", bridge, localeProvider, + i18nProvider); + + if (bridgeHandler == null) { + logger.trace("VeluxDiscoveryService(): registering bridge {} for lateron use for Discovery.", bridge); + } else if (!bridge.equals(bridgeHandler)) { + logger.trace("VeluxDiscoveryService(): replacing already registered bridge {} by {}.", bridgeHandler, + bridge); } + bridgeHandler = bridge; + localization = localizationHandler; + } + + /** + * Constructor + *

    + * Initializes the {@link VeluxDiscoveryService} with a reference to the well-prepared environment with a + * {@link VeluxBridgeHandler}. + * + * @param bridge Initialized Velux bridge handler. + * @param locationProvider Provider for a location. + * @param localeProvider Provider for a locale. + * @param i18nProvider Provider for the internationalization. + */ + public VeluxDiscoveryService(VeluxBridgeHandler bridge, LocationProvider locationProvider, + LocaleProvider localeProvider, TranslationProvider i18nProvider) { + this(bridge, new Localization(localeProvider, i18nProvider)); + logger.trace("VeluxDiscoveryService(bridge={},locale={},i18n={}) finished.", bridge, localeProvider, + i18nProvider); } @Override @@ -96,14 +122,10 @@ protected synchronized void startScan() { logger.debug("startScan(): creating a thing of type binding."); ThingUID thingUID = new ThingUID(THING_TYPE_BINDING, "org_openhab_binding_velux"); - // @formatter:off - DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID) - .withProperty(VeluxBindingProperties.PROPERTY_BINDING_BUNDLEVERSION, ManifestInformation.getBundleVersion()) - // TODO: should work with: - // .withLabel("@text/binding.velux.name") - .withLabel("Velux Binding") - .build(); - // @formatter:on + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID) + .withProperty(VeluxBindingProperties.PROPERTY_BINDING_BUNDLEVERSION, + ManifestInformation.getBundleVersion()) + .withLabel(localization.getText("discovery.velux.binding...label")).build(); logger.debug("startScan(): registering new thing {}.", discoveryResult); thingDiscovered(discoveryResult); @@ -138,7 +160,7 @@ private void discoverScenes() { // Just for avoidance of Potential null pointer access VeluxBridgeHandler bridgeHandlerX = bridgeHandler; if (bridgeHandlerX == null) { - logger.debug("discoverScenes(): VeluxDiscoveryService.bridgeHandlerR not initialized, aborting discovery."); + logger.debug("discoverScenes(): VeluxDiscoveryService.bridgeHandler not initialized, aborting discovery."); return; } ThingUID bridgeUID = bridgeHandlerX.getThing().getUID(); @@ -150,19 +172,12 @@ private void discoverScenes() { String label = sceneName.replaceAll("\\P{Alnum}", "_"); logger.trace("discoverScenes(): using label {}.", label); - ThingUID thingUID = new ThingUID(THING_TYPE_VELUX_SCENE, bridgeUID, label); - @SuppressWarnings("deprecation") - ThingTypeUID thingTypeUID = thingUID.getThingTypeUID(); - - // @formatter:off - DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID) - .withThingType(thingTypeUID) + ThingTypeUID thingTypeUID = THING_TYPE_VELUX_SCENE; + ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, label); + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID) .withProperty(VeluxBindingProperties.PROPERTY_SCENE_NAME, sceneName) - .withRepresentationProperty(VeluxBindingProperties.PROPERTY_SCENE_NAME) - .withBridge(bridgeUID) - .withLabel(label) - .build(); - // @formatter:on + .withRepresentationProperty(VeluxBindingProperties.PROPERTY_SCENE_NAME).withBridge(bridgeUID) + .withLabel(label).build(); logger.debug("discoverScenes(): registering new thing {}.", discoveryResult); thingDiscovered(discoveryResult); } @@ -194,39 +209,33 @@ private void discoverProducts() { } String label = actuatorName.replaceAll("\\P{Alnum}", "_"); logger.trace("discoverProducts(): using label {}.", label); - ThingUID thingUID = null; + ThingTypeUID thingTypeUID; boolean isInverted = false; logger.trace("discoverProducts() dealing with {} (type {}).", product, product.getProductType()); switch (product.getProductType()) { case SLIDER_WINDOW: logger.trace("discoverProducts(): creating window."); - thingUID = new ThingUID(THING_TYPE_VELUX_WINDOW, bridgeUID, label); + thingTypeUID = THING_TYPE_VELUX_WINDOW; isInverted = true; break; case SLIDER_SHUTTER: logger.trace("discoverProducts(): creating rollershutter."); - thingUID = new ThingUID(THING_TYPE_VELUX_ROLLERSHUTTER, bridgeUID, label); + thingTypeUID = THING_TYPE_VELUX_ROLLERSHUTTER; break; default: logger.trace("discoverProducts(): creating actuator."); - thingUID = new ThingUID(THING_TYPE_VELUX_ACTUATOR, bridgeUID, label); + thingTypeUID = THING_TYPE_VELUX_ACTUATOR; } - @SuppressWarnings("deprecation") - ThingTypeUID thingTypeUID = thingUID.getThingTypeUID(); + ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, label); - // @formatter:off - DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID) - .withThingType(thingTypeUID) + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID) .withProperty(VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER, identifier) .withProperty(VeluxBindingProperties.PROPERTY_ACTUATOR_NAME, actuatorName) .withProperty(VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED, isInverted) .withRepresentationProperty(VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER) - .withBridge(bridgeUID) - .withLabel(actuatorName) - .build(); - // @formatter:on + .withBridge(bridgeUID).withLabel(actuatorName).build(); logger.debug("discoverProducts(): registering new thing {}.", discoveryResult); thingDiscovered(discoveryResult); } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/discovery/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/package-info.java similarity index 90% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/discovery/package-info.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/package-info.java index 71912c430c98e..e72cf80c88a5f 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/discovery/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/package-info.java @@ -15,4 +15,4 @@ * * @author Guenther Schreiner - Initial contribution */ -package org.openhab.binding.velux.discovery; +package org.openhab.binding.velux.internal.discovery; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/BridgeChannels.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/BridgeChannels.java similarity index 63% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/BridgeChannels.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/BridgeChannels.java index c90cafe912009..f63adcde8aee0 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/BridgeChannels.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/BridgeChannels.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.handler; +package org.openhab.binding.velux.internal.handler; import java.util.HashSet; import java.util.Set; @@ -19,7 +19,6 @@ 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.binding.BaseBridgeHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,6 +27,7 @@ * properties. *

      *
    • {@link #getAllChannelUIDs}
    • + *
    • {@link #getAllLinkedChannelUIDs}
    • *
    *

    * Noninstantiable utility class @@ -62,8 +62,7 @@ private BridgeChannels() { * @param bridge which will be scrutinized for things. * @return channelUIDs of type {@link Set} of {@link ChannelUID}s. */ - static Set getAllChannelUIDs(BaseBridgeHandler bridge) { - LOGGER.trace("getAllChannels() called."); + static Set getAllChannelUIDs(ExtendedBaseBridgeHandler bridge) { Set channelUIDs = new HashSet(); Set things = new HashSet(bridge.getThing().getThings()); things.add(bridge.getThing()); @@ -72,7 +71,30 @@ static Set getAllChannelUIDs(BaseBridgeHandler bridge) { channelUIDs.add(channel.getUID()); } } - LOGGER.trace("getAllChannels() returns {}.", channelUIDs); + LOGGER.trace("getAllChannelUIDs() returns {}.", channelUIDs); + return channelUIDs; + } + + /** + * Return the Channel identifiers of all child things and the bridge things, + * which are linked. + *

    + * + * @param bridge which will be scrutinized for things. + * @return channelUIDs of type {@link Set} of {@link ChannelUID}s. + */ + static Set getAllLinkedChannelUIDs(ExtendedBaseBridgeHandler bridge) { + Set channelUIDs = new HashSet(); + Set things = new HashSet(bridge.getThing().getThings()); + things.add(bridge.getThing()); + for (Thing thing : things) { + for (Channel channel : thing.getChannels()) { + if (bridge.isLinked(channel.getUID())) { + channelUIDs.add(channel.getUID()); + } + } + } + LOGGER.trace("getAllLinkedChannelUIDs() returns {}.", channelUIDs); return channelUIDs; } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorLimitation.java new file mode 100644 index 0000000000000..2b8f6b4ae20c5 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorLimitation.java @@ -0,0 +1,212 @@ +/** + * Copyright (c) 2010-2019 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.velux.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.internal.VeluxBindingProperties; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeGetLimitation; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeSetLimitation; +import org.openhab.binding.velux.internal.things.VeluxProduct; +import org.openhab.binding.velux.internal.things.VeluxProductPosition; +import org.openhab.binding.velux.internal.things.VeluxProductSerialNo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

    + * This class implements the Channel position of the Thing actuator: + *

      + *
    • Velux bridgeOpenHAB: + *

      + * Information retrieval by method {@link #handleRefresh}.

    • + *
    + *
      + *
    • OpenHAB Event Bus → Velux bridge + *

      + * Sending commands and value updates by method {@link #handleCommand}.

    • + *
    + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelActuatorLimitation extends ChannelHandlerTemplate { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelActuatorLimitation.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelActuatorLimitation() { + throw new AssertionError(); + } + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler); + State newState = null; + do { // just for common exit + boolean setMinimum = channelId.length() == 0; + if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { + LOGGER.trace("handleRefresh(): there are some existing products."); + } + if (!ThingProperty.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER)) { + LOGGER.trace("handleRefresh(): aborting processing as {} is not set.", + VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER); + break; + } + String actuatorSerial = (String) ThingProperty.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER); + LOGGER.trace("handleRefresh(): actuatorSerial={}", actuatorSerial); + + // Handle value inversion + boolean propertyInverted = false; + if (ThingProperty.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) { + propertyInverted = (boolean) ThingProperty.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED); + } + boolean isInverted = propertyInverted || VeluxProductSerialNo.indicatesRevertedValues(actuatorSerial); + LOGGER.trace("handleRefresh(): isInverted={}.", isInverted); + actuatorSerial = VeluxProductSerialNo.cleaned(actuatorSerial); + + if (!thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts + .isRegistered(actuatorSerial)) { + LOGGER.info("handleRefresh(): cannot work on unknown actuator with serial {}.", actuatorSerial); + break; + } + LOGGER.trace("handleRefresh(): fetching actuator for {}.", actuatorSerial); + VeluxProduct thisProduct = thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts + .get(actuatorSerial); + LOGGER.trace("handleRefresh(): found actuator {}.", thisProduct); + + VeluxBridgeGetLimitation getLimitation = new VeluxBridgeGetLimitation(); + boolean success; + if (setMinimum) { + success = getLimitation.getMinimumLimitation(thisBridgeHandler.thisBridge, + thisProduct.getBridgeProductIndex().toInt()); + } else { + success = getLimitation.getMaximumLimitation(thisBridgeHandler.thisBridge, + thisProduct.getBridgeProductIndex().toInt()); + } + if (!success) { + LOGGER.info("handleRefresh(): retrieval failed."); + break; + } + VeluxProductPosition position = getLimitation.getLimitation(); + if (position.isValid()) { + PercentType positionAsPercent = position.getPositionAsPercentType(isInverted); + LOGGER.trace("handleRefresh(): found limitation of actuator at level {}.", positionAsPercent); + newState = positionAsPercent; + } else { + LOGGER.trace("handleRefresh(): limitation level of actuator is unknown."); + } + + } while (false); // common exit + LOGGER.trace("handleRefresh() returns {}.", newState); + return newState; + } + + /** + * Communication method to update the real world according to the passed channel value (or command). + * + * @param channelUID The item passed as type {@link ChannelUID} for which to following command is addressed to. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param command The command passed as type {@link Command} for the mentioned item. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newValue ... + */ + static @Nullable Command handleCommand(ChannelUID channelUID, String channelId, Command command, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleCommand({},{},{},{}) called.", channelUID, channelId, command, thisBridgeHandler); + Command newValue = null; + do { // just for common exit + boolean setMinimum = channelId.length() == 0; + if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { + LOGGER.trace("handleCommand(): there are some existing products."); + } + if (!ThingProperty.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER)) { + LOGGER.trace("handleCommand(): aborting processing as {} is not set.", + VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER); + break; + } + String actuatorSerial = (String) ThingProperty.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER); + LOGGER.trace("handleCommand(): actuatorSerial={}", actuatorSerial); + + // Handle value inversion + boolean propertyInverted = false; + if (ThingProperty.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) { + propertyInverted = (boolean) ThingProperty.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED); + } + boolean isInverted = propertyInverted || VeluxProductSerialNo.indicatesRevertedValues(actuatorSerial); + LOGGER.trace("handleCommand(): isInverted={}.", isInverted); + actuatorSerial = VeluxProductSerialNo.cleaned(actuatorSerial); + + if (!thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts + .isRegistered(actuatorSerial)) { + LOGGER.info("handleCommand(): cannot work on unknown actuator with serial {}.", actuatorSerial); + break; + } + LOGGER.trace("handleCommand(): fetching actuator for {}.", actuatorSerial); + VeluxProduct thisProduct = thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts + .get(actuatorSerial); + LOGGER.trace("handleCommand(): found actuator {}.", thisProduct); + + if (!(command instanceof PercentType)) { + LOGGER.trace("handleCommand(): aborting processing as command is not of type PercentType."); + } + + LOGGER.trace("handleCommand(): found command of type PercentType."); + PercentType ptCommand = (PercentType) command; + if (isInverted) { + ptCommand = new PercentType(PercentType.HUNDRED.intValue() - ptCommand.intValue()); + } + LOGGER.trace("handleCommand(): found command to set level to {}.", ptCommand); + + if (setMinimum) { + new VeluxBridgeSetLimitation().setMinimumLimitation(thisBridgeHandler.thisBridge, + thisProduct.getBridgeProductIndex().toInt(), ptCommand.intValue()); + } else { + new VeluxBridgeSetLimitation().setMaximumLimitation(thisBridgeHandler.thisBridge, + thisProduct.getBridgeProductIndex().toInt(), ptCommand.intValue()); + } + } while (false); // common exit + LOGGER.trace("handleCommand() returns {}.", newValue); + + return newValue; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelActuatorPosition.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorPosition.java similarity index 92% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelActuatorPosition.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorPosition.java index 510183f61091a..0a85953a9a7a7 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelActuatorPosition.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorPosition.java @@ -10,9 +10,9 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.handler; +package org.openhab.binding.velux.internal.handler; -import static org.openhab.binding.velux.VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION; +import static org.openhab.binding.velux.internal.VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -23,12 +23,12 @@ import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.State; -import org.openhab.binding.velux.VeluxBindingProperties; -import org.openhab.binding.velux.bridge.VeluxBridgeRunProductCommand; -import org.openhab.binding.velux.bridge.common.GetProduct; -import org.openhab.binding.velux.things.VeluxProduct; -import org.openhab.binding.velux.things.VeluxProductPosition; -import org.openhab.binding.velux.things.VeluxProductSerialNo; +import org.openhab.binding.velux.internal.VeluxBindingProperties; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeRunProductCommand; +import org.openhab.binding.velux.internal.bridge.common.GetProduct; +import org.openhab.binding.velux.internal.things.VeluxProduct; +import org.openhab.binding.velux.internal.things.VeluxProductPosition; +import org.openhab.binding.velux.internal.things.VeluxProductSerialNo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,7 +50,7 @@ * @author Guenther Schreiner - Initial contribution. */ @NonNullByDefault -final class ChannelActuatorPosition extends VeluxChannelHandler { +final class ChannelActuatorPosition extends ChannelHandlerTemplate { private static final Logger LOGGER = LoggerFactory.getLogger(ChannelActuatorPosition.class); /* @@ -192,15 +192,15 @@ private ChannelActuatorPosition() { VeluxProductPosition targetLevel = VeluxProductPosition.UNKNOWN; if (channelId.equals(CHANNEL_ACTUATOR_POSITION)) { - if ((command instanceof UpDownType) && ((UpDownType) command == UpDownType.UP)) { + if ((command instanceof UpDownType) && (command == UpDownType.UP)) { LOGGER.trace("handleCommand(): found UP command."); targetLevel = isInverted ? new VeluxProductPosition(PercentType.HUNDRED) : new VeluxProductPosition(PercentType.ZERO); - } else if ((command instanceof UpDownType) && ((UpDownType) command == UpDownType.DOWN)) { + } else if ((command instanceof UpDownType) && (command == UpDownType.DOWN)) { LOGGER.trace("handleCommand(): found DOWN command."); targetLevel = isInverted ? new VeluxProductPosition(PercentType.ZERO) : new VeluxProductPosition(PercentType.HUNDRED); - } else if ((command instanceof StopMoveType) && ((StopMoveType) command == StopMoveType.STOP)) { + } else if ((command instanceof StopMoveType) && (command == StopMoveType.STOP)) { LOGGER.trace("handleCommand(): found STOP command."); targetLevel = new VeluxProductPosition(); } else if (command instanceof PercentType) { @@ -216,11 +216,11 @@ private ChannelActuatorPosition() { break; } } else { - if ((command instanceof OnOffType) && ((OnOffType) command == OnOffType.ON)) { + if ((command instanceof OnOffType) && (command == OnOffType.ON)) { LOGGER.trace("handleCommand(): found ON command."); targetLevel = isInverted ? new VeluxProductPosition(PercentType.HUNDRED) : new VeluxProductPosition(PercentType.ZERO); - } else if ((command instanceof OnOffType) && ((OnOffType) command == OnOffType.OFF)) { + } else if ((command instanceof OnOffType) && (command == OnOffType.OFF)) { LOGGER.trace("handleCommand(): found OFF command."); targetLevel = isInverted ? new VeluxProductPosition(PercentType.ZERO) : new VeluxProductPosition(PercentType.HUNDRED); diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeCheck.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeCheck.java similarity index 90% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeCheck.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeCheck.java index 87b48b70ea713..10442c522f2a7 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeCheck.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeCheck.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.handler; +package org.openhab.binding.velux.internal.handler; import java.util.ArrayList; @@ -18,9 +18,9 @@ import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.State; -import org.openhab.binding.velux.VeluxBindingProperties; +import org.openhab.binding.velux.internal.VeluxBindingProperties; import org.openhab.binding.velux.internal.VeluxItemType; -import org.openhab.binding.velux.things.VeluxScene; +import org.openhab.binding.velux.internal.things.VeluxScene; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,7 +37,7 @@ * @author Guenther Schreiner - Initial contribution. */ @NonNullByDefault -final class ChannelBridgeCheck extends VeluxChannelHandler { +final class ChannelBridgeCheck extends ChannelHandlerTemplate { private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeCheck.class); /* @@ -99,9 +99,10 @@ private ChannelBridgeCheck() { } String result; if (unusedScenes.size() > 0) { - result = "@text/channelValue.check-integrity-failed" + " " + unusedScenes.toString() + "."; + result = thisBridgeHandler.localization.getText("channelValue.check-integrity-failed", + unusedScenes.toString()); } else { - result = "@text/channelValue.check-integrity-ok"; + result = thisBridgeHandler.localization.getText("channelValue.check-integrity-ok"); } LOGGER.info("{}", result); newState = StateUtils.createState(result); diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeDoDetection.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeDoDetection.java similarity index 92% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeDoDetection.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeDoDetection.java index 39f098dca452a..53934ffc256f1 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeDoDetection.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeDoDetection.java @@ -10,14 +10,14 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.handler; +package org.openhab.binding.velux.internal.handler; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.Command; -import org.openhab.binding.velux.bridge.VeluxBridgeDetectProducts; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeDetectProducts; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,7 +39,7 @@ * @author Guenther Schreiner - Initial contribution. */ @NonNullByDefault -final class ChannelBridgeDoDetection extends VeluxChannelHandler { +final class ChannelBridgeDoDetection extends ChannelHandlerTemplate { private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeDoDetection.class); /* @@ -66,7 +66,7 @@ private ChannelBridgeDoDetection() { static @Nullable Command handleCommand(ChannelUID channelUID, String channelId, Command command, VeluxBridgeHandler thisBridgeHandler) { LOGGER.debug("handleCommand({},{},{},{}) called.", channelUID, channelId, command, thisBridgeHandler); - if (command.equals(OnOffType.ON)) { + if (command == OnOffType.ON) { LOGGER.trace("handleCommand(): about to activate veluxBridge detection mode."); new VeluxBridgeDetectProducts().detectProducts(thisBridgeHandler.thisBridge); } else { diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeFirmware.java similarity index 93% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeFirmware.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeFirmware.java index 43aa28a4dcef4..8b5625fa9ecf0 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeFirmware.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeFirmware.java @@ -10,13 +10,13 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.handler; +package org.openhab.binding.velux.internal.handler; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.State; -import org.openhab.binding.velux.bridge.VeluxBridgeGetFirmware; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeGetFirmware; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,7 +33,7 @@ * @author Guenther Schreiner - Initial contribution. */ @NonNullByDefault -final class ChannelBridgeFirmware extends VeluxChannelHandler { +final class ChannelBridgeFirmware extends ChannelHandlerTemplate { private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeFirmware.class); /* diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeLANconfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeLANconfig.java similarity index 94% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeLANconfig.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeLANconfig.java index 71f1c8f6be502..45db44d3b33d8 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeLANconfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeLANconfig.java @@ -10,15 +10,15 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.handler; +package org.openhab.binding.velux.internal.handler; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.State; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.bridge.VeluxBridgeLANConfig; +import org.openhab.binding.velux.internal.VeluxBindingConstants; import org.openhab.binding.velux.internal.VeluxItemType; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeLANConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,7 +36,7 @@ * @author Guenther Schreiner - Initial contribution. */ @NonNullByDefault -final class ChannelBridgeLANconfig extends VeluxChannelHandler { +final class ChannelBridgeLANconfig extends ChannelHandlerTemplate { private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeLANconfig.class); /* diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeProducts.java similarity index 95% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeProducts.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeProducts.java index c28f0087a230b..cad458c273eef 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeProducts.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeProducts.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.handler; +package org.openhab.binding.velux.internal.handler; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -32,7 +32,7 @@ * @author Guenther Schreiner - Initial contribution. */ @NonNullByDefault -final class ChannelBridgeProducts extends VeluxChannelHandler { +final class ChannelBridgeProducts extends ChannelHandlerTemplate { private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeProducts.class); /* diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeScenes.java similarity index 95% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeScenes.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeScenes.java index 4903f554a5e63..093b9053e051d 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeScenes.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeScenes.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.handler; +package org.openhab.binding.velux.internal.handler; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -32,7 +32,7 @@ * @author Guenther Schreiner - Initial contribution. */ @NonNullByDefault -final class ChannelBridgeScenes extends VeluxChannelHandler { +final class ChannelBridgeScenes extends ChannelHandlerTemplate { private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeScenes.class); /* diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeStatus.java similarity index 93% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeStatus.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeStatus.java index cac22d421c708..cc96d02f849c1 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeStatus.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeStatus.java @@ -10,13 +10,13 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.handler; +package org.openhab.binding.velux.internal.handler; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.State; -import org.openhab.binding.velux.bridge.VeluxBridgeDeviceStatus; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeDeviceStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,7 +33,7 @@ * @author Guenther Schreiner - Initial contribution. */ @NonNullByDefault -final class ChannelBridgeStatus extends VeluxChannelHandler { +final class ChannelBridgeStatus extends ChannelHandlerTemplate { private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeStatus.class); /* diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeWLANconfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeWLANconfig.java similarity index 93% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeWLANconfig.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeWLANconfig.java index 97d3986aca550..b37b4c9e5068f 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelBridgeWLANconfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeWLANconfig.java @@ -10,15 +10,15 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.handler; +package org.openhab.binding.velux.internal.handler; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.State; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.bridge.VeluxBridgeWLANConfig; +import org.openhab.binding.velux.internal.VeluxBindingConstants; import org.openhab.binding.velux.internal.VeluxItemType; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeWLANConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,7 +35,7 @@ * @author Guenther Schreiner - Initial contribution. */ @NonNullByDefault -final class ChannelBridgeWLANconfig extends VeluxChannelHandler { +final class ChannelBridgeWLANconfig extends ChannelHandlerTemplate { private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeWLANconfig.class); /* diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxChannelHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelHandlerTemplate.java similarity index 85% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxChannelHandler.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelHandlerTemplate.java index 567931703a3ef..a6d4a215850de 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxChannelHandler.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelHandlerTemplate.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.handler; +package org.openhab.binding.velux.internal.handler; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -36,7 +36,7 @@ * @author Guenther Schreiner - Initial contribution. */ @NonNullByDefault -abstract class VeluxChannelHandler { +abstract class ChannelHandlerTemplate { /** * Communication method to retrieve information to update the channel value. @@ -45,9 +45,10 @@ abstract class VeluxChannelHandler { * @param channelId The same item passed as type {@link String} for which a refresh is intended. * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides * information for this channel. - * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + * @return newValue The value retrieved for the passed channel, or null in case if there is no (new) + * value. */ - static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, // NO_UCD (unused code) + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, VeluxBridgeHandler thisBridgeHandler) { throw new IllegalStateException("handleRefresh hasn't been set up in the subclass"); } @@ -60,10 +61,10 @@ abstract class VeluxChannelHandler { * @param command The command passed as type {@link Command} for the mentioned item. * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides * information for this channel. - * @return newValue ... + * @return newValue value to be assigned to the channel by BaseThingHandler.postUpdate, or null, if + * not desired. */ - static @Nullable Command handleCommand(ChannelUID channelUID, String channelId, Command command, // NO_UCD (unused - // code) + static @Nullable Command handleCommand(ChannelUID channelUID, String channelId, Command command, VeluxBridgeHandler thisBridgeHandler) { throw new IllegalStateException("handleRefresh hasn't been set up in the subclass"); } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelSceneAction.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneAction.java similarity index 92% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelSceneAction.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneAction.java index fa34d27add326..11a5cbb12106e 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelSceneAction.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneAction.java @@ -10,18 +10,18 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.handler; +package org.openhab.binding.velux.internal.handler; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.Command; -import org.openhab.binding.velux.VeluxBindingProperties; -import org.openhab.binding.velux.bridge.VeluxBridgeRunScene; -import org.openhab.binding.velux.things.VeluxProductVelocity; -import org.openhab.binding.velux.things.VeluxScene; -import org.openhab.binding.velux.things.VeluxScene.SceneName; +import org.openhab.binding.velux.internal.VeluxBindingProperties; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeRunScene; +import org.openhab.binding.velux.internal.things.VeluxProductVelocity; +import org.openhab.binding.velux.internal.things.VeluxScene; +import org.openhab.binding.velux.internal.things.VeluxScene.SceneName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,7 +43,7 @@ * @author Guenther Schreiner - Initial contribution. */ @NonNullByDefault -final class ChannelSceneAction extends VeluxChannelHandler { +final class ChannelSceneAction extends ChannelHandlerTemplate { private static final Logger LOGGER = LoggerFactory.getLogger(ChannelSceneAction.class); /* diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelSceneSilentmode.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneSilentmode.java similarity index 91% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelSceneSilentmode.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneSilentmode.java index 5a8843167f9a9..f06b3e3d09211 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelSceneSilentmode.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneSilentmode.java @@ -10,17 +10,17 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.handler; +package org.openhab.binding.velux.internal.handler; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.Command; -import org.openhab.binding.velux.VeluxBindingProperties; -import org.openhab.binding.velux.bridge.VeluxBridgeSetSceneVelocity; -import org.openhab.binding.velux.things.VeluxScene; -import org.openhab.binding.velux.things.VeluxScene.SceneName; +import org.openhab.binding.velux.internal.VeluxBindingProperties; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeSetSceneVelocity; +import org.openhab.binding.velux.internal.things.VeluxScene; +import org.openhab.binding.velux.internal.things.VeluxScene.SceneName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,7 +42,7 @@ * @author Guenther Schreiner - Initial contribution. */ @NonNullByDefault -final class ChannelSceneSilentmode extends VeluxChannelHandler { +final class ChannelSceneSilentmode extends ChannelHandlerTemplate { private static final Logger LOGGER = LoggerFactory.getLogger(ChannelSceneSilentmode.class); /* diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelVShutterPosition.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelVShutterPosition.java similarity index 93% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelVShutterPosition.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelVShutterPosition.java index f763a5b7f58f8..259a3bc4b97d4 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ChannelVShutterPosition.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelVShutterPosition.java @@ -10,9 +10,9 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.handler; +package org.openhab.binding.velux.internal.handler; -import static org.openhab.binding.velux.VeluxBindingConstants.CHANNEL_VSHUTTER_POSITION; +import static org.openhab.binding.velux.internal.VeluxBindingConstants.CHANNEL_VSHUTTER_POSITION; import java.math.BigDecimal; @@ -23,12 +23,12 @@ import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.State; -import org.openhab.binding.velux.VeluxBindingProperties; -import org.openhab.binding.velux.bridge.VeluxBridgeRunScene; +import org.openhab.binding.velux.internal.VeluxBindingProperties; import org.openhab.binding.velux.internal.VeluxItemType; import org.openhab.binding.velux.internal.VeluxRSBindingConfig; -import org.openhab.binding.velux.things.VeluxScene; -import org.openhab.binding.velux.things.VeluxScene.SceneName; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeRunScene; +import org.openhab.binding.velux.internal.things.VeluxScene; +import org.openhab.binding.velux.internal.things.VeluxScene.SceneName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,7 +50,7 @@ * @author Guenther Schreiner - Initial contribution. */ @NonNullByDefault -final class ChannelVShutterPosition extends VeluxChannelHandler { +final class ChannelVShutterPosition extends ChannelHandlerTemplate { private static final Logger LOGGER = LoggerFactory.getLogger(ChannelVShutterPosition.class); /* diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ExtendedBaseBridgeHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ExtendedBaseBridgeHandler.java similarity index 80% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ExtendedBaseBridgeHandler.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ExtendedBaseBridgeHandler.java index 4a4ff031ccb11..ea057ebabc224 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ExtendedBaseBridgeHandler.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ExtendedBaseBridgeHandler.java @@ -10,12 +10,13 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.handler; +package org.openhab.binding.velux.internal.handler; import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; /** @@ -62,4 +63,14 @@ public void updateProperties(Map properties) { super.updateProperties(properties); } + /** + * Returns whether at least one item is linked for the given UID of the channel. + * + * @param channelUID UID of the channel (must not be null) + * @return true if at least one item is linked, false otherwise + */ + @Override + public boolean isLinked(ChannelUID channelUID) { + return super.isLinked(channelUID); + } } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ExtendedBaseThingHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ExtendedBaseThingHandler.java similarity index 97% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ExtendedBaseThingHandler.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ExtendedBaseThingHandler.java index 8d79f70c06f07..bbc921e8c70e3 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ExtendedBaseThingHandler.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ExtendedBaseThingHandler.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.handler; +package org.openhab.binding.velux.internal.handler; import java.util.Map; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/StateUtils.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/StateUtils.java similarity index 98% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/StateUtils.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/StateUtils.java index fa7192d158cfe..ccc8d9399ddd2 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/StateUtils.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/StateUtils.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.handler; +package org.openhab.binding.velux.internal.handler; import java.math.BigDecimal; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ThingProperty.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ThingProperty.java similarity index 80% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ThingProperty.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ThingProperty.java index e6bcaf450cda6..abf17a30aa329 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/ThingProperty.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ThingProperty.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.handler; +package org.openhab.binding.velux.internal.handler; import java.util.Map; @@ -37,7 +37,7 @@ * @author Guenther Schreiner - Initial contribution */ @NonNullByDefault -final class ThingProperty { +public class ThingProperty { private static final Logger LOGGER = LoggerFactory.getLogger(ThingProperty.class); /* @@ -67,19 +67,17 @@ private ThingProperty() { * @return exists of type boolean. */ static boolean exists(BaseBridgeHandler bridge, ChannelUID channelUID, String propertyName) { - LOGGER.trace("exists({},{}) called.", channelUID, propertyName); - ThingUID channelTUID = channelUID.getThingUID(); Thing thingOfChannel = bridge.getThingByUID(channelTUID); boolean exists = false; if (thingOfChannel == null) { - LOGGER.trace("exists(): Channel {} does not belong to a thing.", channelUID); + LOGGER.warn("exists(): Channel {} does not belong to a thing.", channelUID); } else { if (thingOfChannel.getConfiguration().get(propertyName) != null) { exists = true; } } - LOGGER.trace("exists() returns {}.", exists); + LOGGER.trace("exists({},{}) returns {}.", channelUID, propertyName, exists); return exists; } @@ -95,8 +93,6 @@ static boolean exists(BaseBridgeHandler bridge, ChannelUID channelUID, String pr * is {@code null}. */ static Object getValue(BaseBridgeHandler bridge, ChannelUID channelUID, String propertyName) { - LOGGER.trace("getValue({},{}) called.", channelUID, propertyName); - ThingUID channelTUID = channelUID.getThingUID(); Thing thingOfChannel = bridge.getThingByUID(channelTUID); if (thingOfChannel == null) { @@ -104,10 +100,25 @@ static Object getValue(BaseBridgeHandler bridge, ChannelUID channelUID, String p return true; } Object propertyValue = thingOfChannel.getConfiguration().get(propertyName); - LOGGER.trace("getValue() returns {}.", propertyValue); + LOGGER.trace("getValue({},{}) returns {}.", channelUID, propertyName, propertyValue); return propertyValue; } + /** + * Modifies the property value for the givenpropertyName which are defined within + * VeluxBindingProperties. + *

    + * + * @param thing with property will be modified, + * @param propertyName defines the property which is to be modified. + * @param propertyValue defines the new property value. + */ + public static void setValue(Thing thing, String propertyName, Object propertyValue) { + thing.setProperty(propertyName, propertyValue.toString()); + LOGGER.trace("setValue() {} set to {}.", propertyName, propertyValue); + return; + } + /** * Modifies the property value for the given bridge and desired propertyName which are defined within * VeluxBindingProperties. @@ -120,16 +131,14 @@ static Object getValue(BaseBridgeHandler bridge, ChannelUID channelUID, String p */ static void setValue(ExtendedBaseBridgeHandler bridgeHandler, ChannelUID channelUID, String propertyName, Object propertyValue) { - LOGGER.trace("setValue({},{}) called.", propertyName, propertyValue); - ThingUID channelTUID = channelUID.getThingUID(); Thing thingOfChannel = bridgeHandler.getThingByUID(channelTUID); if (thingOfChannel == null) { - LOGGER.warn("getValue(): Channel {} does not belong to a thing.", channelUID); + LOGGER.warn("setValue(): Channel {} does not belong to a thing.", channelUID); return; } - thingOfChannel.setProperty(propertyName, propertyName); - LOGGER.trace("setValue() done."); + thingOfChannel.setProperty(propertyName, propertyValue.toString()); + LOGGER.trace("setValue() {} set to {}.", propertyName, propertyValue); return; } @@ -143,12 +152,10 @@ static void setValue(ExtendedBaseBridgeHandler bridgeHandler, ChannelUID channel * @param propertyValue defines the new property value. */ static void setValue(ExtendedBaseBridgeHandler bridgeHandler, String propertyName, Object propertyValue) { - LOGGER.trace("setValue({},{}) called.", propertyName, propertyValue); - Map properties = bridgeHandler.editProperties(); properties.put(propertyName, propertyValue.toString()); bridgeHandler.updateProperties(properties); - LOGGER.trace("setValue() done."); + LOGGER.trace("setValue() {} set to {}.", propertyName, propertyValue); return; } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxBindingHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBindingHandler.java similarity index 55% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxBindingHandler.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBindingHandler.java index 1cf51b38b9d84..34a2741f22b94 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxBindingHandler.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBindingHandler.java @@ -10,9 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.handler; +package org.openhab.binding.velux.internal.handler; import org.eclipse.jdt.annotation.NonNullByDefault; +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; @@ -22,9 +23,10 @@ import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.RefreshType; import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.internal.VeluxBindingProperties; import org.openhab.binding.velux.internal.VeluxItemType; import org.openhab.binding.velux.internal.utils.Localization; -import org.openhab.binding.velux.internal.utils.LoggerFulltrace; +import org.openhab.binding.velux.internal.utils.ManifestInformation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,30 +35,35 @@ *

    * Beside the normal thing handling introduced by {@link BaseThingHandler}, it provides a method: *

      - *
    • {@link #updateNoOfBridges} enable other classes to modify the number of activated Velux bridges.
    • + *
    • {@link #updateBindingState} to enable other classes to modify the number of activated Velux bridges and + * Things.
    • *
    * * @author Guenther Schreiner - Initial contribution */ @NonNullByDefault -public class VeluxBindingHandler extends BaseThingHandler { +public class VeluxBindingHandler extends ExtendedBaseThingHandler { private final Logger logger = LoggerFactory.getLogger(VeluxBindingHandler.class); - private final LoggerFulltrace log = new LoggerFulltrace(logger, false); /* * *************************** * ***** Private Objects ***** */ + private Thing thing; + private Localization localization; private Integer currentNumberOfBridges = 0; + private Integer currentNumberOfThings = 0; /* * ************************ * ***** Constructors ***** */ - public VeluxBindingHandler(Thing thing) { + public VeluxBindingHandler(Thing thing, final Localization localization) { super(thing); - logger.trace("VeluxHandler(constructor) called."); + this.thing = thing; + this.localization = localization; + logger.trace("VeluxBindingHandler(constructor) called."); } /* @@ -75,7 +82,6 @@ public VeluxBindingHandler(Thing thing) { */ @SuppressWarnings("deprecation") private ThingTypeUID thingTypeUIDOf(ChannelUID channelUID) { - log.fulltrace("thingTypeUIDOf({}) called.", channelUID); return channelUID.getThingUID().getThingTypeUID(); } @@ -86,18 +92,52 @@ private ThingTypeUID thingTypeUIDOf(ChannelUID channelUID) { * @return bindingInformation of type {@link String}. */ private String bridgeCountToString() { - String information = Localization.getText("@text/runtime.multiple-bridges"); + String information; switch (currentNumberOfBridges) { case 0: - information = Localization.getText("@text/runtime.no-bridge"); + information = localization.getText("runtime.no-bridge"); break; case 1: - information = Localization.getText("@text/runtime.one-bridge"); + information = localization.getText("runtime.one-bridge"); + break; + default: + information = localization.getText("runtime.multiple-bridges"); break; } return information; } + /** + * Modifies all informations within openHAB to inform the user about the current state of this binding. That is: + *
      + *
    • Update the properties about bundle version, number of bridges and things,
    • + *
    • Usability of the binding in respect to defined bridges within the ThingStatus, and
    • + *
    • Information of the binding state as channel value.
    • + *
    + */ + private void updateVisibleInformation() { + logger.trace("updateVisibleInformation(): updating properties."); + ThingProperty.setValue(thing, VeluxBindingProperties.PROPERTY_BINDING_BUNDLEVERSION, + ManifestInformation.getBundleVersion()); + ThingProperty.setValue(thing, VeluxBindingProperties.PROPERTY_BINDING_NOOFBRIDGES, currentNumberOfBridges); + ThingProperty.setValue(thing, VeluxBindingProperties.PROPERTY_BINDING_NOOFTHINGS, currentNumberOfThings); + + // BaseThingHandler is sensitive during initialization phase. Therefore, to avoid (wrong) warnings about: + // "tried updating the thing status although the handler was already disposed." + if (this.isInitialized()) { + logger.trace("updateVisibleInformation(): updating thing status."); + if (currentNumberOfBridges < 1) { + updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING, bridgeCountToString()); + } else { + updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, bridgeCountToString()); + } + logger.trace("updateVisibleInformation(): updating all channels."); + for (Channel channel : thing.getChannels()) { + handleCommand(channel.getUID(), RefreshType.REFRESH); + } + } + } + /* * ******************************************************************* * ***** Objects and Methods for abstract class BaseThingHandler ***** @@ -105,27 +145,23 @@ private String bridgeCountToString() { @Override public void initialize() { - logger.trace("initialize() called."); - updateNoOfBridges(0); + logger.debug("initialize() called."); + logger.info("Initializing VeluxBindingHandler for '{}'.", getThing().getUID()); + // The framework requires you to return from this method quickly. + // Setting the thing status to UNKNOWN temporarily and let the background task decide for the real status. + updateStatus(ThingStatus.UNKNOWN); + + logger.trace("initialize(): preparing background initialization task."); + // Background initialization... + scheduler.execute(() -> { + logger.trace("initialize.scheduled(): Setting ThingStatus to ONLINE."); + updateStatus(ThingStatus.ONLINE); + updateVisibleInformation(); + logger.trace("initialize.scheduled(): done."); + }); logger.trace("initialize() done."); } - @Override - public void dispose() { - logger.trace("dispose() called."); - super.dispose(); - } - - /** - * NOTE: It takes care by calling {@link #handleCommand} with the REFRESH command, that every used channel is - * initialized. - */ - @Override - public void channelLinked(ChannelUID channelUID) { - logger.trace("channelLinked({}) called.", channelUID.getAsString()); - handleCommand(channelUID, RefreshType.REFRESH); - } - @Override public void handleCommand(ChannelUID channelUID, Command command) { logger.trace("handleCommand({},{}) called.", channelUID.getAsString(), command); @@ -164,8 +200,6 @@ public void handleCommand(ChannelUID channelUID, Command command) { * =========================================================== * Modification part */ - logger.trace("handleCommand(): found COMMAND {}.", command); - switch (channelId) { default: logger.warn("handleCommand() cannot handle command {} on channel {} (type {}).", command, itemName, @@ -181,21 +215,19 @@ public void handleCommand(ChannelUID channelUID, Command command) { */ /** - * Modifies the number of activated Velux bridges, which is reflected in the Thing representing the overall status - * of this binding. + * Update the information about bridges and things. + *

    + * Provided for instrumentation of factory class to update this set of information. + *

    * - * @param bridgeCount as Integer. + * @param veluxBridgeCount describing the number of initialized bridges. + * @param veluxThingCount describing the number of initialized things (in addition to Thing of type + * BindingInformation). */ - - public void updateNoOfBridges(Integer bridgeCount) { - logger.trace("updateNoOfBridges({}) called.", bridgeCount); - this.currentNumberOfBridges = bridgeCount; - if (bridgeCount < 1) { - updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING, bridgeCountToString()); - } else { - updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, bridgeCountToString()); - } - logger.trace("updateNoOfBridges() done."); + public void updateBindingState(Integer veluxBridgeCount, Integer veluxThingCount) { + currentNumberOfBridges = veluxBridgeCount; + currentNumberOfThings = veluxThingCount; + updateVisibleInformation(); } } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxBridgeHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBridgeHandler.java similarity index 65% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxBridgeHandler.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBridgeHandler.java index 48e65555c7afc..0f82c1de91000 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxBridgeHandler.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBridgeHandler.java @@ -10,14 +10,15 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.handler; +package org.openhab.binding.velux.internal.handler; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import org.apache.commons.lang.exception.ExceptionUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.common.ThreadPoolManager; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.library.types.PercentType; @@ -31,32 +32,33 @@ import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.RefreshType; import org.eclipse.smarthome.core.types.State; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.VeluxBindingProperties; -import org.openhab.binding.velux.bridge.VeluxBridge; -import org.openhab.binding.velux.bridge.VeluxBridgeActuators; -import org.openhab.binding.velux.bridge.VeluxBridgeDeviceStatus; -import org.openhab.binding.velux.bridge.VeluxBridgeGetFirmware; -import org.openhab.binding.velux.bridge.VeluxBridgeGetHouseStatus; -import org.openhab.binding.velux.bridge.VeluxBridgeInstance; -import org.openhab.binding.velux.bridge.VeluxBridgeLANConfig; -import org.openhab.binding.velux.bridge.VeluxBridgeProvider; -import org.openhab.binding.velux.bridge.VeluxBridgeScenes; -import org.openhab.binding.velux.bridge.VeluxBridgeSetHouseStatusMonitor; -import org.openhab.binding.velux.bridge.VeluxBridgeWLANConfig; -import org.openhab.binding.velux.bridge.common.BridgeAPI; -import org.openhab.binding.velux.bridge.common.BridgeCommunicationProtocol; -import org.openhab.binding.velux.bridge.json.JsonVeluxBridge; -import org.openhab.binding.velux.bridge.slip.SlipVeluxBridge; import org.openhab.binding.velux.internal.VeluxBinding; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.VeluxBindingProperties; import org.openhab.binding.velux.internal.VeluxItemType; +import org.openhab.binding.velux.internal.bridge.VeluxBridge; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeActuators; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeDeviceStatus; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeGetFirmware; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeGetHouseStatus; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeInstance; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeLANConfig; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeScenes; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeSetHouseStatusMonitor; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeWLANConfig; +import org.openhab.binding.velux.internal.bridge.common.BridgeAPI; +import org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol; +import org.openhab.binding.velux.internal.bridge.json.JsonVeluxBridge; +import org.openhab.binding.velux.internal.bridge.slip.SlipVeluxBridge; import org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration; -import org.openhab.binding.velux.internal.utils.LoggerFulltrace; -import org.openhab.binding.velux.things.VeluxExistingProducts; -import org.openhab.binding.velux.things.VeluxExistingScenes; -import org.openhab.binding.velux.things.VeluxProduct; -import org.openhab.binding.velux.things.VeluxProductPosition; -import org.openhab.binding.velux.things.VeluxProductSerialNo; +import org.openhab.binding.velux.internal.development.Threads; +import org.openhab.binding.velux.internal.things.VeluxExistingProducts; +import org.openhab.binding.velux.internal.things.VeluxExistingScenes; +import org.openhab.binding.velux.internal.things.VeluxProduct; +import org.openhab.binding.velux.internal.things.VeluxProductPosition; +import org.openhab.binding.velux.internal.things.VeluxProductSerialNo; +import org.openhab.binding.velux.internal.utils.Localization; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -83,26 +85,39 @@ @NonNullByDefault public class VeluxBridgeHandler extends ExtendedBaseBridgeHandler implements VeluxBridgeInstance, VeluxBridgeProvider { private final Logger logger = LoggerFactory.getLogger(VeluxBridgeHandler.class); - private final LoggerFulltrace log = new LoggerFulltrace(logger, false); /* * *************************** * ***** Private Objects ***** */ + /** + * Scheduler for continuous refresh by scheduleWithFixedDelay. + */ private @Nullable ScheduledFuture refreshJob = null; + + /** + * Counter of refresh invocations by {@link refreshJob}. + */ private int refreshCounter = 0; - private VeluxBridge myJsonBridge; - private VeluxBridge mySlipBridge; + /** + * Dedicated thread pool for the long-running bridge communication threads. + */ + private final ScheduledExecutorService handleScheduler = ThreadPoolManager + .getScheduledPool(VeluxBindingConstants.BINDING_ID); + + private VeluxBridge myJsonBridge = new JsonVeluxBridge(this); + private VeluxBridge mySlipBridge = new SlipVeluxBridge(this); /* * ************************************** * ***** Default visibility Objects ***** */ - BridgeParameters bridgeParameters; - VeluxBridge thisBridge; + VeluxBridge thisBridge = myJsonBridge; + BridgeParameters bridgeParameters = new BridgeParameters(); + Localization localization; /** * Information retrieved by {@link VeluxBinding#VeluxBinding}. @@ -114,25 +129,11 @@ public class VeluxBridgeHandler extends ExtendedBaseBridgeHandler implements Vel * ***** Constructors ***** */ - public VeluxBridgeHandler(final Bridge bridge) { + public VeluxBridgeHandler(final Bridge bridge, Localization localization) { super(bridge); - logger.trace("VeluxBridgeHandler(constructor with bridge={}) called.", bridge); + logger.trace("VeluxBridgeHandler(constructor with bridge={}, localization={}) called.", bridge, localization); + this.localization = localization; logger.debug("Creating a VeluxBridgeHandler for thing '{}'.", getThing().getUID()); - bridgeParameters = new BridgeParameters(); - logger.trace("VeluxBridgeHandler(): Initializing empty storage for existing products."); - bridgeParameters.actuators = new VeluxBridgeActuators(); - logger.trace("VeluxBridgeHandler(): Initializing empty storage for existing scenes."); - bridgeParameters.scenes = new VeluxBridgeScenes(); - bridgeParameters.gateway = new VeluxBridgeDeviceStatus().getChannel(); - bridgeParameters.firmware = new VeluxBridgeGetFirmware().getChannel(); - bridgeParameters.lanConfig = new VeluxBridgeLANConfig().getChannel(); - bridgeParameters.wlanConfig = new VeluxBridgeWLANConfig().getChannel(); - // - logger.trace("VeluxBridgeHandler(): Initializing the different bridge protocols."); - myJsonBridge = new JsonVeluxBridge(this); - mySlipBridge = new SlipVeluxBridge(this); - thisBridge = myJsonBridge; - logger.trace("VeluxBridgeHandler(constructor) done."); } /* @@ -153,11 +154,12 @@ public VeluxBridgeHandler(final Bridge bridge) { *
  • {@link #wlanConfig} - Information about the gateway configuration.
  • * */ + @NonNullByDefault public class BridgeParameters { /** Information retrieved by {@link VeluxBridgeActuators#getProducts} */ VeluxBridgeActuators actuators = new VeluxBridgeActuators(); - /** Information retrieved by {@link org.openhab.binding.velux.bridge.VeluxBridgeScenes#getScenes} */ + /** Information retrieved by {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeScenes#getScenes} */ VeluxBridgeScenes scenes = new VeluxBridgeScenes(); /** Information retrieved by {@link VeluxBridgeDeviceStatus#retrieve} */ @@ -189,7 +191,6 @@ public class BridgeParameters { */ @SuppressWarnings("deprecation") ThingTypeUID thingTypeUIDOf(ChannelUID channelUID) { - log.fulltrace("thingTypeUIDOf({}) called.", channelUID); return channelUID.getThingUID().getThingTypeUID(); } @@ -229,13 +230,13 @@ public VeluxExistingScenes existingScenes() { @Override public boolean bridgeCommunicate(BridgeCommunicationProtocol communication) { - logger.warn("bridgeCommunicate() called. Auto-generated method stub. Should never be called."); + logger.warn("bridgeCommunicate() called. Should never be called (as implemented by protocol-specific layers)."); return false; } @Override public @Nullable BridgeAPI bridgeAPI() { - logger.warn("bridgeAPI() called. . Auto-generated method stub. Should never be called."); + logger.warn("bridgeAPI() called. Should never be called (as implemented by protocol-specific layers)."); return null; } @@ -244,21 +245,29 @@ public boolean bridgeCommunicate(BridgeCommunicationProtocol communication) { ***/ private synchronized void execute() { - logger.debug("execute() called."); + logger.debug("execute() initiated by {} starting cycle {}.", Thread.currentThread(), refreshCounter); + logger.trace("execute(): processing of possible HSM messages."); - if (new VeluxBridgeGetHouseStatus().evaluateState(thisBridge)) { - logger.trace("execute(): successfully processed of GetHouseStatus()"); - } - logger.trace("execute(): looping through all (both child things and bridge) channels for a need of refresh."); - for (ChannelUID channelUID : BridgeChannels.getAllChannelUIDs(this)) { - log.fulltrace("execute(): evaluating ChannelUID {}.", channelUID); - if (VeluxItemType.isToBeRefreshedNow(this.refreshCounter, thingTypeUIDOf(channelUID), channelUID.getId())) { + // Background execution of bridge related I/O + handleScheduler.execute(() -> { + logger.trace("handleCommand.scheduler.execute() initiated by {} will process HouseStatus.", + Thread.currentThread()); + if (new VeluxBridgeGetHouseStatus().evaluateState(thisBridge)) { + logger.trace("execute(): successfully processed of GetHouseStatus()"); + } + logger.trace("handleCommand.scheduler.execute() initiated by {} has finished.", Thread.currentThread()); + }); + + logger.trace( + "execute(): looping through all (both child things and bridge) linked channels for a need of refresh."); + for (ChannelUID channelUID : BridgeChannels.getAllLinkedChannelUIDs(this)) { + if (VeluxItemType.isToBeRefreshedNow(refreshCounter, thingTypeUIDOf(channelUID), channelUID.getId())) { logger.trace("execute(): refreshing item {}.", channelUID); handleCommand(channelUID, RefreshType.REFRESH); } } - this.refreshCounter++; - logger.debug("execute() done."); + logger.debug("execute() initiated by {} finished cycle {}.", Thread.currentThread(), refreshCounter); + refreshCounter++; } @Override @@ -271,7 +280,7 @@ public void initialize() { logger.trace("initialize(): preparing background initialization task."); - // Background initialization: + // Background initialization... scheduler.execute(() -> { logger.trace("initialize.scheduled(): Further work within scheduler.execute()."); logger.trace("initialize.scheduled(): Initializing bridge configuration parameters."); @@ -285,8 +294,7 @@ public void initialize() { try { execute(); } catch (RuntimeException e) { - logger.warn("Exception occurred during activated refresh scheduler: {}, {}.", e.getMessage(), - ExceptionUtils.getStackTrace(e)); + logger.warn("Exception occurred during activated refresh scheduler: {}.", e.getMessage()); } }, this.veluxBridgeConfiguration.refreshMSecs, this.veluxBridgeConfiguration.refreshMSecs, TimeUnit.MILLISECONDS); @@ -301,6 +309,8 @@ public void initialize() { @Override public synchronized void dispose() { logger.trace("dispose({}) called.", this); + logger.trace("dispose(): shutting down scheduled bridge I/O tasks."); + handleScheduler.shutdownNow(); logger.debug("Shutting down Velux veluxBridge '{}'.", getThing().getUID()); // Just for avoidance of Potential null pointer access ScheduledFuture refreshJobX = refreshJob; @@ -323,15 +333,19 @@ public synchronized void dispose() { * NOTE: It takes care by calling {@link #handleCommand} with the REFRESH command, that every used channel is * initialized. */ + @SuppressWarnings("unused") @Override public void channelLinked(ChannelUID channelUID) { logger.trace("channelLinked({}) called.", channelUID.getAsString()); - if (thing.getStatus() == ThingStatus.ONLINE) { - logger.trace("channelLinked() refreshing channel value with help of handleCommand as Thing is online."); - handleCommand(channelUID, RefreshType.REFRESH); - } else { - logger.trace("channelLinked() doing nothing as Thing is not online."); + // TODO ... code eliminated for OH2.5 + if (false) { + if (thing.getStatus() == ThingStatus.ONLINE) { + logger.trace("channelLinked() refreshing channel value with help of handleCommand as Thing is online."); + handleCommand(channelUID, RefreshType.REFRESH); + } else { + logger.trace("channelLinked() doing nothing as Thing is not online."); + } } } @@ -355,7 +369,7 @@ public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) { /*** *** Reconfiguration methods ***/ - private synchronized void bridgeParamsUpdated() { + private void bridgeParamsUpdated() { logger.debug("bridgeParamsUpdated() called."); // Determine the appropriate bridge communication channel @@ -394,13 +408,13 @@ private synchronized void bridgeParamsUpdated() { logger.trace("bridgeParamsUpdated(): Fetching existing scenes."); bridgeParameters.scenes.getScenes(thisBridge); - String scenes = bridgeParameters.scenes.getChannel().existingScenes.toString(false, "\n\t"); - logger.info("Found {} scenes:\n\t{}", VeluxBindingConstants.BINDING_ID, scenes); + logger.info("Found {} scenes:\n\t{}", VeluxBindingConstants.BINDING_ID, + bridgeParameters.scenes.getChannel().existingScenes.toString(false, "\n\t")); logger.trace("bridgeParamsUpdated(): Fetching existing actuators/products."); bridgeParameters.actuators.getProducts(thisBridge); - String products = bridgeParameters.actuators.getChannel().existingProducts.toString(false, "\n\t"); - logger.info("Found {} actuators:\n\t{}", VeluxBindingConstants.BINDING_ID, products); + logger.info("Found {} actuators:\n\t{}", VeluxBindingConstants.BINDING_ID, + bridgeParameters.actuators.getChannel().existingProducts.toString(false, "\n\t")); if (thisBridge.bridgeAPI().setHouseStatusMonitor() != null) { logger.trace("bridgeParamsUpdated(): Activating HouseStatusMonitor."); @@ -425,31 +439,23 @@ private synchronized void bridgeParamsUpdated() { * update the corresponding states via openHAB event bus. */ private void syncChannelsWithProducts() { - logger.trace("syncChannelsWithProducts() called."); if (!bridgeParameters.actuators.getChannel().existingProducts.isDirty()) { - logger.trace("syncChannelsWithProducts() done."); return; } logger.trace("syncChannelsWithProducts(): existingProducts have changed."); outer: for (VeluxProduct product : bridgeParameters.actuators.getChannel().existingProducts .valuesOfModified()) { - logger.trace("syncChannelsWithProducts(): actuator {} has changed values.", - product.getProductName().toString()); - for (ChannelUID channelUID : BridgeChannels.getAllChannelUIDs(this)) { - log.fulltrace("syncChannelsWithProducts(): evaluating ChannelUID {}.", channelUID); + logger.trace("syncChannelsWithProducts(): actuator {} has changed values.", product.getProductName()); + for (ChannelUID channelUID : BridgeChannels.getAllLinkedChannelUIDs(this)) { String itemName = channelUID.getAsString(); VeluxItemType itemType = VeluxItemType.getByThingAndChannel(thingTypeUIDOf(channelUID), channelUID.getId()); - log.fulltrace("syncChannelsWithProducts(): evaluating VeluxItemType {}.", itemType); - if (itemType == VeluxItemType.UNKNOWN) { - continue; - } switch (itemType) { case ACTUATOR_POSITION: case ACTUATOR_STATE: case ROLLERSHUTTER_POSITION: case WINDOW_POSITION: - log.fulltrace("syncChannelsWithProducts(): found on suitable entry of VeluxItemType."); + // found on suitable entry of VeluxItemType. break; default: continue; @@ -475,19 +481,15 @@ private void syncChannelsWithProducts() { if (product.getSerialNumber().equals(actuatorSerial)) { logger.trace("syncChannelsWithProducts(): product {}/{} used within item {}.", product.getProductName(), product.getSerialNumber(), itemName); - try { - VeluxProductPosition position = new VeluxProductPosition(product.getCurrentPosition()); - if (position.isValid()) { - PercentType positionAsPercent = position.getPositionAsPercentType(isInverted); - logger.debug("syncChannelsWithProducts(): updating item {} to position {}%.", itemName, - positionAsPercent); - updateState(channelUID, positionAsPercent); - } else { - logger.trace("syncChannelsWithProducts(): update of item {} to position {} skipped.", - itemName, position.toString()); - } - } catch (RuntimeException e) { - logger.warn("syncChannelsWithProducts(): getProducts() exception: {}.", e.getMessage()); + VeluxProductPosition position = new VeluxProductPosition(product.getCurrentPosition()); + if (position.isValid()) { + PercentType positionAsPercent = position.getPositionAsPercentType(isInverted); + logger.debug("syncChannelsWithProducts(): updating item {} to position {}%.", itemName, + positionAsPercent); + updateState(channelUID, positionAsPercent); + } else { + logger.trace("syncChannelsWithProducts(): update of item {} to position {} skipped.", itemName, + position); } break outer; } @@ -498,39 +500,61 @@ private void syncChannelsWithProducts() { logger.trace("syncChannelsWithProducts() done."); } + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + logger.trace("handleCommand({}): execution of command {} on channel {} will be scheduled.", + Thread.currentThread(), command, channelUID.getAsString()); + logger.debug("handleCommand({},{}) called.", channelUID.getAsString(), command); + + // Background execution of bridge related I/O + handleScheduler.execute(() -> { + logger.trace("handleCommand.scheduler.execute({}) Start work with calling handleCommandScheduled().", + Thread.currentThread()); + handleCommandScheduled(channelUID, command); + logger.trace("handleCommand.scheduler.execute({}) done.", Thread.currentThread()); + }); + logger.trace("handleCommand({}) done.", Thread.currentThread()); + } + /** - * General two-way communication method. + * Normally called by {@link #handleCommand} to handle a command for a given channel with possibly long execution + * time. + *

    + * NOTE: This method is to be called as separated thread to ensure proper openHAB framework in parallel. + *

    * - * It provides either information retrieval or information update according to the passed command. - * - * @param channelUID The item passed as type {@link ChannelUID} for which to following command is addressed to. - * @param command The command passed as type {@link Command} for the mentioned item. If - * {@code command} is {@code null}, an information retrieval via a Refresh command - * is initiated for this item. + * @param channelUID the {@link ChannelUID} of the channel to which the command was sent + * @param command the {@link Command} */ - @Override - public synchronized void handleCommand(ChannelUID channelUID, Command command) { - logger.debug("handleCommand({},{}) called.", channelUID.getAsString(), command); + private synchronized void handleCommandScheduled(ChannelUID channelUID, Command command) { + logger.trace("handleCommandScheduled({}): execution of command {} on channel {}.", Thread.currentThread(), + command, channelUID.getAsString()); + logger.debug("handleCommandScheduled({},{}) called.", channelUID.getAsString(), command); /* * =========================================================== * Common part */ + // if (veluxBridgeConfiguration.isProtocolTraceEnabled) { + if (true) { + Threads.findDeadlocked(); + } + String channelId = channelUID.getId(); State newState = null; String itemName = channelUID.getAsString(); VeluxItemType itemType = VeluxItemType.getByThingAndChannel(thingTypeUIDOf(channelUID), channelUID.getId()); if (itemType == VeluxItemType.UNKNOWN) { - logger.warn("handleCommand(): cannot determine type of Channel {}, ignoring command {}.", channelUID, - command); - logger.trace("handleCommand() aborting."); + logger.warn("{} Cannot determine type of Channel {}, ignoring command {}.", + VeluxBindingConstants.LOGGING_CONTACT, channelUID, command); + logger.trace("handleCommandScheduled() aborting."); return; } if (veluxBridgeConfiguration.hasChanged) { - logger.trace("handleCommand(): work on updated bridge configuration parameters."); + logger.trace("handleCommandScheduled(): work on updated bridge configuration parameters."); bridgeParamsUpdated(); } @@ -541,18 +565,19 @@ public synchronized void handleCommand(ChannelUID channelUID, Command command) { * =========================================================== * Refresh part */ - logger.trace("handleCommand(): work on refresh."); + logger.trace("handleCommandScheduled(): work on refresh."); if (!itemType.isReadable()) { - logger.debug("handleCommand(): received a Refresh command for a non-readable item."); + logger.debug("handleCommandScheduled(): received a Refresh command for a non-readable item."); } else { - logger.trace("handleCommand(): refreshing item {} (type {}).", itemName, itemType); + logger.trace("handleCommandScheduled(): refreshing item {} (type {}).", itemName, itemType); switch (itemType) { // Bridge channels case BRIDGE_STATUS: newState = ChannelBridgeStatus.handleRefresh(channelUID, channelId, this); break; - case BRIDGE_TIMESTAMP: - newState = new DecimalType(thisBridge.lastSuccessfulCommunication()); + case BRIDGE_DOWNTIME: + newState = new DecimalType( + thisBridge.lastCommunication() - thisBridge.lastSuccessfulCommunication()); break; case BRIDGE_FIRMWARE: newState = ChannelBridgeFirmware.handleRefresh(channelUID, channelId, this); @@ -583,13 +608,15 @@ public synchronized void handleCommand(ChannelUID channelUID, Command command) { case WINDOW_POSITION: newState = ChannelActuatorPosition.handleRefresh(channelUID, channelId, this); break; - case ACTUATOR_LIMITATION: - case ROLLERSHUTTER_LIMITATION: - case WINDOW_LIMITATION: - // TODO: waiting for feedback from Velux engineering - if (false) { - ChannelActuatorLimitation.handleRefresh(channelUID, channelId, this); - } + case ACTUATOR_LIMIT_MINIMUM: + case ROLLERSHUTTER_LIMIT_MINIMUM: + case WINDOW_LIMIT_MINIMUM: + newState = ChannelActuatorLimitation.handleRefresh(channelUID, "", this); + break; + case ACTUATOR_LIMIT_MAXIMUM: + case ROLLERSHUTTER_LIMIT_MAXIMUM: + case WINDOW_LIMIT_MAXIMUM: + newState = ChannelActuatorLimitation.handleRefresh(channelUID, channelId, this); break; // VirtualShutter channels @@ -598,31 +625,34 @@ public synchronized void handleCommand(ChannelUID channelUID, Command command) { break; default: - logger.trace("handleCommand(): cannot handle REFRESH on channel {} as it is of type {}.", + logger.trace( + "handleCommandScheduled(): cannot handle REFRESH on channel {} as it is of type {}.", itemName, channelId); } - } - if (newState != null) { - logger.debug("handleCommand(): updating {} ({}) to {}.", itemName, channelUID, newState); - updateState(channelUID, newState); - } else { - logger.info("handleCommand({},{}): updating of item {} (type {}) failed.", channelUID.getAsString(), - command, itemName, itemType); + if (newState != null) { + logger.debug("handleCommandScheduled(): updating {} ({}) to {}.", itemName, channelUID, newState); + updateState(channelUID, newState); + } else { + logger.info("handleCommandScheduled({},{}): updating of item {} (type {}) failed.", + channelUID.getAsString(), command, itemName, itemType); + } } } else { /* * =========================================================== * Modification part */ - logger.trace("handleCommand(): working on item {} (type {}) with COMMAND {}.", itemName, itemType, command); + logger.trace("handleCommandScheduled(): working on item {} (type {}) with COMMAND {}.", itemName, itemType, + command); + Command newValue = null; switch (itemType) { // Bridge channels case BRIDGE_RELOAD: - if (command.equals(OnOffType.ON)) { - logger.trace("handleCommand(): about to reload informations from veluxBridge."); + if (command == OnOffType.ON) { + logger.trace("handleCommandScheduled(): about to reload informations from veluxBridge."); bridgeParamsUpdated(); } else { - logger.trace("handleCommand(): ignoring OFF command."); + logger.trace("handleCommandScheduled(): ignoring OFF command."); } break; case BRIDGE_DO_DETECTION: @@ -644,39 +674,36 @@ public synchronized void handleCommand(ChannelUID channelUID, Command command) { case WINDOW_POSITION: ChannelActuatorPosition.handleCommand(channelUID, channelId, command, this); break; - case ACTUATOR_LIMITATION: - case ROLLERSHUTTER_LIMITATION: - case WINDOW_LIMITATION: - // TODO: waiting for feedback from Velux engineering - if (false) { - ChannelActuatorLimitation.handleCommand(channelUID, channelId, command, this); - } + case ACTUATOR_LIMIT_MINIMUM: + case ROLLERSHUTTER_LIMIT_MINIMUM: + case WINDOW_LIMIT_MINIMUM: + ChannelActuatorLimitation.handleCommand(channelUID, channelId, command, this); + break; + case ACTUATOR_LIMIT_MAXIMUM: + case ROLLERSHUTTER_LIMIT_MAXIMUM: + case WINDOW_LIMIT_MAXIMUM: + ChannelActuatorLimitation.handleCommand(channelUID, channelId, command, this); break; - // ToDo: wait for Velux to correct implementation - // case ACTUATOR_SILENTMODE: - // logger.warn( - // "handleCommand() sorry, but yet not implemented: cannot handle command {} on channel {} (type {}).", - // command, itemName, itemType); - // break; // VirtualShutter channels case VSHUTTER_POSITION: - Command newValue = ChannelVShutterPosition.handleCommand(channelUID, channelId, command, this); - if (newValue != null) { - postCommand(channelUID, newValue); - } + newValue = ChannelVShutterPosition.handleCommand(channelUID, channelId, command, this); break; default: - logger.warn("handleCommand() cannot handle command {} on channel {} (type {}).", command, itemName, - itemType); + logger.warn("{} Cannot handle command {} on channel {} (type {}).", + VeluxBindingConstants.LOGGING_CONTACT, command, itemName, itemType); + } + if (newValue != null) { + postCommand(channelUID, newValue); } } - ThingProperty.setValue(this, VeluxBindingConstants.CHANNEL_BRIDGE_IOTIMESTAMP, thisBridge.lastCommunication()); - ThingProperty.setValue(this, VeluxBindingConstants.CHANNEL_BRIDGE_TIMESTAMP, - thisBridge.lastSuccessfulCommunication()); - logger.trace("handleCommand() done."); + ThingProperty.setValue(this, VeluxBindingConstants.PROPERTY_BRIDGE_TIMESTAMP_ATTEMPT, + new java.util.Date(thisBridge.lastCommunication()).toString()); + ThingProperty.setValue(this, VeluxBindingConstants.PROPERTY_BRIDGE_TIMESTAMP_SUCCESS, + new java.util.Date(thisBridge.lastSuccessfulCommunication()).toString()); + logger.trace("handleCommandScheduled({}) done.", Thread.currentThread()); } } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxHandler.java similarity index 83% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxHandler.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxHandler.java index 2ab8e923acd97..b4641441b96d0 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/VeluxHandler.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxHandler.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.handler; +package org.openhab.binding.velux.internal.handler; import java.util.Map; import java.util.Map.Entry; @@ -23,9 +23,11 @@ import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingStatus; import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.binding.BridgeHandler; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.RefreshType; import org.openhab.binding.velux.internal.config.VeluxThingConfiguration; +import org.openhab.binding.velux.internal.utils.Localization; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,9 +44,9 @@ public class VeluxHandler extends ExtendedBaseThingHandler { @SuppressWarnings("unused") private @Nullable VeluxThingConfiguration configuration = null; - public VeluxHandler(Thing thing) { + public VeluxHandler(Thing thing, Localization localization) { super(thing); - logger.trace("VeluxHandler(constructor) called."); + logger.trace("VeluxHandler(thing={},localization={}) constructor called.", thing, localization); } @Override @@ -92,15 +94,20 @@ public void channelLinked(ChannelUID channelUID) { } } - @SuppressWarnings("null") @Override public void handleCommand(ChannelUID channelUID, Command command) { - logger.trace("handleCommand({},{}) called.", channelUID.getAsString(), command); - - if (getBridge() == null) { + logger.trace("handleCommand({},{}) initiated by {}.", channelUID.getAsString(), command, + Thread.currentThread()); + Bridge bridge = getBridge(); + if (bridge == null) { logger.trace("handleCommand() nothing yet to do as there is no bridge available."); } else { - getBridge().getHandler().handleCommand(channelUID, command); + BridgeHandler handler = bridge.getHandler(); + if (handler == null) { + logger.trace("handleCommand() nothing yet to do as thing is not initialized."); + } else { + handler.handleCommand(channelUID, command); + } } logger.trace("handleCommand() done."); } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/package-info.java similarity index 90% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/package-info.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/package-info.java index ae1993935f7b7..6ecc6e1b6bec0 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/handler/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/package-info.java @@ -15,4 +15,4 @@ * * @author Guenther Schreiner - Initial contribution */ -package org.openhab.binding.velux.handler; +package org.openhab.binding.velux.internal.handler; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxExistingProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingProducts.java similarity index 94% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxExistingProducts.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingProducts.java index e4cc4f71db87c..efd86268149f8 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxExistingProducts.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingProducts.java @@ -10,13 +10,13 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.things; +package org.openhab.binding.velux.internal.things; import java.util.concurrent.ConcurrentHashMap; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.things.VeluxProduct.ProductBridgeIndex; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,13 +26,13 @@ *

    * The following class access methods exist: *

      - *
    • {@link VeluxExistingProducts#isRegistered} for querying existence of a {@link VeluxProduct}, - *
    • {@link VeluxExistingProducts#register} for storing a {@link VeluxProduct}, - *
    • {@link VeluxExistingProducts#update} for updating/storing of a {@link VeluxProduct}, - *
    • {@link VeluxExistingProducts#get} for retrieval of a {@link VeluxProduct}, - *
    • {@link VeluxExistingProducts#values} for retrieval of all {@link VeluxProduct}s, - *
    • {@link VeluxExistingProducts#getNoMembers} for retrieval of the number of all {@link VeluxProduct}s, - *
    • {@link VeluxExistingProducts#toString} for a descriptive string representation. + *
    • {@link VeluxExistingProducts#isRegistered} for querying existence of a {@link VeluxProduct},
    • + *
    • {@link VeluxExistingProducts#register} for storing a {@link VeluxProduct},
    • + *
    • {@link VeluxExistingProducts#update} for updating/storing of a {@link VeluxProduct},
    • + *
    • {@link VeluxExistingProducts#get} for retrieval of a {@link VeluxProduct},
    • + *
    • {@link VeluxExistingProducts#values} for retrieval of all {@link VeluxProduct}s,
    • + *
    • {@link VeluxExistingProducts#getNoMembers} for retrieval of the number of all {@link VeluxProduct}s,
    • + *
    • {@link VeluxExistingProducts#toString} for a descriptive string representation.
    • *
    * * @see VeluxProduct @@ -83,7 +83,7 @@ public boolean isRegistered(VeluxProduct product) { return isRegistered(product.getProductUniqueIndex()); } - public boolean isRegistered(ProductBridgeIndex bridgeProductIndex) { // NO_UCD (use private) + public boolean isRegistered(ProductBridgeIndex bridgeProductIndex) { logger.trace("isRegisteredProductBridgeIndex {}) called.", bridgeProductIndex.toString()); if (!bridgeIndexToSerialNumber.containsKey(bridgeProductIndex.toInt())) { return false; @@ -150,7 +150,7 @@ public VeluxProduct get(String productUniqueIndexOrSerialNumber) { return existingProductsByUniqueIndex.get(productUniqueIndexOrSerialNumber); } - public VeluxProduct get(ProductBridgeIndex bridgeProductIndex) { // NO_UCD (use private) + public VeluxProduct get(ProductBridgeIndex bridgeProductIndex) { logger.trace("get({}) called.", bridgeProductIndex); if (!isRegistered(bridgeProductIndex)) { return VeluxProduct.UNKNOWN; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxExistingScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingScenes.java similarity index 91% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxExistingScenes.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingScenes.java index 7960a00a99ccb..847079b262628 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxExistingScenes.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingScenes.java @@ -10,13 +10,13 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.things; +package org.openhab.binding.velux.internal.things; import java.util.concurrent.ConcurrentHashMap; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.VeluxBindingConstants; -import org.openhab.binding.velux.things.VeluxScene.SceneName; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.things.VeluxScene.SceneName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,12 +26,12 @@ *

    * The following class access methods exist: *

      - *
    • {@link VeluxExistingScenes#isRegistered} for querying existence of a {@link VeluxScene}, - *
    • {@link VeluxExistingScenes#register} for storing a {@link VeluxScene}, - *
    • {@link VeluxExistingScenes#get} for retrieval of a {@link VeluxScene}, - *
    • {@link VeluxExistingScenes#values} for retrieval of all {@link VeluxScene}s, - *
    • {@link VeluxExistingScenes#getNoMembers} for retrieval of the number of all {@link VeluxScene}s, - *
    • {@link VeluxExistingScenes#toString} for a descriptive string representation. + *
    • {@link VeluxExistingScenes#isRegistered} for querying existence of a {@link VeluxScene},
    • + *
    • {@link VeluxExistingScenes#register} for storing a {@link VeluxScene},
    • + *
    • {@link VeluxExistingScenes#get} for retrieval of a {@link VeluxScene},
    • + *
    • {@link VeluxExistingScenes#values} for retrieval of all {@link VeluxScene}s,
    • + *
    • {@link VeluxExistingScenes#getNoMembers} for retrieval of the number of all {@link VeluxScene}s,
    • + *
    • {@link VeluxExistingScenes#toString} for a descriptive string representation.
    • *
    * * @see VeluxScene diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwFirmware.java similarity index 91% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwFirmware.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwFirmware.java index 0d0727f6b1881..36a39c3005cf0 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwFirmware.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwFirmware.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.things; +package org.openhab.binding.velux.internal.things; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.internal.VeluxBindingConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwLAN.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwLAN.java similarity index 95% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwLAN.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwLAN.java index b28124ec273da..86f35cc10063e 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwLAN.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwLAN.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.things; +package org.openhab.binding.velux.internal.things; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.internal.VeluxBindingConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwState.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwState.java similarity index 99% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwState.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwState.java index 823733660aba4..d832343ca83e6 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwState.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwState.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.things; +package org.openhab.binding.velux.internal.things; import java.util.Map; import java.util.function.Function; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwWLAN.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwWLAN.java similarity index 92% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwWLAN.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwWLAN.java index d379bb7583f71..d9eb5334fb97b 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxGwWLAN.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwWLAN.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.things; +package org.openhab.binding.velux.internal.things; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.internal.VeluxBindingConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxKLFAPI.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxKLFAPI.java similarity index 99% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxKLFAPI.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxKLFAPI.java index e1ddc7278dec0..941e8c71cc2fc 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxKLFAPI.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxKLFAPI.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.things; +package org.openhab.binding.velux.internal.things; import java.util.Map; import java.util.function.Function; @@ -66,6 +66,7 @@ public class VeluxKLFAPI { *
  • Method {@link toString} to return a String.
  • * */ + @NonNullByDefault public static class CommandName { private String name; @@ -90,6 +91,7 @@ public String toString() { *
  • Method {@link toString} to return a well-formatted String.
  • * */ + @NonNullByDefault public static class CommandNumber { private short commandNumber; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProduct.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProduct.java similarity index 97% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProduct.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProduct.java index 262a169a1bd65..5e1f5265af6a8 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProduct.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProduct.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.things; +package org.openhab.binding.velux.internal.things; import org.eclipse.jdt.annotation.NonNullByDefault; import org.slf4j.Logger; @@ -33,6 +33,7 @@ public class VeluxProduct { // Type definitions + @NonNullByDefault public static class ProductBridgeIndex { // Public definition @@ -260,7 +261,7 @@ public int getState() { * @param newState Update the operating state of the node. * @return modified as type boolean to signal a real modification. */ - public boolean setState(int newState) { // NO_UCD (use default) + public boolean setState(int newState) { if (this.state == newState) { return false; } else { @@ -282,7 +283,7 @@ public int getCurrentPosition() { * @param newCurrentPosition Update the current position of the node. * @return modified as boolean to signal a real modification. */ - public boolean setCurrentPosition(int newCurrentPosition) { // NO_UCD (use default) + public boolean setCurrentPosition(int newCurrentPosition) { if (this.currentPosition == newCurrentPosition) { return false; } else { @@ -304,7 +305,7 @@ public int getTarget() { * @param newTarget Update the target position of the current operation. * @return modified as boolean to signal a real modification. */ - public boolean setTarget(int newTarget) { // NO_UCD (use default) + public boolean setTarget(int newTarget) { if (this.target == newTarget) { return false; } else { diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductName.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductName.java similarity index 78% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductName.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductName.java index 959622ab843fa..c9c8d8eaa1542 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductName.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductName.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.things; +package org.openhab.binding.velux.internal.things; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.internal.VeluxBindingConstants; /** * Velux product representation. @@ -27,8 +27,7 @@ public class VeluxProductName { // Public definition - public static final VeluxProductName UNKNOWN = new VeluxProductName(VeluxBindingConstants.UNKNOWN); // NO_UCD (use - // default) + public static final VeluxProductName UNKNOWN = new VeluxProductName(VeluxBindingConstants.UNKNOWN); // Class internal diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductPosition.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductPosition.java similarity index 92% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductPosition.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductPosition.java index 30274874fb37e..88ee85af68730 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductPosition.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductPosition.java @@ -10,11 +10,11 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.things; +package org.openhab.binding.velux.internal.things; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.PercentType; -import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.internal.VeluxBindingConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,13 +42,14 @@ */ @NonNullByDefault public class VeluxProductPosition { - private static final Logger LOGGER = LoggerFactory.getLogger(VeluxProductPosition.class); private final Logger logger = LoggerFactory.getLogger(getClass()); // Public definition public static final VeluxProductPosition UNKNOWN = new VeluxProductPosition(); - public static final int VPP_VELUX_STOP = 0xD200; // NO_UCD (use private) + public static final int VPP_VELUX_STOP = 0xD200; + public static final int VPP_VELUX_DEFAULT = 0xD300; + public static final int VPP_VELUX_IGNORE = 0xD400; // Make sure that the calculation are done as non-integer private static final float ONE = 1; @@ -148,8 +149,7 @@ public String toString() { // Helper methods - public static int getRelativePositionAsVeluxType(boolean upwards, PercentType position) { // NO_UCD (unused code) - LOGGER.trace("getRelativePositionAsVeluxType(upwards={},{}) created.", upwards, position); + public static int getRelativePositionAsVeluxType(boolean upwards, PercentType position) { float result = (VPP_VELUX_PERCENTAGE_MAX + VPP_VELUX_PERCENTAGE_MIN) / 2; if (upwards) { result = result + (ONE * position.intValue() - VPP_OPENHAB_MIN) / (VPP_OPENHAB_MAX - VPP_OPENHAB_MIN) @@ -158,7 +158,6 @@ public static int getRelativePositionAsVeluxType(boolean upwards, PercentType po result = result - (ONE * position.intValue() - VPP_OPENHAB_MIN) / (VPP_OPENHAB_MAX - VPP_OPENHAB_MIN) * ((VPP_VELUX_PERCENTAGE_MAX - VPP_VELUX_PERCENTAGE_MIN) / 2); } - LOGGER.trace("getRelativePositionAsVeluxType() returns {}.", (int) result); return (int) result; } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductReference.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductReference.java similarity index 95% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductReference.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductReference.java index 08bddc4929d2d..24196f37e24dc 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductReference.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductReference.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.things; +package org.openhab.binding.velux.internal.things; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.internal.VeluxBindingConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductSerialNo.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductSerialNo.java similarity index 98% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductSerialNo.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductSerialNo.java index 14d4acd1fb397..48a40469bbdea 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductSerialNo.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductSerialNo.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.things; +package org.openhab.binding.velux.internal.things; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -75,7 +75,7 @@ private VeluxProductSerialNo() { * @param separator as of Type String. * @return serialNumberString of type String. */ - public static String toString(byte[] serialNumber, String separator) { // NO_UCD (use private) + public static String toString(byte[] serialNumber, String separator) { StringBuilder sb = new StringBuilder(); for (byte b : serialNumber) { sb.append(String.format("%02X", b)); diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductState.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductState.java similarity index 97% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductState.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductState.java index 9833726a8b763..8f5ef464c10fd 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductState.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductState.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.things; +package org.openhab.binding.velux.internal.things; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -37,6 +37,7 @@ public class VeluxProductState { // Type definitions + @NonNullByDefault private class ProductState { private int state; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductType.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductType.java similarity index 97% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductType.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductType.java index 522ced7e09a75..98fc5c86487e3 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductType.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductType.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.things; +package org.openhab.binding.velux.internal.things; import java.util.Map; import java.util.function.Function; @@ -18,7 +18,7 @@ import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.internal.VeluxBindingConstants; /** * Velux product characteristics: Product Type. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductVelocity.java similarity index 91% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductVelocity.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductVelocity.java index de47cf096381e..5816853f67736 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxProductVelocity.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductVelocity.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.things; +package org.openhab.binding.velux.internal.things; import java.util.Map; import java.util.function.Function; @@ -18,7 +18,7 @@ import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.internal.VeluxBindingConstants; /** * Velux product characteristics: Velocity. @@ -69,11 +69,11 @@ public short getVelocity() { return velocity; } - public static VeluxProductVelocity get(int velocity) { // NO_UCD (unused code) + public static VeluxProductVelocity get(int velocity) { return LOOKUPTYPEID2ENUM.getOrDefault(velocity, VeluxProductVelocity.UNDEFTYPE); } - public static VeluxProductVelocity getByName(String velocityName) { // NO_UCD (unused code) + public static VeluxProductVelocity getByName(String velocityName) { for (VeluxProductVelocity enumItem : VeluxProductVelocity.values()) { if (enumItem.velocityName.equals(velocityName)) { return enumItem; @@ -82,7 +82,7 @@ public static VeluxProductVelocity getByName(String velocityName) { // NO_UCD (u return VeluxProductVelocity.UNDEFTYPE; } - public static String dump() { // NO_UCD (unused code) + public static String dump() { StringBuilder sb = new StringBuilder(); for (VeluxProductVelocity typeId : VeluxProductVelocity.values()) { sb.append(typeId).append(VeluxBindingConstants.OUTPUT_VALUE_SEPARATOR); diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxScene.java similarity index 94% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxScene.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxScene.java index dc1da61a1e499..4bcd5f1148d28 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/VeluxScene.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxScene.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.things; +package org.openhab.binding.velux.internal.things; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.VeluxBindingConstants; +import org.openhab.binding.velux.internal.VeluxBindingConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,6 +45,7 @@ public class VeluxScene { // Type definitions + @NonNullByDefault public static class SceneName { private static final SceneName UNKNOWN = new SceneName(VeluxBindingConstants.UNKNOWN); @@ -65,7 +66,8 @@ public boolean equals(SceneName anotherName) { } } - public static class SceneBridgeIndex { // NO_UCD (use private) + @NonNullByDefault + public static class SceneBridgeIndex { private static final SceneBridgeIndex UNKNOWN = new SceneBridgeIndex(0); diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/package-info.java similarity index 91% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/package-info.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/package-info.java index 1d552ca0c938c..a4e4b7b7f30e4 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/things/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/package-info.java @@ -15,4 +15,4 @@ * * @author Guenther Schreiner - Initial contribution */ -package org.openhab.binding.velux.things; +package org.openhab.binding.velux.internal.things; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/Localization.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/Localization.java index 252cf680e90e1..87965dba1effa 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/Localization.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/Localization.java @@ -12,10 +12,18 @@ */ package org.openhab.binding.velux.internal.utils; +import java.util.Locale; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.i18n.LocaleProvider; +import org.eclipse.smarthome.core.i18n.TranslationProvider; +import org.osgi.framework.Bundle; +import org.osgi.framework.FrameworkUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * This is a workaround class for dealing with localization. + * This is a utility class for dealing with localization. * * It provides the following methods: *
      @@ -27,6 +35,31 @@ */ @NonNullByDefault public class Localization { + private final Logger logger = LoggerFactory.getLogger(Localization.class); + + // Public definition + + public static final Localization UNKNOWN = new Localization(); + + /* + * *************************** + * ***** Private Objects ***** + */ + private static final String OPENBRACKET = "("; + private static final String CLOSEBRACKET = ")"; + private LocaleProvider localeProvider; + private @NonNullByDefault({}) TranslationProvider i18nProvider; + + /** + * Class, which is needed to maintain a @NonNullByDefault for class {@link Localization}. + */ + @NonNullByDefault + private class UnknownLocale implements LocaleProvider { + @Override + public Locale getLocale() { + return java.util.Locale.ROOT; + } + } /* * ************************ @@ -34,10 +67,26 @@ public class Localization { */ /** - * Suppress default constructor for creating a non-instantiable class. + * Constructor + *

      + * Initializes the {@link Localization} module without any framework informations. */ - private Localization() { - throw new AssertionError(); + Localization() { + this.localeProvider = new UnknownLocale(); + } + + /** + * Constructor + *

      + * Initializes the {@link Localization} module with framework informations. + * + * @param localeProvider providing a locale, + * @param i18nProvider as service interface for internationalization. + */ + public Localization(final LocaleProvider localeProvider, final TranslationProvider i18nProvider) { + logger.trace("Localization(Constructor w/ {},{}) called.", localeProvider, i18nProvider); + this.localeProvider = localeProvider; + this.i18nProvider = i18nProvider; } /** @@ -47,9 +96,23 @@ private Localization() { * @param arguments (optional) arguments being referenced within the messageString. * @return localizedMessageString the resulted message of type {@link String}. */ - public static String getText(String key, Object... arguments) { - // ToDo: a well-working solution still to be found - String text = String.format(key, arguments); + public String getText(String key, Object... arguments) { + logger.trace("getText({}) called.", key); + + if (i18nProvider == null) { + return key; + } + + Bundle bundle = FrameworkUtil.getBundle(this.getClass()).getBundleContext().getBundle(); + Locale locale = localeProvider.getLocale(); + String defaultText = OPENBRACKET.concat(key).concat(CLOSEBRACKET); + + String text = i18nProvider.getText(bundle, key, defaultText, locale, arguments); + if (text == null) { + logger.warn("Internal error: localization for key {} is missing.", key); + text = defaultText; + } + logger.trace("getText() returns {}.", text); return text; } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/LoggerFulltrace.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/LoggerFulltrace.java deleted file mode 100644 index 0d197c661f034..0000000000000 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/LoggerFulltrace.java +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright (c) 2010-2019 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.velux.internal.utils; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.slf4j.Logger; - -/** - * This is an extension of org.slf4j.Logger interface to provide an intensive logging - * for framework debugging. - *

      Typical usage pattern:

      - * - *
      - * public class Wombat {
      - *
      - *   private final Logger logger = LoggerFactory.getLogger(Demo.class);
      - *   
      - *   final static LoggerFulltrace log = new LoggerFulltrace(logger,false);
      - *   Integer t;
      - *   Integer oldT;
      - *
      - *   public void setTemperature(Integer temperature) {
      - *     oldT = t;
      - *     t = temperature;
      - *     log.fulltrace("Temperature set to {}. Old temperature was {}.", t, oldT);
      - *     if(temperature.intValue() > 50) {
      - *       log.fulltrace("Temperature has risen above 50 degrees.");
      - *     }
      - *   }
      - * }
      - * 
      - * - * @author Guenther Schreiner - Initial contribution - */ -@NonNullByDefault -public class LoggerFulltrace { - private boolean enabled; - private Logger logger; - - /* Constructor */ - public LoggerFulltrace(Logger logger, boolean enabled) { - this.logger = logger; - this.enabled = enabled; - } - - /** - * Log a message at the TRACE level. - * - * @param msg the message string to be logged - */ - public void fulltrace(String msg) { - if (enabled) { - logger.trace("{}", msg); - } - }; - - /** - * Log a message at the TRACE level according to the specified format - * and argument. - *

      - * This form avoids superfluous object creation when the logger - * is disabled for the TRACE level. - *

      - * - * @param format the format string - * @param arg the argument - */ - public void fulltrace(String format, Object arg) { - if (enabled) { - // [ERROR] Format should be constant. Use placeholder to reduce the needless cost of parameter - // construction. see http://www.slf4j.org/faq.html#logging_performance - // SOLVED by bad hack. WAIT for Jenkis check being fixed - // logger.trace(format, arg); - logger.trace("{}/{}", format, arg); - } - }; - -} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/ManifestInformation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/ManifestInformation.java index a884c0c50b182..20864db4df97f 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/ManifestInformation.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/ManifestInformation.java @@ -13,8 +13,7 @@ package org.openhab.binding.velux.internal.utils; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.osgi.framework.FrameworkUtil; /** * This is a helper class for dealing with information from MANIFEST file. @@ -29,7 +28,6 @@ */ @NonNullByDefault public class ManifestInformation { - private static final Logger LOGGER = LoggerFactory.getLogger(ManifestInformation.class); /* * ************************ @@ -43,15 +41,16 @@ private ManifestInformation() { throw new AssertionError(); } + // Class access methods + /** * Returns the bundle version as specified within the MANIFEST file. * * @return bundleVersion the resulted bundle version as {@link String}. */ public static String getBundleVersion() { - String osgiBundleVersion = org.osgi.framework.FrameworkUtil.getBundle(ManifestInformation.class) - .getBundleContext().getBundle().toString(); - LOGGER.trace("getBundleVersion() has found {}.", osgiBundleVersion); + String osgiBundleVersion = FrameworkUtil.getBundle(ManifestInformation.class).getBundleContext().getBundle() + .toString(); return osgiBundleVersion; } diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/config/config.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/config/config.xml index 08290ca86b54e..e5b61344d4fab 100644 --- a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/config/config.xml +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/config/config.xml @@ -12,6 +12,7 @@ @text/config.velux.bridge.ipAddress.description + network-address true @@ -42,7 +43,7 @@ velux123 - + @text/config.velux.bridge.timeoutMsecs.description false @@ -56,11 +57,11 @@ 5 true - + @text/config.velux.bridge.refreshMsecs.description false - 10000 + 15000 true diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux.properties b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux.properties index ce0eb2b78817d..20e63febeea38 100644 --- a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux.properties +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux.properties @@ -42,6 +42,10 @@ thing-type.velux.vshutter.description = A Set of Scenes which act together as Sh thing-type.velux.window.label = Velux Window Information thing-type.velux.window.description = Control a window on the Velux KLF 200. # +# Discovery descriptions +# +discovery.velux.binding...label = Velux Binding Information Element +# # Config descriptions # config.velux.bridge.ipAddress.label = IP Address @@ -76,8 +80,10 @@ config.velux.thing.actuator.name.label = Name config.velux.thing.actuator.name.description = (Optional) Name of the Actuator as defined in the gateway. config.velux.thing.actuator.inverted.label = Actuator Position Inverted config.velux.thing.actuator.inverted.description = The actuator position is inverted (i.e. 0% translates to 100%). -config.velux.thing.actuator.limitation.label = Limitation Position -config.velux.thing.actuator.limitation.description = The limitation position of the actuator. +config.velux.thing.actuator.limitMinimum.label = Minimum Limitation Position +config.velux.thing.actuator.limitMinimum.description = The minimum limitation position of the actuator. +config.velux.thing.actuator.limitMaximum.label = Maximum Limitation Position +config.velux.thing.actuator.limitMaximum.description = The maximum limitation position of the actuator. # config.velux.thing.rollershutter.serial.label = Serial Number config.velux.thing.rollershutter.serial.description = Eight hex digits (i.e. 65:23:3E:26:0C:1B:00:10). @@ -85,8 +91,10 @@ config.velux.thing.rollershutter.name.label = Name config.velux.thing.rollershutter.name.description = (Optional) Name of the Actuator as defined in the gateway. config.velux.thing.rollershutter.inverted.label = Actuator Position Inverted config.velux.thing.rollershutter.inverted.description = The actuator position is inverted (i.e. 0% translates to 100%). -config.velux.thing.rollershutter.limitation.label = Limitation Position -config.velux.thing.rollershutter.limitation.description = The limitation position of the rollershutter. +config.velux.thing.rollershutter.limitMinimum.label = Minimum Limitation Position +config.velux.thing.rollershutter.limitMinimum.description = The minimum limitation position of the rollershutter. +config.velux.thing.rollershutter.limitMaximum.label = Maximum Limitation Position +config.velux.thing.rollershutter.limitMaximum.description = The maximum limitation position of the rollershutter. # config.velux.thing.window.serial.label = Serial Number config.velux.thing.window.serial.description = Eight hex digits (i.e. 65:23:3E:26:0C:1B:00:10). @@ -94,8 +102,10 @@ config.velux.thing.window.name.label = Name config.velux.thing.window.name.description = (Optional) Name of the Actuator as defined in the gateway. config.velux.thing.window.inverted.label = Actuator Position Inverted config.velux.thing.window.inverted.description = The actuator position is inverted (i.e. 0% translates to 100%). -config.velux.thing.window.limitation.label = Limitation Position -config.velux.thing.window.limitation.description = The limitation position of the windows. +config.velux.thing.window.limitMinimum.label = Minimum Limitation Position +config.velux.thing.window.limitMinimum.description = The minimum limitation position of the window. +config.velux.thing.window.limitMaximum.label = Maximum Limitation Position +config.velux.thing.window.limitMaximum.description = The maximum limitation position of the window. # config.velux.thing.vshutter.sceneLevels.label = List of Positions/Scenes config.velux.thing.vshutter.sceneLevels.description = Definition of a virtual shutter by declaring one scene corresponding to one position. Therefore this parameter look like: ,,, @@ -110,8 +120,8 @@ channel-type.velux.status.label = Bridge State channel-type.velux.status.description = Description of current Bridge State. channel-type.velux.reload.label = Reload channel-type.velux.reload.description = Reload Bridge Information. -channel-type.velux.timestamp.label = Timestamp -channel-type.velux.timestamp.description = Timestamp when last successful device interaction happened (seconds since epoch). +channel-type.velux.downtime.label = Downtime +channel-type.velux.downtime.description = Time interval between last successful and recent device interaction. channel-type.velux.doDetection.label = Activate Bridge Detection mode channel-type.velux.doDetection.description = Start of the product detection mode. channel-type.velux.firmware.label = Firmware Version @@ -143,8 +153,10 @@ channel-type.velux.action.label = Start of a Scene channel-type.velux.action.description = Activates a set of predefined product settings. channel-type.velux.silentMode.label = Enabling silent mode channel-type.velux.silentMode.description = Activates the silent mode of the predefined product settings. -channel-type.velux.limitation.label = Limitation Position -channel-type.velux.limitation.description = The limitation position of the actuator/rollershutter/window. +channel-type.velux.limitMinimum.label = Minimum Limitation Position +channel-type.velux.limitMinimum.description = The minimum limitation position of the actuator/rollershutter/window. +channel-type.velux.limitMaximum.label = Maximum Limitation Position +channel-type.velux.limitMaximum.description = The maximum limitation position of the actuator/rollershutter/window. channel-type.velux.vposition.label = Position channel-type.velux.vposition.description = Device control (UP, DOWN, STOP, closure 0-100%). # @@ -158,7 +170,7 @@ runtime.bridge-offline-login-sequence-failed = Login sequence failed. # # Thing status descriptions # -channelValue.check-integrity-failed = Integrity check failed: The following scenes are unused: +channelValue.check-integrity-failed = Integrity check failed: The following scenes are unused: {}. channelValue.check-integrity-ok = Integrity check ok. All scenes are used within Items. # # end-of-ESH-INF/i18n/velux.properties diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_de.properties b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_de.properties index d85a0fa6e87eb..357a993d903e2 100644 --- a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_de.properties +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_de.properties @@ -41,7 +41,11 @@ thing-type.velux.vshutter.label = Virtueller Rolladen thing-type.velux.vshutter.description = Rolladen, welcher ber eine Menge von Szenen definiert wird. # thing-type.velux.window.label = Velux Fenster -thing-type.velux.window.description = Fenster, welcher ber io-homecontrol steuert wird. +thing-type.velux.window.description = Fenster, welches ber io-homecontrol steuert wird. +# +# Discovery descriptions +# +discovery.velux.binding...label = Velux Binding Informationselement # # Config descriptions # @@ -77,8 +81,10 @@ config.velux.thing.actuator.name.label = Name config.velux.thing.actuator.name.description = (Optionaler) Name des Aktuators, wie er auf dem Kopplungselements definiert wurde. config.velux.thing.actuator.inverted.label = Positioninvertierung config.velux.thing.actuator.inverted.description = Die Aktuatorposition wird invertiert (bspw. 0% umgesetzt zu 100%). -config.velux.thing.actuator.limitation.label = Begrenzungsposition -config.velux.thing.actuator.limitation.description = Die Begrenzungsposition des Stellantriebs. +config.velux.thing.actuator.limitMinimum.label = Minimale Begrenzungsposition +config.velux.thing.actuator.limitMinimum.description = Die minimale Begrenzungsposition des Stellantriebs. +config.velux.thing.actuator.limitMaximum.label = Maximale Begrenzungsposition +config.velux.thing.actuator.limitMaximum.description = Die maximale Begrenzungsposition des Stellantriebs. # config.velux.thing.rollershutter.serial.label = Seriennumber config.velux.thing.rollershutter.serial.description = Acht hexadezimale Ziffern (bspw. 65:23:3E:26:0C:1B:00:10). @@ -86,166 +92,10 @@ config.velux.thing.rollershutter.name.label = Name config.velux.thing.rollershutter.name.description = (Optionaler) Name des Rolladens, wie er auf dem Kopplungselements definiert wurde. config.velux.thing.rollershutter.inverted.label = Positioninvertierung config.velux.thing.rollershutter.inverted.description = Die Rolladenposition wird invertiert (bspw. 0% umgesetzt zu 100%). -config.velux.thing.rollershutter.limitation.label = Begrenzungsposition -config.velux.thing.rollershutter.limitation.description = Die Begrenzungsposition des Rolladens. -# -config.velux.thing.window.serial.label = Seriennumber -config.velux.thing.window.serial.description = Acht hexadezimale Ziffern (bspw. 65:23:3E:26:0C:1B:00:10). -config.velux.thing.window.name.label = Name -config.velux.thing.window.name.description = (Optionaler) Name des Fensters, wie er auf dem Kopplungselements definiert wurde. -config.velux.thing.window.inverted.label = Positioninvertierung -config.velux.thing.window.inverted.description = Die Fensterposition wird invertiert (bspw. 0% umgesetzt zu 100%). -config.velux.thing.window.limitation.label = Begrenzungsposition -config.velux.thing.window.limitation.description = Die Begrenzungsposition des Fensters. -# -config.velux.thing.vshutter.sceneLevels.label = Liste von Positionen/Szenen -config.velux.thing.vshutter.sceneLevels.description = Definition eines virtuellen Rolladens mittels Festlegung von einer Position durch eine Szene. So sieht der Parameter aus wie: ,,, -config.velux.thing.vshutter.currentLevel.label = Aktuelle Position -config.velux.thing.vshutter.currentLevel.description = Wert zwischen 0 und 100. -# -# Channel types descriptions -# -channel-type.velux.information.label = Binding Information -channel-type.velux.information.description = Beschreibung des aktuellen Binding Status. -channel-type.velux.status.label = Status -channel-type.velux.status.description = Status des Kopplungselements. -channel-type.velux.reload.label = Nachladen -channel-type.velux.reload.description = Konfigurationen neu laden. -channel-type.velux.timestamp.label = Zeitstempel -channel-type.velux.timestamp.description = Zeitpunkt des letzten erfolgreichen Kommunikation -channel-type.velux.doDetection.label = Produkterkennung -channel-type.velux.doDetection.description = Schalter zum Start der Produkterkennung. -channel-type.velux.firmware.label = Firmware -channel-type.velux.firmware.description = Softwareversion des Kopplungselements. -channel-type.velux.ipAddress.label = LAN IP-Adresse -channel-type.velux.ipAddress.description = IP-Adresse des Kopplungselements. -channel-type.velux.subnetMask.label = LAN Subnetzmaske -channel-type.velux.subnetMask.description = IP-Subnetzmaske des Kopplungselements. -channel-type.velux.defaultGW.label = LAN Router-IP-Adresse -channel-type.velux.defaultGW.description = IP-Adresse des Routers. -channel-type.velux.DHCP.label = LAN DHCP-Aktivierung -channel-type.velux.DHCP.description = Aktivierung der automatischen IP-Konfiguration mittels DHCP. -channel-type.velux.WLANSSID.label = WLAN-SSID -channel-type.velux.WLANSSID.description = Netzwerkkennung des Wireless-Netzwerks. -channel-type.velux.WLANPassword.label = WLAN-Passwort -channel-type.velux.WLANPassword.description = Passwort zum Zugriff auf das Wireless-Netzwerk. -channel-type.velux.products.label = Identifizierte Produkte -channel-type.velux.products.description = Produkte welche auf dem Kopplungselement im Rahmen von Szenen definiert sind. -channel-type.velux.scenes.label = Identifizierte Szenen -channel-type.velux.scenes.description = Szenen welche auf dem Kopplungselement definiert sind. -channel-type.velux.check.label = Konfigurationsprfung -channel-type.velux.check.description = Vergleich der Gateway-Konfiguration mit der openHAB-Konfiguration. -# -channel-type.velux.position.label = Position -channel-type.velux.position.description = Positionssteuerungselement (hoch/runter/stop). -channel-type.velux.state.label = Zustand -channel-type.velux.state.description = Steuerung (AN, AUS). -channel-type.velux.action.label = Szenenstart -channel-type.velux.action.description = Schalter zum Start dieser Szene -channel-type.velux.silentMode.label = Stillmodus -channel-type.velux.silentMode.description = Aktiviert den Leise-Modus auf einer definierten Szene. -channel-type.velux.limitation.label = Begrenzungsposition -channel-type.velux.limitation.description = Begrenzungsposition des Aktuators/Rolladens/Fensters. -channel-type.velux.vposition.label = Position -channel-type.velux.vposition.description = Positionssteuerungselement (hoch/runter/stop). -# -# Runtime status descriptions -# -runtime.no-bridge = Bislang ist noch ist kein Kopplungselement definiert. Bitte fgen Sie ein Thing vom Typ "Velux KLF200" hinzu, um eine Verbindung zum Kopplungselement aufzubauen, welches die Voraussetzung fr die weitere Inbetriebnahme darstellt. -runtime.one-bridge = Es ist bereits ein Kopplungselement definiert. Somit knnen Sie nun weitere Gerte mittels Suche (bzw. Discovery) oder durch manuelles Hinzufgen von Things einrichten. -runtime.multiple-bridges = Es sind mehr als ein Kopplungselement definiert. Dies ist natrlich bei mehreren Velux KLF200 Gateways oder bei einem redundanten Aufbau (SLIP ber LAN parallel zu JSON ber WLAN) mglich. Jeder andere Fall sollte vermieden werden. -runtime.bridge-offline-no-valid-bridgeProtocol-selected = Der Parameter "bridgeProtocol" muss korrekt gesetzt werden. -runtime.bridge-offline-login-sequence-failed = Anmeldung am Kopplungselement fehlgeschlagen. -# -# Thing status descriptions -# -channelValue.check-integrity-failed = Integrittsprfung fehlgeschlagen. Nachfolgende Szenen werden nicht verwendet: -channelValue.check-integrity-ok = Integrittsprfung bestanden. Alle definierten Szenen werden verwendet. -# -# Note: this entry should overwrite the implicit naming within paperUI slider element -# -channel-type.velux.control.label = Steuerung -channel-type.velux.control.description = Steuerungselement (hoch/runter/stop). -# -# end-of-ESH-INF/i18n/velux_de.properties -# -# -config.velux.thing.window.serial.label = Seriennumber -config.velux.thing.window.serial.description = Acht hexadezimale Ziffern (bspw. 65:23:3E:26:0C:1B:00:10). -config.velux.thing.window.name.label = Name -config.velux.thing.window.name.description = (Optionaler) Name des Fensters, wie er auf dem Kopplungselements definiert wurde. -config.velux.thing.window.inverted.label = Positioninvertierung -config.velux.thing.window.inverted.description = Die Fensterposition wird invertiert (bspw. 0% umgesetzt zu 100%). -# -config.velux.thing.vshutter.sceneLevels.label = Liste von Positionen/Szenen -config.velux.thing.vshutter.sceneLevels.description = Definition eines virtuellen Rolladens mittels Festlegung von einer Position durch eine Szene. So sieht der Parameter aus wie: ,,, -config.velux.thing.vshutter.currentLevel.label = Aktuelle Position -config.velux.thing.vshutter.currentLevel.description = Wert zwischen 0 und 100. -# -# Channel types descriptions -# -channel-type.velux.information.label = Binding Information -channel-type.velux.information.description = Beschreibung des aktuellen Binding Status. -channel-type.velux.status.label = Status -channel-type.velux.status.description = Status des Kopplungselements. -channel-type.velux.reload.label = Nachladen -channel-type.velux.reload.description = Konfigurationen neu laden. -channel-type.velux.timestamp.label = Zeitstempel -channel-type.velux.timestamp.description = Zeitpunkt des letzten erfolgreichen Kommunikation -channel-type.velux.doDetection.label = Produkterkennung -channel-type.velux.doDetection.description = Schalter zum Start der Produkterkennung. -channel-type.velux.firmware.label = Firmware -channel-type.velux.firmware.description = Softwareversion des Kopplungselements. -channel-type.velux.ipAddress.label = LAN IP-Adresse -channel-type.velux.ipAddress.description = IP-Adresse des Kopplungselements. -channel-type.velux.subnetMask.label = LAN Subnetzmaske -channel-type.velux.subnetMask.description = IP-Subnetzmaske des Kopplungselements. -channel-type.velux.defaultGW.label = LAN Router-IP-Adresse -channel-type.velux.defaultGW.description = IP-Adresse des Routers. -channel-type.velux.DHCP.label = LAN DHCP-Aktivierung -channel-type.velux.DHCP.description = Aktivierung der automatischen IP-Konfiguration mittels DHCP. -channel-type.velux.WLANSSID.label = WLAN-SSID -channel-type.velux.WLANSSID.description = Netzwerkkennung des Wireless-Netzwerks. -channel-type.velux.WLANPassword.label = WLAN-Passwort -channel-type.velux.WLANPassword.description = Passwort zum Zugriff auf das Wireless-Netzwerk. -channel-type.velux.products.label = Identifizierte Produkte -channel-type.velux.products.description = Produkte welche auf dem Kopplungselement im Rahmen von Szenen definiert sind. -channel-type.velux.scenes.label = Identifizierte Szenen -channel-type.velux.scenes.description = Szenen welche auf dem Kopplungselement definiert sind. -channel-type.velux.check.label = Konfigurationsprfung -channel-type.velux.check.description = Vergleich der Gateway-Konfiguration mit der openHAB-Konfiguration. -# -channel-type.velux.position.label = Position -channel-type.velux.position.description = Positionssteuerungselement (hoch/runter/stop). -channel-type.velux.state.label = Zustand -channel-type.velux.state.description = Steuerung (AN, AUS). -channel-type.velux.action.label = Szenenstart -channel-type.velux.action.description = Schalter zum Start dieser Szene -channel-type.velux.silentMode.label = Stillmodus -channel-type.velux.silentMode.description = Aktiviert den Leise-Modus auf einer definierten Szene. -channel-type.velux.vposition.label = Position -channel-type.velux.vposition.description = Positionssteuerungselement (hoch/runter/stop). -# -# Runtime status descriptions -# -runtime.no-bridge = Bislang ist noch ist kein Kopplungselement definiert. Bitte fgen Sie ein Thing vom Typ "Velux KLF200" hinzu, um eine Verbindung zum Kopplungselement aufzubauen, welches die Voraussetzung fr die weitere Inbetriebnahme darstellt. -runtime.one-bridge = Es ist bereits ein Kopplungselement definiert. Somit knnen Sie nun weitere Gerte mittels Suche (bzw. Discovery) oder durch manuelles Hinzufgen von Things einrichten. -runtime.multiple-bridges = Es sind mehr als ein Kopplungselement definiert. Dies ist natrlich bei mehreren Velux KLF200 Gateways oder bei einem redundanten Aufbau (SLIP ber LAN parallel zu JSON ber WLAN) mglich. Jeder andere Fall sollte vermieden werden. -runtime.bridge-offline-no-valid-bridgeProtocol-selected = Der Parameter "bridgeProtocol" muss korrekt gesetzt werden. -runtime.bridge-offline-login-sequence-failed = Anmeldung am Kopplungselement fehlgeschlagen. -# -# Thing status descriptions -# -channelValue.check-integrity-failed = Integrittsprfung fehlgeschlagen. Nachfolgende Szenen werden nicht verwendet: -channelValue.check-integrity-ok = Integrittsprfung bestanden. Alle definierten Szenen werden verwendet. -# -# Note: this entry should overwrite the implicit naming within paperUI slider element -# -channel-type.velux.control.label = Steuerung -channel-type.velux.control.description = Steuerungselement (hoch/runter/stop). -# -# end-of-ESH-INF/i18n/velux_de.properties -# +config.velux.thing.rollershutter.limitMinimum.label = Minimale Begrenzungsposition +config.velux.thing.rollershutter.limitMinimum.description = Die minimale Begrenzungsposition des Rolladens. +config.velux.thing.rollershutter.limitMaximum.label = Maximale Begrenzungsposition +config.velux.thing.rollershutter.limitMaximum.description = Die maximale Begrenzungsposition des Rolladens. # config.velux.thing.window.serial.label = Seriennumber config.velux.thing.window.serial.description = Acht hexadezimale Ziffern (bspw. 65:23:3E:26:0C:1B:00:10). @@ -253,6 +103,10 @@ config.velux.thing.window.name.label = Name config.velux.thing.window.name.description = (Optionaler) Name des Fensters, wie er auf dem Kopplungselements definiert wurde. config.velux.thing.window.inverted.label = Positioninvertierung config.velux.thing.window.inverted.description = Die Fensterposition wird invertiert (bspw. 0% umgesetzt zu 100%). +config.velux.thing.window.limitMinimum.label = Minimale Begrenzungsposition +config.velux.thing.window.limitMinimum.description = Die minimale Begrenzungsposition des Fensters. +config.velux.thing.window.limitMaximum.label = Maximale Begrenzungsposition +config.velux.thing.window.limitMaximum.description = Die maximale Begrenzungsposition des Fensters. # config.velux.thing.vshutter.sceneLevels.label = Liste von Positionen/Szenen config.velux.thing.vshutter.sceneLevels.description = Definition eines virtuellen Rolladens mittels Festlegung von einer Position durch eine Szene. So sieht der Parameter aus wie: ,,, @@ -267,8 +121,8 @@ channel-type.velux.status.label = Status channel-type.velux.status.description = Status des Kopplungselements. channel-type.velux.reload.label = Nachladen channel-type.velux.reload.description = Konfigurationen neu laden. -channel-type.velux.timestamp.label = Zeitstempel -channel-type.velux.timestamp.description = Zeitpunkt des letzten erfolgreichen Kommunikation +channel-type.velux.downtime.label = Ausfallzeit +channel-type.velux.downtime.description = Zeitintervall zwischen der letzten erfolgreichen und der letzten Gerteinteraktion. channel-type.velux.doDetection.label = Produkterkennung channel-type.velux.doDetection.description = Schalter zum Start der Produkterkennung. channel-type.velux.firmware.label = Firmware @@ -300,6 +154,10 @@ channel-type.velux.action.label = Szenenstart channel-type.velux.action.description = Schalter zum Start dieser Szene channel-type.velux.silentMode.label = Stillmodus channel-type.velux.silentMode.description = Aktiviert den Leise-Modus auf einer definierten Szene. +channel-type.velux.limitMinimum.label = Miniale Begrenzungsposition +channel-type.velux.limitMinimum.description = Untere Begrenzungsposition des Aktuators/Rolladens/Fensters. +channel-type.velux.limitMaximum.label = Maximale Begrenzungsposition +channel-type.velux.limitMaximum.description = Obere Begrenzungsposition des Aktuators/Rolladens/Fensters. channel-type.velux.vposition.label = Position channel-type.velux.vposition.description = Positionssteuerungselement (hoch/runter/stop). # @@ -313,7 +171,7 @@ runtime.bridge-offline-login-sequence-failed = Anmeldung am Kopplungselement feh # # Thing status descriptions # -channelValue.check-integrity-failed = Integrittsprfung fehlgeschlagen. Nachfolgende Szenen werden nicht verwendet: +channelValue.check-integrity-failed = Integrittsprfung fehlgeschlagen. Nachfolgende Szenen werden nicht verwendet: {}. channelValue.check-integrity-ok = Integrittsprfung bestanden. Alle definierten Szenen werden verwendet. # # Note: this entry should overwrite the implicit naming within paperUI slider element diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_dk.properties b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_dk.properties index 7912aac57b188..9a271388e4b81 100644 --- a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_dk.properties +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_dk.properties @@ -39,6 +39,10 @@ thing-type.velux.vshutter.description = Et sæt scener, der fungerer sammen som thing-type.velux.window.label = Velux-vinduesinformation thing-type.velux.window.description = Styr et vindue på Velux KLF 200 # +# Discovery descriptions +# +discovery.velux.binding...label = Velux Binding Oplysninger Element +# # Konfig-beskrivelser # config.velux.bridge.ipAddress.label = IP-adresse @@ -73,8 +77,10 @@ config.velux.thing.actuator.name.label = Navn config.velux.thing.actuator.name.description = (Valgfrit) Navn på aktuatoren som defineret i gateway. config.velux.thing.actuator.inverted.label = Aktuatorposition Inverteret config.velux.thing.actuator.inverted.description = Aktuatorpositionen er omvendt (dvs. 0% oversættes til 100%). -config.velux.thing.actuator.limitation.label = Begrænsningsposition -config.velux.thing.actuator.limitation.description = Aktuatorens begrænsningsposition. +config.velux.thing.actuator.limitMinimum.label = Min. Begrænsningsposition +config.velux.thing.actuator.limitMinimum.description = Min. Aktuatorens begrænsningsposition. +config.velux.thing.actuator.limitMaximum.label = Max. Begrænsningsposition +config.velux.thing.actuator.limitMaximum.description = Max. Aktuatorens begrænsningsposition. # config.velux.thing.rollershutter.serial.label = Serienummer config.velux.thing.rollershutter.serial.description = Otte hex-cifre (dvs. 65:23:3E:26:0C:1B:00:10). @@ -82,8 +88,10 @@ config.velux.thing.rollershutter.name.label = Navn config.velux.thing.rollershutter.name.description = (Valgfrit) Navn på rulleskodder som defineret i gateway. config.velux.thing.rollershutter.inverted.label = Rulleskodderposition Inverteret config.velux.thing.rollershutter.inverted.description = Rulleskodderposition er omvendt (dvs. 0% oversættes til 100%). -config.velux.thing.rollershutter.limitation.label = Begrænsningsposition -config.velux.thing.rollershutter.limitation.description = Rulleskodders begrænsningsposition. +config.velux.thing.rollershutter.limitMinimum.label = Min. Begrænsningsposition +config.velux.thing.rollershutter.limitMinimum.description = Min. Rulleskodders begrænsningsposition. +config.velux.thing.rollershutter.limitMaximum.label = Max. Begrænsningsposition +config.velux.thing.rollershutter.limitMaximum.description = Max. Rulleskodders begrænsningsposition. # config.velux.thing.window.serial.label = Serienummer config.velux.thing.window.serial.description = Otte hexcifre (dvs. 65:23:3E:26:0C:1B:00:10). @@ -91,8 +99,10 @@ config.velux.thing.window.name.label = Navn config.velux.thing.window.name.description = (Valgfrit) Navn på vindue som defineret i gatewayen. config.velux.thing.window.inverted.label = Vindueposition Inverteret config.velux.thing.window.inverted.description = Vinduepositionen er omvendt (dvs. 0% oversættes til 100%). -config.velux.thing.window.limitation.label = Begrænsningsposition -config.velux.thing.window.limitation.description = Vindues begrænsningsposition. +config.velux.thing.window.limitMinimum.label = Min. Begrænsningsposition +config.velux.thing.window.limitMinimum.description = Min. Vindues begrænsningsposition. +config.velux.thing.window.limitMaximum.label = Max. Begrænsningsposition +config.velux.thing.window.limitMaximum.description = Max. Vindues begrænsningsposition. # config.velux.thing.vshutter.sceneLevels.label = Liste over positioner / scener config.velux.thing.vshutter.sceneLevels.description = Definition af en virtuel lukker ved at erklære en scene, der svarer til en position. Derfor ser denne parameter ud: , , , @@ -105,8 +115,8 @@ channel-type.velux.status.label = Bridge State channel-type.velux.status.description = Beskrivelse af den aktuelle Bridge State. channel-type.velux.reload.label = Genindlæs channel-type.velux.reload.description = Opdater broinformation. -channel-type.velux.timestamp.label = Tidstempel -channel-type.velux.timestamp.description = Tidsstempel, når sidst vellykkede enhedsinteraktion skete (sekunder siden epoke). +channel-type.velux.downtime.label = Nedetid +channel-type.velux.downtime.description = Tidsinterval mellem sidst vellykkede og nylige enhedsinteraktion. channel-type.velux.doDetection.label = Aktivér Bridge Detection-tilstand channel-type.velux.doDetection.description = Start af produktdetekteringstilstand. channel-type.velux.firmware.label = Firmwareversion @@ -138,8 +148,10 @@ channel-type.velux.action.label = Start af en scene channel-type.velux.action.description = Aktiverer et sæt foruddefinerede produktindstillinger. channel-type.velux.silentMode.label = Aktiverer lydtilstand channel-type.velux.silentMode.description = Aktiverer lydløs tilstand for de foruddefinerede produktindstillinger. -channel-type.velux.limitation.label = Begrænsningsposition -channel-type.velux.limitation.description = Aktuatorens/Rulleskodders/Vindues begrænsningsposition. +channel-type.velux.limitMinimum.label = Min. Begrænsningsposition +channel-type.velux.limitMinimum.description = Min. Aktuatorens/Rulleskodders/Vindues begrænsningsposition. +channel-type.velux.limitMaximum.label = Max. Begrænsningsposition +channel-type.velux.limitMaximum.description = Max. Begrænsningsposition channel-type.velux.vposition.label = Position channel-type.velux.vposition.description = Enhedskontrol (OP, NED, STOP, lukning 0-100%). # @@ -153,7 +165,7 @@ runtime.bridge-offline-login -sequence-failed = Login-sekvens mislykkedes. # # Statusbeskrivelser # -channelValue.check-integrity-failed = Integritetskontrol mislykkedes: Følgende scener er ubrugte: +channelValue.check-integrity-failed = Integritetskontrol mislykkedes: Følgende scener er ubrugte: {}. channelValue.check-integrity-ok = Integrity check ok. Alle scener bruges i artikler. # # end-of-ESH-INF/i18n/velux.properties diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_nl.properties b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_nl.properties index 91cb6854f1661..9019db1f65464 100644 --- a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_nl.properties +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_nl.properties @@ -39,6 +39,10 @@ thing-type.velux.vshutter.description = Een set scènes die samenwerken als slui thing-type.velux.window.label = Velux-vensterinformatie thing-type.velux.window.description = Bedien een venster op de Velux KLF 200 # +# Discovery descriptions +# +discovery.velux.binding...label = Velux Binding Informatie-Element +# # Config beschrijvingen # config.velux.bridge.ipAddress.label = IP-adres @@ -73,8 +77,10 @@ config.velux.thing.actuator.name.label = Naam config.velux.thing.actuator.name.description = (Optioneel) Naam van de actuator zoals gedefinieerd in de gateway. config.velux.thing.actuator.inverted.label = Actuatorpositie omgekeerd config.velux.thing.actuator.inverted.description = De actuatorpositie is omgekeerd (d.w.z. 0% vertaalt zich naar 100%). -config.velux.thing.actuator.limitation.label = Beperkingspositie -config.velux.thing.actuator.limitation.description = De beperkingspositie van de actuator. +config.velux.thing.actuator.limitMinimum.label = Min. Beperkingspositie +config.velux.thing.actuator.limitMinimum.description = Min. De beperkingspositie van de actuator. +config.velux.thing.actuator.limitMaximum.label = Max. Beperkingspositie +config.velux.thing.actuator.limitMaximum.description = Max. De beperkingspositie van de actuator. # config.velux.thing.rollershutter.serial.label = Serienummer config.velux.thing.rollershutter.serial.description = Acht hexadecimale cijfers (d.w.z. 65:23:3E:26:0C:1B:00:10). @@ -82,8 +88,10 @@ config.velux.thing.rollershutter.name.label = Naam config.velux.thing.rollershutter.name.description = (Optioneel) Naam van de rolluik zoals gedefinieerd in de gateway. config.velux.thing.rollershutter.inverted.label = Rolluikpositie omgekeerd config.velux.thing.rollershutter.inverted.description = De Rolluikpositie is omgekeerd (d.w.z. 0% vertaalt zich naar 100%). -config.velux.thing.rollershutter.limitation.label = Beperkingspositie -config.velux.thing.rollershutter.limitation.description = De beperkingspositie van de rolluik. +config.velux.thing.rollershutter.limitMinimum.label = Min. Beperkingspositie +config.velux.thing.rollershutter.limitMinimum.description = Min. De beperkingspositie van de rolluik. +config.velux.thing.rollershutter.limitMaximum.label = Max. Beperkingspositie +config.velux.thing.rollershutter.limitMaximum.description = Max. De beperkingspositie van de rolluik. # config.velux.thing.window.serial.label = Serienummer config.velux.thing.window.serial.description = Acht hexadecimale cijfers (d.w.z. 65:23:3E:26:0C:1B:00:10). @@ -91,8 +99,10 @@ config.velux.thing.window.name.label = Naam config.velux.thing.window.name.description = (Optioneel) Naam van de venster zoals gedefinieerd in de gateway. config.velux.thing.window.inverted.label = Vensterpositie omgekeerd config.velux.thing.window.inverted.description = De Vensterpositie is omgekeerd (d.w.z. 0% vertaalt zich naar 100%). -config.velux.thing.window.limitation.label = Beperkingspositie -config.velux.thing.window.limitation.description = De beperkingspositie van de venster. +config.velux.thing.window.limitMinimum.label = Min. Beperkingspositie +config.velux.thing.window.limitMinimum.description = Min. De beperkingspositie van de venster. +config.velux.thing.window.limitMaximum.label = Max. Beperkingspositie +config.velux.thing.window.limitMaximum.description = Max. De beperkingspositie van de venster. # config.velux.thing.vshutter.sceneLevels.label = Lijst met posities / scènes config.velux.thing.vshutter.sceneLevels.description = Definitie van een virtuele sluiter door een scène aan te geven die overeenkomt met een positie. Daarom ziet deze parameter er als volgt uit: , , , @@ -105,8 +115,8 @@ channel-type.velux.status.label = Bridge State channel-type.velux.status.description = Beschrijving van huidige brugstatus. channel-type.velux.reload.label = Herladen channel-type.velux.reload.description = Bruginformatie opnieuw laden. -channel-type.velux.timestamp.label = Tijdstempel -channel-type.velux.timestamp.description = Tijdstempel wanneer de laatste succesvolle apparaatinteractie plaatsvond (seconden sinds het tijdperk). +channel-type.velux.downtime.label = Downtime. +channel-type.velux.downtime.description = tussen laatste succesvolle en recente apparaatinteractie. channel-type.velux.doDetection.label = Activeer de brugdetectiemodus channel-type.velux.doDetection.description = Start van de productdetectiemodus. channel-type.velux.firmware.label = Firmwareversie @@ -138,8 +148,10 @@ channel-type.velux.action.label = Start van een scène channel-type.velux.action.description = Activeert een set vooraf gedefinieerde productinstellingen. channel-type.velux.silentMode.label = Stille modus inschakelen channel-type.velux.silentMode.description = Activeert de stille modus van de vooraf gedefinieerde productinstellingen. -channel-type.velux.limitation.label = Beperkingspositie -channel-type.velux.limitation.description = De beperkingspositie van de actuator/rolluik/venster. +channel-type.velux.limitMinimum.label = Min. Beperkingspositie +channel-type.velux.limitMinimum.description = Min. de beperkingspositie van de actuator/rolluik/venster. +channel-type.velux.limitMaximum.label = Max. Beperkingspositie +channel-type.velux.limitMaximum.description = Max. de De beperkingspositie van de actuator/rolluik/venster channel-type.velux.vposition.label = Positie channel-type.velux.vposition.description = Apparaatbeheer (OMHOOG, OMLAAG, STOP, sluiting 0-100%). # @@ -153,7 +165,7 @@ runtime.bridge-offline-login-sequence-failure = Aanmeldingsreeks mislukt. # # Ding statusbeschrijvingen # -channelValue.check-integrity-failure = Integriteitscontrole mislukt: de volgende scènes worden niet gebruikt: +channelValue.check-integrity-failed = Integriteitscontrole mislukt: de volgende scènes worden niet gebruikt: {}. channelValue.check-integrity-ok = Integriteitscontrole ok. Alle scènes worden gebruikt binnen Items. # # end-of-ESH-INF / i18n / velux_nl.properties diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/actuator.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/actuator.xml index cf33c07a5c9c7..129311b2616b3 100644 --- a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/actuator.xml +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/actuator.xml @@ -3,7 +3,7 @@ + xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd"> @@ -13,17 +13,14 @@ @text/thing-type.velux.actuator.description - Rollershutter + Blinds - - + + serialNumber - - - diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/binding.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/binding.xml index 45df7f30d2f96..6089360d02210 100644 --- a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/binding.xml +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/binding.xml @@ -3,14 +3,14 @@ + xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd"> @text/thing-type.velux.binding.description - Other + NetworkAppliance @@ -19,6 +19,3 @@ - - - diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/bridge.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/bridge.xml index c696e9159774f..8ae902c5dabbd 100644 --- a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/bridge.xml +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/bridge.xml @@ -3,7 +3,7 @@ + xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd"> @@ -13,7 +13,7 @@ - + @@ -37,7 +37,3 @@ - - - - diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/channels.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/channels.xml index 6f65da79f4026..49d9abd37efeb 100644 --- a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/channels.xml +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/channels.xml @@ -3,7 +3,7 @@ + xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd"> @@ -11,7 +11,7 @@ String @text/channel-type.velux.information.description - Binding + NetworkAppliance @@ -22,22 +22,22 @@ String @text/channel-type.velux.status.description - Bridge + NetworkAppliance - + Switch @text/channel-type.velux.reload.description - Bridge + NetworkAppliance - + Number - - @text/channel-type.velux.timestamp.description - Bridge + + @text/channel-type.velux.downtime.description + NetworkAppliance @@ -45,14 +45,14 @@ Switch @text/channel-type.velux.doDetection.description - Bridge + NetworkAppliance String @text/channel-type.velux.firmware.description - Bridge + NetworkAppliance @@ -60,7 +60,7 @@ String @text/channel-type.velux.ipAddress.description - Bridge + NetworkAppliance @@ -68,7 +68,7 @@ String @text/channel-type.velux.subnetMask.description - Bridge + NetworkAppliance @@ -76,7 +76,7 @@ String @text/channel-type.velux.defaultGW.description - Bridge + NetworkAppliance @@ -84,7 +84,7 @@ Switch @text/channel-type.velux.DHCP.description - Bridge + NetworkAppliance @@ -92,7 +92,7 @@ String @text/channel-type.velux.WLANSSID.description - Bridge + NetworkAppliance @@ -100,7 +100,7 @@ String @text/channel-type.velux.WLANPassword.description - Bridge + NetworkAppliance @@ -108,7 +108,7 @@ String @text/channel-type.velux.products.description - Bridge + NetworkAppliance @@ -116,7 +116,7 @@ String @text/channel-type.velux.scenes.description - Bridge + NetworkAppliance @@ -124,7 +124,7 @@ String @text/channel-type.velux.check.description - Bridge + NetworkAppliance @@ -134,48 +134,49 @@ Rollershutter @text/channel-type.velux.position.description - Rollershutter + Blinds Switch @text/channel-type.velux.state.description - Switch + WallSwitch - + Rollershutter - - @text/channel-type.velux.limitation.description - Rollershutter + + @text/channel-type.velux.limitMinimum.description + Blinds + + Rollershutter + + @text/channel-type.velux.limitMaximum.description + Blinds + Switch @text/channel-type.velux.action.description - Scene + WallSwitch Switch @text/channel-type.velux.silentMode.description - Scene + Blinds Rollershutter @text/channel-type.velux.vposition.description - Rollershutter + Blinds - - - - - diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/rollershutter.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/rollershutter.xml index 13f678d74de49..c4d59c0aeb348 100644 --- a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/rollershutter.xml +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/rollershutter.xml @@ -3,7 +3,8 @@ + xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd"> + @@ -13,16 +14,13 @@ @text/thing-type.velux.rollershutter.description - Rollershutter + Blinds - - + + unique - - - diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/scene.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/scene.xml index d6dfad74ac383..1bce1894caadc 100644 --- a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/scene.xml +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/scene.xml @@ -3,7 +3,8 @@ + xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd"> + @@ -13,16 +14,12 @@ @text/thing-type.velux.scene.description - Scene + Blinds - unique - - - diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/vshutter.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/vshutter.xml index 80ded429820cf..7e77495b67fd3 100644 --- a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/vshutter.xml +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/vshutter.xml @@ -3,7 +3,8 @@ + xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd"> + @@ -13,13 +14,10 @@ @text/thing-type.velux.vshutter.description - Rollershutter + Blinds - - - diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/window.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/window.xml index 4b67180f61dd8..7a04b12aa877b 100644 --- a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/window.xml +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/window.xml @@ -3,7 +3,8 @@ + xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd"> + @@ -16,13 +17,10 @@ Window - - + + serialNumber - - - From e74b2473d7349df917bb68816e39ee3e69254ac3 Mon Sep 17 00:00:00 2001 From: Guenther Schreiner Date: Tue, 31 Dec 2019 11:38:43 +0100 Subject: [PATCH 3/9] [velux] Adapted bundles/pom.xml to original settings. [velux] Enhanced README.md according to community feedback.. [velux] Minor logging adaptions. Improved inline comments. Signed-off-by: Guenther Schreiner --- bundles/org.openhab.binding.velux/README.md | 7 +- .../binding/velux/internal/VeluxBinding.java | 8 +- .../internal/handler/VeluxBridgeHandler.java | 95 +++++++++++-------- bundles/pom.xml | 4 +- 4 files changed, 65 insertions(+), 49 deletions(-) diff --git a/bundles/org.openhab.binding.velux/README.md b/bundles/org.openhab.binding.velux/README.md index 8d4767a2529cb..aa6b758d3ce05 100644 --- a/bundles/org.openhab.binding.velux/README.md +++ b/bundles/org.openhab.binding.velux/README.md @@ -161,8 +161,7 @@ Optionally the subtype is enhanced with parameters like the appropriate name of Notes: (\*\*\*) The existence of this item triggers the continuous realtime status updates of any Velux item like shutters even if they are manually controlled by other controllers. -(\*\*\*\*) To enable a complete invertion of all parameter values (i.e. for Velux windows), add a trailing star to the eight-byte serial number. For an example, -see below at item `Velux DG Window Bathroom`. +(\*\*\*\*) To enable a complete invertion of all parameter values (i.e. for Velux windows), use the property `inverted` or add a trailing star to the eight-byte serial number. For an example, see below at item `Velux DG Window Bathroom`. (\*\*\*\*\*) Somfy devices does not provides a valid serial number to the Velux KLF200 gateway: The bridge reports a registration of the serial number 00:00:00:00:00:00:00:00. Therefore the binding implements a fallback to allow an item specification with a actuator name instead of actuator serial number whenever such an invalid serial number occurs. For an example, see below at item `Velux OG Somfy Shutter`. @@ -181,6 +180,10 @@ The subtype shutter requires an even pair of parameters, each defining the shutt { velux="thing=brigde;channel=shutter#,,," } ``` +### Rain Sensor + +Unfortunately Velux has decided to closely integrate the rain sensor into the window device. The rain sensor is therefore not displayed in the device list. On the other hand, the 'limitMinimum' channel of a roof window now provides information about rainy weather: if it is set internally by the Velux control unit to a value other than zero, it rains. + ### Virtual shutter As the bridge with firmware version one does not support a real rollershutter interaction, this binding provides a virtual rollershutter consisting of different scenes which set a specific shutter level. Therefore the item definition contains multiple pairs of rollershutter levels each followed by a scene name, which leads to this setting. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java index d09e4b229a024..cb59dd65b17e5 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java @@ -54,9 +54,11 @@ public class VeluxBinding extends VeluxBridgeConfiguration { */ public VeluxBinding(@Nullable VeluxBridgeConfiguration uncheckedConfiguration) { logger.trace("VeluxBinding(constructor) called."); - for (Field field : VeluxBridgeConfiguration.class.getFields()) { - if (!StringUtils.capitalize(field.getName()).contentEquals(field.getName())) { - logger.trace("VeluxBinding(): FYI: a potential configuration string is '{}'.", field.getName()); + if (logger.isTraceEnabled()) { + for (Field field : VeluxBridgeConfiguration.class.getFields()) { + if (!StringUtils.capitalize(field.getName()).contentEquals(field.getName())) { + logger.trace("VeluxBinding(): FYI: a potential configuration string is '{}'.", field.getName()); + } } } if (uncheckedConfiguration == null) { diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBridgeHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBridgeHandler.java index 0f82c1de91000..c4405b4e0c743 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBridgeHandler.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBridgeHandler.java @@ -240,35 +240,10 @@ public boolean bridgeCommunicate(BridgeCommunicationProtocol communication) { return null; } - /*** - *** Continuous methods - ***/ - - private synchronized void execute() { - logger.debug("execute() initiated by {} starting cycle {}.", Thread.currentThread(), refreshCounter); - - logger.trace("execute(): processing of possible HSM messages."); - // Background execution of bridge related I/O - handleScheduler.execute(() -> { - logger.trace("handleCommand.scheduler.execute() initiated by {} will process HouseStatus.", - Thread.currentThread()); - if (new VeluxBridgeGetHouseStatus().evaluateState(thisBridge)) { - logger.trace("execute(): successfully processed of GetHouseStatus()"); - } - logger.trace("handleCommand.scheduler.execute() initiated by {} has finished.", Thread.currentThread()); - }); - - logger.trace( - "execute(): looping through all (both child things and bridge) linked channels for a need of refresh."); - for (ChannelUID channelUID : BridgeChannels.getAllLinkedChannelUIDs(this)) { - if (VeluxItemType.isToBeRefreshedNow(refreshCounter, thingTypeUIDOf(channelUID), channelUID.getId())) { - logger.trace("execute(): refreshing item {}.", channelUID); - handleCommand(channelUID, RefreshType.REFRESH); - } - } - logger.debug("execute() initiated by {} finished cycle {}.", Thread.currentThread(), refreshCounter); - refreshCounter++; - } + /* + * ************************************************** + * ******** Provisioning/Deprovisioning methods ***** + */ @Override public void initialize() { @@ -292,7 +267,7 @@ public void initialize() { this.veluxBridgeConfiguration.refreshMSecs); refreshJob = scheduler.scheduleWithFixedDelay(() -> { try { - execute(); + refreshOpenHAB(); } catch (RuntimeException e) { logger.warn("Exception occurred during activated refresh scheduler: {}.", e.getMessage()); } @@ -308,10 +283,10 @@ public void initialize() { */ @Override public synchronized void dispose() { - logger.trace("dispose({}) called.", this); + logger.debug("dispose({}) called.", this); logger.trace("dispose(): shutting down scheduled bridge I/O tasks."); handleScheduler.shutdownNow(); - logger.debug("Shutting down Velux veluxBridge '{}'.", getThing().getUID()); + logger.trace("Shutting down Velux veluxBridge '{}'.", getThing().getUID()); // Just for avoidance of Potential null pointer access ScheduledFuture refreshJobX = refreshJob; if (refreshJobX != null) { @@ -366,9 +341,11 @@ public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) { super.childHandlerDisposed(childHandler, childThing); } - /*** - *** Reconfiguration methods - ***/ + /* + * ************************************** + * ******** Reconfiguration methods ***** + */ + private void bridgeParamsUpdated() { logger.debug("bridgeParamsUpdated() called."); @@ -434,6 +411,37 @@ private void bridgeParamsUpdated() { logger.trace("bridgeParamsUpdated() successfully finished."); } + /* + * ************************************************* + * ******** Continuous synchronization methods ***** + */ + + private synchronized void refreshOpenHAB() { + logger.debug("refreshOpenHAB() initiated by {} starting cycle {}.", Thread.currentThread(), refreshCounter); + + logger.trace("refreshOpenHAB(): processing of possible HSM messages."); + // Background execution of bridge related I/O + handleScheduler.execute(() -> { + logger.trace("refreshOpenHAB.scheduled() initiated by {} will process HouseStatus.", + Thread.currentThread()); + if (new VeluxBridgeGetHouseStatus().evaluateState(thisBridge)) { + logger.trace("refreshOpenHAB.scheduled(): successfully processed of GetHouseStatus()"); + } + logger.trace("refreshOpenHAB.scheduled() initiated by {} has finished.", Thread.currentThread()); + }); + + logger.trace( + "refreshOpenHAB(): looping through all (both child things and bridge) linked channels for a need of refresh."); + for (ChannelUID channelUID : BridgeChannels.getAllLinkedChannelUIDs(this)) { + if (VeluxItemType.isToBeRefreshedNow(refreshCounter, thingTypeUIDOf(channelUID), channelUID.getId())) { + logger.trace("refreshOpenHAB(): refreshing item {}.", channelUID); + handleCommand(channelUID, RefreshType.REFRESH); + } + } + logger.debug("refreshOpenHAB() initiated by {} finished cycle {}.", Thread.currentThread(), refreshCounter); + refreshCounter++; + } + /** * In case of recognized changes in the real world, the method will * update the corresponding states via openHAB event bus. @@ -500,6 +508,11 @@ private void syncChannelsWithProducts() { logger.trace("syncChannelsWithProducts() done."); } + /* + * ******************************************* + * ******** Processing of openHAB events ***** + */ + @Override public void handleCommand(ChannelUID channelUID, Command command) { logger.trace("handleCommand({}): execution of command {} on channel {} will be scheduled.", @@ -508,10 +521,10 @@ public void handleCommand(ChannelUID channelUID, Command command) { // Background execution of bridge related I/O handleScheduler.execute(() -> { - logger.trace("handleCommand.scheduler.execute({}) Start work with calling handleCommandScheduled().", + logger.trace("handleCommand.scheduled({}) Start work with calling handleCommandScheduled().", Thread.currentThread()); handleCommandScheduled(channelUID, command); - logger.trace("handleCommand.scheduler.execute({}) done.", Thread.currentThread()); + logger.trace("handleCommand.scheduled({}) done.", Thread.currentThread()); }); logger.trace("handleCommand({}) done.", Thread.currentThread()); } @@ -523,8 +536,8 @@ public void handleCommand(ChannelUID channelUID, Command command) { * NOTE: This method is to be called as separated thread to ensure proper openHAB framework in parallel. *

      * - * @param channelUID the {@link ChannelUID} of the channel to which the command was sent - * @param command the {@link Command} + * @param channelUID the {@link ChannelUID} of the channel to which the command was sent, + * @param command the {@link Command}. */ private synchronized void handleCommandScheduled(ChannelUID channelUID, Command command) { logger.trace("handleCommandScheduled({}): execution of command {} on channel {}.", Thread.currentThread(), @@ -536,8 +549,7 @@ private synchronized void handleCommandScheduled(ChannelUID channelUID, Command * Common part */ - // if (veluxBridgeConfiguration.isProtocolTraceEnabled) { - if (true) { + if (veluxBridgeConfiguration.isProtocolTraceEnabled) { Threads.findDeadlocked(); } @@ -697,7 +709,6 @@ private synchronized void handleCommandScheduled(ChannelUID channelUID, Command if (newValue != null) { postCommand(channelUID, newValue); } - } ThingProperty.setValue(this, VeluxBindingConstants.PROPERTY_BRIDGE_TIMESTAMP_ATTEMPT, new java.util.Date(thisBridge.lastCommunication()).toString()); diff --git a/bundles/pom.xml b/bundles/pom.xml index 42c81b6eef9c7..3768a5c06a986 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -1,5 +1,5 @@ - - + + 4.0.0 From 50a828829c81759bdb86386715ec10afa990fcf1 Mon Sep 17 00:00:00 2001 From: Guenther Schreiner Date: Sun, 2 Feb 2020 13:15:41 +0100 Subject: [PATCH 4/9] [velux] Moved some information from channels into properties. Signed-off-by: Guenther Schreiner --- bundles/org.openhab.binding.velux/README.md | 63 ++- .../doc/conf/items/velux.items | 2 +- .../doc/conf/rules/velux.rules | 2 +- .../doc/conf/sitemaps/velux.sitemap | 2 +- .../doc/conf/things/velux-001.things | 30 ++ .../doc/conf/things/velux-bridge.things | 30 ++ .../{velux.things => velux.things.disabled} | 21 +- bundles/org.openhab.binding.velux/pom.xml | 10 +- .../src/main/feature/feature.xml | 4 +- .../binding/velux/internal/VeluxBinding.java | 2 +- .../velux/internal/VeluxBindingConfig.java | 2 +- .../velux/internal/VeluxBindingConstants.java | 2 +- .../internal/VeluxBindingProperties.java | 9 +- .../binding/velux/internal/VeluxItemType.java | 170 ++++--- .../velux/internal/VeluxRSBindingConfig.java | 2 +- .../velux/internal/bridge/VeluxBridge.java | 2 +- .../internal/bridge/VeluxBridgeActuators.java | 2 +- .../bridge/VeluxBridgeDetectProducts.java | 2 +- .../VeluxBridgeDeviceCheckLostNodes.java | 2 +- .../bridge/VeluxBridgeDeviceStatus.java | 2 +- .../bridge/VeluxBridgeGetFirmware.java | 2 +- .../bridge/VeluxBridgeGetHouseStatus.java | 2 +- .../bridge/VeluxBridgeGetLimitation.java | 2 +- .../internal/bridge/VeluxBridgeInstance.java | 2 +- .../internal/bridge/VeluxBridgeLANConfig.java | 2 +- .../internal/bridge/VeluxBridgeProvider.java | 2 +- .../bridge/VeluxBridgeRunProductCommand.java | 2 +- .../internal/bridge/VeluxBridgeRunScene.java | 2 +- .../internal/bridge/VeluxBridgeScenes.java | 2 +- .../VeluxBridgeSetHouseStatusMonitor.java | 2 +- .../bridge/VeluxBridgeSetLimitation.java | 11 +- .../bridge/VeluxBridgeSetSceneVelocity.java | 2 +- .../bridge/VeluxBridgeWLANConfig.java | 2 +- .../internal/bridge/common/BridgeAPI.java | 2 +- .../common/BridgeCommunicationProtocol.java | 2 +- .../bridge/common/GetDeviceStatus.java | 2 +- .../internal/bridge/common/GetFirmware.java | 2 +- .../bridge/common/GetHouseStatus.java | 2 +- .../internal/bridge/common/GetLANConfig.java | 2 +- .../internal/bridge/common/GetProduct.java | 2 +- .../bridge/common/GetProductLimitation.java | 2 +- .../internal/bridge/common/GetProducts.java | 2 +- .../internal/bridge/common/GetScenes.java | 2 +- .../internal/bridge/common/GetWLANConfig.java | 2 +- .../velux/internal/bridge/common/Login.java | 2 +- .../velux/internal/bridge/common/Logout.java | 2 +- .../bridge/common/RunProductCommand.java | 2 +- .../bridge/common/RunProductDiscovery.java | 2 +- .../common/RunProductIdentification.java | 2 +- .../bridge/common/RunProductSearch.java | 2 +- .../internal/bridge/common/RunScene.java | 2 +- .../bridge/common/SetHouseStatusMonitor.java | 2 +- .../bridge/common/SetProductLimitation.java | 2 +- .../bridge/common/SetSceneVelocity.java | 2 +- .../internal/bridge/common/package-info.java | 2 +- .../bridge/json/JCgetDeviceStatus.java | 2 +- .../internal/bridge/json/JCgetFirmware.java | 2 +- .../internal/bridge/json/JCgetLANConfig.java | 2 +- .../internal/bridge/json/JCgetProducts.java | 2 +- .../internal/bridge/json/JCgetScenes.java | 2 +- .../internal/bridge/json/JCgetWLANConfig.java | 2 +- .../velux/internal/bridge/json/JClogin.java | 2 +- .../velux/internal/bridge/json/JClogout.java | 2 +- .../bridge/json/JCrunProductDiscovery.java | 2 +- .../json/JCrunProductIdentification.java | 2 +- .../bridge/json/JCrunProductSearch.java | 2 +- .../internal/bridge/json/JCrunScene.java | 2 +- .../bridge/json/JCsetSceneVelocity.java | 2 +- .../internal/bridge/json/JsonBridgeAPI.java | 2 +- .../json/JsonBridgeCommunicationProtocol.java | 2 +- .../internal/bridge/json/JsonVeluxBridge.java | 2 +- .../internal/bridge/json/package-info.java | 2 +- .../velux/internal/bridge/package-info.java | 2 +- .../bridge/slip/SCgetDeviceStatus.java | 2 +- .../internal/bridge/slip/SCgetFirmware.java | 2 +- .../bridge/slip/SCgetHouseStatus.java | 2 +- .../internal/bridge/slip/SCgetLANConfig.java | 2 +- .../internal/bridge/slip/SCgetLimitation.java | 2 +- .../internal/bridge/slip/SCgetProduct.java | 10 +- .../internal/bridge/slip/SCgetProducts.java | 10 +- .../internal/bridge/slip/SCgetScenes.java | 2 +- .../internal/bridge/slip/SCgetWLANConfig.java | 2 +- .../velux/internal/bridge/slip/SClogin.java | 2 +- .../velux/internal/bridge/slip/SClogout.java | 2 +- .../bridge/slip/SCrunProductCommand.java | 2 +- .../bridge/slip/SCrunProductDiscovery.java | 2 +- .../slip/SCrunProductIdentification.java | 2 +- .../bridge/slip/SCrunProductSearch.java | 2 +- .../internal/bridge/slip/SCrunScene.java | 2 +- .../bridge/slip/SCsetHouseStatusMonitor.java | 2 +- .../internal/bridge/slip/SCsetLimitation.java | 2 +- .../bridge/slip/SCsetSceneVelocity.java | 2 +- .../internal/bridge/slip/SlipBridgeAPI.java | 2 +- .../slip/SlipBridgeCommunicationProtocol.java | 2 +- .../internal/bridge/slip/SlipVeluxBridge.java | 2 +- .../internal/bridge/slip/io/Connection.java | 2 +- .../slip/io/DataInputStreamWithTimeout.java | 2 +- .../bridge/slip/io/SSLconnection.java | 8 +- .../internal/bridge/slip/io/package-info.java | 2 +- .../internal/bridge/slip/package-info.java | 2 +- .../bridge/slip/utils/KLF200Response.java | 2 +- .../internal/bridge/slip/utils/Packet.java | 2 +- .../bridge/slip/utils/SlipEncoding.java | 2 +- .../bridge/slip/utils/SlipRFC1055.java | 2 +- .../bridge/slip/utils/package-info.java | 2 +- .../config/VeluxBridgeConfiguration.java | 2 +- .../config/VeluxThingConfiguration.java | 34 +- .../velux/internal/config/package-info.java | 2 +- .../velux/internal/development/Threads.java | 2 +- .../internal/development/package-info.java | 4 +- .../discovery/VeluxDiscoveryService.java | 38 +- .../internal/discovery/package-info.java | 5 +- .../{ => factory}/VeluxHandlerFactory.java | 75 ++- .../velux/internal/factory/package-info.java | 18 + .../internal/handler/BridgeChannels.java | 3 +- .../handler/ChannelActuatorLimitation.java | 42 +- .../handler/ChannelActuatorPosition.java | 121 ++--- .../internal/handler/ChannelBridgeCheck.java | 12 +- .../handler/ChannelBridgeDoDetection.java | 2 +- .../handler/ChannelBridgeFirmware.java | 2 +- .../handler/ChannelBridgeLANconfig.java | 12 +- .../handler/ChannelBridgeProducts.java | 3 +- .../internal/handler/ChannelBridgeScenes.java | 3 +- .../internal/handler/ChannelBridgeStatus.java | 2 +- .../handler/ChannelBridgeWLANconfig.java | 8 +- .../handler/ChannelHandlerTemplate.java | 2 +- .../internal/handler/ChannelSceneAction.java | 9 +- .../handler/ChannelSceneSilentmode.java | 7 +- .../handler/ChannelVShutterPosition.java | 18 +- .../handler/PropertyHandlerTemplate.java | 50 ++ .../velux/internal/handler/ThingProperty.java | 162 ------- .../internal/handler/VeluxBindingHandler.java | 20 +- .../internal/handler/VeluxBridgeHandler.java | 441 +++++++++--------- .../velux/internal/handler/VeluxHandler.java | 11 +- .../velux/internal/handler/package-info.java | 5 +- .../ExtendedBaseBridgeHandler.java | 8 +- .../{ => utils}/ExtendedBaseThingHandler.java | 8 +- .../handler/{ => utils}/StateUtils.java | 8 +- .../handler/utils/Thing2VeluxActuator.java | 133 ++++++ .../handler/utils/ThingConfiguration.java | 105 +++++ .../internal/handler/utils/ThingProperty.java | 104 +++++ .../internal/handler/utils/package-info.java | 18 + .../binding/velux/internal/package-info.java | 2 +- .../things/VeluxExistingProducts.java | 2 +- .../internal/things/VeluxExistingScenes.java | 2 +- .../internal/things/VeluxGwFirmware.java | 2 +- .../velux/internal/things/VeluxGwLAN.java | 2 +- .../velux/internal/things/VeluxGwState.java | 2 +- .../velux/internal/things/VeluxGwWLAN.java | 2 +- .../velux/internal/things/VeluxKLFAPI.java | 2 +- .../velux/internal/things/VeluxProduct.java | 9 +- .../internal/things/VeluxProductName.java | 2 +- .../internal/things/VeluxProductPosition.java | 12 +- .../things/VeluxProductReference.java | 2 +- .../internal/things/VeluxProductSerialNo.java | 2 +- .../internal/things/VeluxProductState.java | 2 +- .../internal/things/VeluxProductType.java | 2 +- .../internal/things/VeluxProductVelocity.java | 2 +- .../velux/internal/things/VeluxScene.java | 2 +- .../velux/internal/things/package-info.java | 4 +- .../velux/internal/utils/Localization.java | 2 +- .../internal/utils/ManifestInformation.java | 2 +- .../velux/internal/utils/package-info.java | 4 +- .../openhab/binding/velux/package-info.java | 5 +- .../main/resources/ESH-INF/config/config.xml | 6 +- .../resources/ESH-INF/i18n/velux.properties | 2 +- .../ESH-INF/i18n/velux_de.properties | 2 +- .../ESH-INF/i18n/velux_dk.properties | 2 +- .../ESH-INF/i18n/velux_nl.properties | 2 +- .../main/resources/ESH-INF/thing/bridge.xml | 23 +- .../main/resources/ESH-INF/thing/channels.xml | 2 + 171 files changed, 1289 insertions(+), 879 deletions(-) create mode 100644 bundles/org.openhab.binding.velux/doc/conf/things/velux-001.things create mode 100644 bundles/org.openhab.binding.velux/doc/conf/things/velux-bridge.things rename bundles/org.openhab.binding.velux/doc/conf/things/{velux.things => velux.things.disabled} (54%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/{ => factory}/VeluxHandlerFactory.java (68%) create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/factory/package-info.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/PropertyHandlerTemplate.java delete mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ThingProperty.java rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/{ => utils}/ExtendedBaseBridgeHandler.java (89%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/{ => utils}/ExtendedBaseThingHandler.java (88%) rename bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/{ => utils}/StateUtils.java (92%) create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/Thing2VeluxActuator.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ThingConfiguration.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ThingProperty.java create mode 100644 bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/package-info.java diff --git a/bundles/org.openhab.binding.velux/README.md b/bundles/org.openhab.binding.velux/README.md index aa6b758d3ce05..35b7ed8978576 100644 --- a/bundles/org.openhab.binding.velux/README.md +++ b/bundles/org.openhab.binding.velux/README.md @@ -33,6 +33,12 @@ Items marked with (\*) are fully implemented. Items marked with (+) have only pa ## Binding Configuration +To simplify the initial provisioning, the binding provides one thing which can be found by autodiscovery: +This thing named Velux Binding Information establishes one channel named information describing +the current binding state in a (hopefully) human understandable fashion. Additionally it will give three +properties with the version number of the bundle, the number of registered bridges and number of overall +things attached to the bridge(s). + The Velux KLF200 bridge has to be configured with some parameters, at least with the IP address of the bridge. | Property | Default | Required | Description | @@ -292,16 +298,6 @@ On the other hand, if you prefer a textual configuration, you can append the log During startup of normal operations, there should be only some few messages within the logfile, like: ``` -[INFO ] [i.dashboard.internal.DashboardService] - Started Dashboard at http://192.168.45.150:8080 -[INFO ] [i.dashboard.internal.DashboardService] - Started Dashboard at https://192.168.45.150:8443 -[INFO ] [rthome.model.lsp.internal.ModelServer] - Started Language Server Protocol (LSP) service on port 5007 -[INFO ] [e.core.internal.i18n.I18nProviderImpl] - Location set to '49.035932145739444,8.455450258310762'. -[INFO ] [smarthome.event.ExtensionEvent ] - Extension 'package-expert' has been installed. -[INFO ] [marthome.ui.paper.internal.PaperUIApp] - Started Paper UI at /paperui -[INFO ] [.ui.habmin.internal.servlet.HABminApp] - Started HABmin servlet at /habmin -[INFO ] [ding.velux.handler.VeluxBridgeHandler] - Initializing Velux veluxBridge handler for 'velux:klf200:7dff2548'. -[INFO ] [ome.event.ThingStatusInfoChangedEvent] - 'velux:klf200:7dff2548' changed from UNINITIALIZED to INITIALIZING -[INFO ] [ome.event.ThingStatusInfoChangedEvent] - 'velux:klf200:7dff2548' changed from INITIALIZING to UNKNOWN [INFO ] [nal.VeluxValidatedBridgeConfiguration] - veluxConfig[protocol=slip,ipAddress=192.168.45.9,tcpPort=51200,password=********,timeoutMsecs=1000,retries=5,refreshMsecs=15000,isBulkRetrievalEnabled=true] [INFO ] [ng.velux.bridge.slip.io.SSLconnection] - Starting velux bridge connection. [INFO ] [hab.binding.velux.bridge.slip.SClogin] - velux bridge connection successfully established (login succeeded). @@ -322,21 +318,44 @@ During startup of normal operations, there should be only some few messages with Product "M_Window" / WINDOW_OPENER (bridgeIndex=0,serial=43:12:3E:26:0C:1B:00:10,position=C800) Product "W-Rollershutter" / ROLLER_SHUTTER (bridgeIndex=1,serial=43:12:40:5A:0C:2A:05:64,position=0000) . [INFO ] [ding.velux.handler.VeluxBridgeHandler] - velux Bridge is online with 10 scenes and 4 actuators, now. -[INFO ] [ome.event.ThingStatusInfoChangedEvent] - 'velux:klf200:7dff2548' changed from UNKNOWN to ONLINE -[INFO ] [thome.event.ItemChannelLinkAddedEvent] - Link 'velux_klf200_7dff2548_status-velux:klf200:7dff2548:status' has been added. -[INFO ] [smarthome.event.ItemStateChangedEvent] - velux_klf200_7dff2548_status changed from NULL to GW_S_GWM/GW_SS_IDLE -[INFO ] [thome.event.ItemChannelLinkAddedEvent] - Link 'velux_klf200_7dff2548_timestamp-velux:klf200:7dff2548:timestamp' has been added. -[WARN ] [ding.velux.handler.VeluxBridgeHandler] - Exception occurred during activated refresh scheduler: null -[INFO ] [smarthome.event.ItemStateChangedEvent] - velux_klf200_7dff2548_timestamp changed from NULL to 1565646559977 -[INFO ] [thome.event.ItemChannelLinkAddedEvent] - Link 'velux_klf200_7dff2548_products-velux:klf200:7dff2548:products' has been added. -[INFO ] [smarthome.event.ItemStateChangedEvent] - velux_klf200_7dff2548_products changed from NULL to 4_members:_Product_"M_Rollershutter"_/_ROLLER_SHUTTER_(bridgeIndex=4,serial=56:32:14:5A:12:1C:05:5F,position=0010),Product_"O_Rollershutter"_/_ROLLER_SHUTTER_(bridgeIndex=3,serial=53:09:40:5A:0C:23:0A:6E,position=0000),Product_"M_Window"_/_WINDOW_OPENER_(bridgeIndex=0,serial=56:23:3E:26:0C:1B:00:10,position=C800),Product_"W-Rollershutter"_/_ROLLER_SHUTTER_(bridgeIndex=1,serial=53:09:40:5A:0C:2A:05:64,position=0000) -[INFO ] [thome.event.ItemChannelLinkAddedEvent] - Link 'velux_klf200_7dff2548_scenes-velux:klf200:7dff2548:scenes' has been added. -[INFO ] [smarthome.event.ItemStateChangedEvent] - velux_klf200_7dff2548_scenes changed from NULL to 10_members:_Scene_"V_Shutter_West_100"_(index_5)_with_non-silent_mode_and_0_actions,Scene_"V_Shutter_West_000"_(index_4)_with_non-silent_mode_and_0_actions,Scene_"V_Shutter_Ost_090"_(index_10)_with_non-silent_mode_and_0_actions,Scene_"V_Window_Mitte_005"_(index_3)_with_non-silent_mode_and_0_actions,Scene_"V_Window_Mitte_000"_(index_1)_with_non-silent_mode_and_0_actions,Scene_"V_Window_Mitte_100"_(index_2)_with_non-silent_mode_and_0_actions,Scene_"V_Shutter_West_090"_(index_7)_with_non-silent_mode_and_0_actions,Scene_"V_Window_Mitte_010"_(index_0)_with_non-silent_mode_and_0_actions,Scene_"V_Shutter_Ost_000"_(index_8)_with_non-silent_mode_and_0_actions,Scene_"V_Shutter_Ost_100"_(index_9)_with_non-silent_mode_and_0_actions -[INFO ] [smarthome.event.ItemStateChangedEvent] - velux_klf200_7dff2548_timestamp changed from 1565646559977 to 1565646593645 -[INFO ] [ig.discovery.internal.PersistentInbox] - Added new thing 'velux:scene:7dff2548:V_Shutter_West_100' to inbox. +``` + +However if you have set the configuration parameter isProtocolTraceEnabled to true, you'll see the complete sequence of exchanged messages: + +``` +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Sending command GW_PASSWORD_ENTER_REQ. +[INFO ] [nternal.bridge.slip.io.SSLconnection] - Starting velux bridge connection. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_PASSWORD_ENTER_CFM. +[INFO ] [g.velux.internal.bridge.slip.SClogin] - velux bridge connection successfully established (login succeeded). +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Sending command GW_COMMAND_SEND_REQ. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_COMMAND_SEND_CFM. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Sending command GW_GET_LIMITATION_STATUS_REQ. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_NODE_STATE_POSITION_CHANGED_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_COMMAND_RUN_STATUS_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_COMMAND_RUN_STATUS_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_SESSION_FINISHED_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_NODE_STATE_POSITION_CHANGED_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_GET_LIMITATION_STATUS_CFM. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_LIMITATION_STATUS_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Sending command GW_GET_NODE_INFORMATION_REQ. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_COMMAND_RUN_STATUS_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_SESSION_FINISHED_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_GET_NODE_INFORMATION_CFM. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_GET_NODE_INFORMATION_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Sending command GW_GET_NODE_INFORMATION_REQ. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_GET_NODE_INFORMATION_CFM. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_GET_NODE_INFORMATION_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Sending command GW_GET_NODE_INFORMATION_REQ. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_GET_NODE_INFORMATION_CFM. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_GET_NODE_INFORMATION_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Sending command GW_GET_NODE_INFORMATION_REQ. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_GET_NODE_INFORMATION_CFM. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_GET_NODE_INFORMATION_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Sending command GW_GET_LIMITATION_STATUS_REQ. ... ``` + ## Supported/Tested Firmware Revisions The Velux Bridge in API version one (firmware version 0.1.1.*) allows activating a set of predefined actions, so called scenes. Therefore beside the bridge, only one main thing exists, the scene element. The next-generation firmware version two is not backward compatible, and does not provide a public web frontend, but version two does provide full access to any IO-Home compatible devices not limited to Velux and includes many different features. diff --git a/bundles/org.openhab.binding.velux/doc/conf/items/velux.items b/bundles/org.openhab.binding.velux/doc/conf/items/velux.items index 6b8ca67bb34cc..781feb191b9f1 100644 --- a/bundles/org.openhab.binding.velux/doc/conf/items/velux.items +++ b/bundles/org.openhab.binding.velux/doc/conf/items/velux.items @@ -1,6 +1,6 @@ /** * - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/doc/conf/rules/velux.rules b/bundles/org.openhab.binding.velux/doc/conf/rules/velux.rules index b60541b1c75f2..28159b27f7cac 100644 --- a/bundles/org.openhab.binding.velux/doc/conf/rules/velux.rules +++ b/bundles/org.openhab.binding.velux/doc/conf/rules/velux.rules @@ -1,6 +1,6 @@ /** * - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/doc/conf/sitemaps/velux.sitemap b/bundles/org.openhab.binding.velux/doc/conf/sitemaps/velux.sitemap index f71efbfb89581..0ff17a8864c50 100644 --- a/bundles/org.openhab.binding.velux/doc/conf/sitemaps/velux.sitemap +++ b/bundles/org.openhab.binding.velux/doc/conf/sitemaps/velux.sitemap @@ -1,6 +1,6 @@ /** * - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/doc/conf/things/velux-001.things b/bundles/org.openhab.binding.velux/doc/conf/things/velux-001.things new file mode 100644 index 0000000000000..5d99d56ce8034 --- /dev/null +++ b/bundles/org.openhab.binding.velux/doc/conf/things/velux-001.things @@ -0,0 +1,30 @@ +/** + * + * 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 + */ + +/** + * OpenHAB thing definition for velux binding: + * define a Velux device behind the bridge + * + * @author Guenther Schreiner - Initial contribution + */ + +// +// Definition of a Velux window behind bridge velux:klf200:gs28 +// + +Thing velux:window:gs28:window001 (velux:klf200:gs28) [ serial="53:09:40:5A:0C:2A:05:64" ] + +// +// vim: syntax=Xtend vim: noai:ts=4:sw=4 +// +// end-of-things/velux-bridge.things +// diff --git a/bundles/org.openhab.binding.velux/doc/conf/things/velux-bridge.things b/bundles/org.openhab.binding.velux/doc/conf/things/velux-bridge.things new file mode 100644 index 0000000000000..b0e3d1f7b82d7 --- /dev/null +++ b/bundles/org.openhab.binding.velux/doc/conf/things/velux-bridge.things @@ -0,0 +1,30 @@ +/** + * + * 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 + */ + +/** + * OpenHAB thing definition for velux binding: + * define basic Velux bridge parameters + * + * @author Guenther Schreiner - Initial contribution + */ + +// +// Definition of Velux bridge velux:klf200:home +// + +Bridge velux:klf200:gs28 [ ipAddress="192.168.45.9", tcpPort=51200, password="secret", isProtocolTraceEnabled=true ] + +// +// vim: syntax=Xtend vim: noai:ts=4:sw=4 +// +// end-of-things/velux-bridge.things +// diff --git a/bundles/org.openhab.binding.velux/doc/conf/things/velux.things b/bundles/org.openhab.binding.velux/doc/conf/things/velux.things.disabled similarity index 54% rename from bundles/org.openhab.binding.velux/doc/conf/things/velux.things rename to bundles/org.openhab.binding.velux/doc/conf/things/velux.things.disabled index 324681e94375e..c8e3f6af6271a 100644 --- a/bundles/org.openhab.binding.velux/doc/conf/things/velux.things +++ b/bundles/org.openhab.binding.velux/doc/conf/things/velux.things.disabled @@ -1,6 +1,6 @@ /** * - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -14,6 +14,17 @@ * OpenHAB thing definition for velux binding: * define basic Velux bridge parameters * + * This is a definition which corresponds to the output of th Bridge + * during startup, i.e. + * + * [INFO ] [x.internal.handler.VeluxBridgeHandler] - Found velux actuators: + * Product "#0" / SLIDER_SHUTTER (bridgeIndex=0,serial=56:32:14:5A:12:1C:05:5F,position=0000) + * Product "#1" / SLIDER_SHUTTER (bridgeIndex=1,serial=53:09:40:5A:0C:23:0A:6E,position=0000) + * Product "DG-M-Window" / SLIDER_WINDOW (bridgeIndex=3,serial=56:23:3E:26:0C:1B:00:10,position=C800) + * Product "#2" / SLIDER_SHUTTER (bridgeIndex=2,serial=53:09:40:5A:0C:2A:05:64,position=0000) + * Product "#4" / SWITCH (bridgeIndex=4,serial=Somfy-Switch,position=C800) + * Product "#5" / SWITCH (bridgeIndex=5,serial=Somfy-Switch,position=C800) + * * @author Guenther Schreiner - Initial contribution */ @@ -21,7 +32,7 @@ // Definition of Velux bridge velux:klf200:home // -Bridge velux:klf200:home [ ipAddress="192.168.45.9", tcpPort=51200 ] { +Bridge velux:klf200:home [ ipAddress="192.168.45.9", tcpPort=51200, password="verySecret" ] { // Velux scenes @@ -33,9 +44,11 @@ Bridge velux:klf200:home [ ipAddress="192.168.45.9", tcpPort=51200 ] { // Velux IO-homecontrol devices Thing window V_DG_M_W [ serial="56:23:3E:26:0C:1B:00:10" ] - Thing rollershutter V_DG_M_S [ serial="56:32:14:5A:12:1C:05:5F" ] + Thing rollershutter V_DG_M_S [ serial="56:23:3E:26:0C:1B:00:10" ] Thing rollershutter V_DG_W_S [ serial="53:09:40:5A:0C:2A:05:64" ] - Thing actuator V_DG_O_S [ serial="53:09:40:5A:0C:23:0A:6E" ] + Thing rollershuffer V_DG_O_S [ serial="53:09:40:5A:0C:23:0A:6E" ] + Thing actuator V_SWITCH1 [ name="#4" ] + Thing actuator V_SWITCH2 [ name="#5" ] // Virtual rollershutter diff --git a/bundles/org.openhab.binding.velux/pom.xml b/bundles/org.openhab.binding.velux/pom.xml index 4a138563ced08..6d8d1cce56a0c 100644 --- a/bundles/org.openhab.binding.velux/pom.xml +++ b/bundles/org.openhab.binding.velux/pom.xml @@ -1,16 +1,20 @@ - - + + 4.0.0 org.openhab.addons.bundles org.openhab.addons.reactor.bundles - 2.5.0-SNAPSHOT + 2.5.1-SNAPSHOT org.openhab.binding.velux openHAB Add-ons :: Bundles :: Velux Binding + This binding integrates the Velux devices with help of a gateway, the Velux Bridge KLF200. diff --git a/bundles/org.openhab.binding.velux/src/main/feature/feature.xml b/bundles/org.openhab.binding.velux/src/main/feature/feature.xml index 8f81632822ccd..75a556b51b850 100644 --- a/bundles/org.openhab.binding.velux/src/main/feature/feature.xml +++ b/bundles/org.openhab.binding.velux/src/main/feature/feature.xml @@ -1,6 +1,6 @@ - - mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${project.version}/xml/features + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features openhab-runtime-base diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java index cb59dd65b17e5..3f11984c1e095 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConfig.java index 61890703062bf..f05d437a2df41 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConstants.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConstants.java index 9a8968c138132..96453e0f4ba4f 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConstants.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConstants.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingProperties.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingProperties.java index 5d33388eb7ab8..1e71a932214a6 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingProperties.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingProperties.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -21,12 +21,15 @@ * This class contains the property identifications: *

        *
      • {@link #PROPERTY_BINDING_BUNDLEVERSION} for identification of the binding,
      • + *
      • {@link #PROPERTY_BINDING_NOOFBRIDGES} for number of bridges,
      • + *
      • {@link #PROPERTY_BINDING_NOOFTHINGS} for number of things,
      • *
      *
        *
      • {@link #PROPERTY_SCENE_NAME} for defining the name of a scene,
      • + *
      • {@link #PROPERTY_SCENE_VELOCITY} for defining the velocity of a scene,
      • *
      *
        - *
      • {@link #PROPERTY_ACTUATOR_SERIALNUMBER} for defining the serial number of an actuator, a rollershutter and a + *
      • {@link #CONFIG_ACTUATOR_SERIALNUMBER} for defining the serial number of an actuator, a rollershutter and a * window,
      • *
      • {@link #PROPERTY_ACTUATOR_NAME} for defining the name of an actuator, a rollershutter and a window,
      • *
      • {@link #PROPERTY_ACTUATOR_INVERTED} for modifying the value of a Channel,
      • @@ -48,7 +51,7 @@ public class VeluxBindingProperties { public static final String PROPERTY_SCENE_NAME = "sceneName"; public static final String PROPERTY_SCENE_VELOCITY = "velocity"; - public static final String PROPERTY_ACTUATOR_SERIALNUMBER = "serial"; + public static final String CONFIG_ACTUATOR_SERIALNUMBER = "serial"; public static final String PROPERTY_ACTUATOR_NAME = "name"; public static final String PROPERTY_ACTUATOR_INVERTED = "inverted"; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxItemType.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxItemType.java index 9832387ab8492..6fc005d999163 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxItemType.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxItemType.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -12,10 +12,8 @@ */ package org.openhab.binding.velux.internal; -import java.util.Arrays; -import java.util.HashSet; +import java.util.ArrayList; import java.util.List; -import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.items.GenericItem; @@ -34,29 +32,28 @@ *
          *
        • associated thing identified by String
        • *
        • defined channel identified by String
        • - *
        • {@link VeluxItemType#getItemClass getItemClass} item class
        • - *
        • {@link VeluxItemType#isReadable isReadable} about a read possibility
        • - *
        • {@link VeluxItemType#isWritable isWritable} about a write possibility
        • - *
        • {@link VeluxItemType#isExecutable isExecutable} about an execute possibility
        • - *
        • {@link VeluxItemType#isToBeRefreshed isToBeRefreshed} about necessarily to be refreshed.
        • - *
        • {@link VeluxItemType#isToBeRefreshedNow isToBeRefreshedNow} about necessarily to be refreshed at this time.
        • + *
        • {@link #getItemClass} item class,
        • + *
        • {@link #isReadable} about a read possibility,
        • + *
        • {@link #isWritable} about a write possibility,
        • + *
        • {@link #isExecutable} about an execute possibility,
        • + *
        • {@link #isToBeRefreshed} about necessarily to be refreshed,
        • + *
        • {@link #isToBeRefreshedNow} about necessarily to be refreshed at this time,
        • + *
        • {@link #isChannel} as indication of being handled as Channel of a thing,
        • + *
        • {@link #isProperty} as indication of being handled as property of a thing.
        • *
        * * In addition there are helper methods providing information about: * *
          - *
        • {@link VeluxItemType#getByString getByString} the enum by identifier string,
        • - *
        • {@link VeluxItemType#getByThingAndChannel getByThingAndChannel} to retrieve an enum instance selected by Thing + *
        • {@link #getIdentifier} returning the common identifier string,
        • + *
        • {@link #getByThingAndChannel} to retrieve an enum instance selected by Thing * and Channel identifier,
        • - *
        • {@link VeluxItemType#getThingIdentifiers getThingIdentifiers} to retrieve any Thing identifiers as array of + *
        • {@link #getPropertyEntriesByThing} to retrieve any Thing identifiers as array of * String,
        • - *
        • {@link VeluxItemType#getChannelIdentifiers getChannelIdentifiers} to retrieve any Channel identifiers as array of - * String.
        • *
        *

        - * Within this enumeration, the expected behaviour of the OpenHAB item (resp. Channel) - * is set. For each kind of Channel (i.e. bridge or device) parameter a - * set of information is defined: + * Within this enumeration, the expected behaviour of the OpenHAB item (resp. Channel or Property) is set. For each kind + * of Channel (i.e. bridge or device) parameter a set of information is defined: *

          *
        • * Unique identification by: @@ -85,16 +82,16 @@ public enum VeluxItemType { BRIDGE_DOWNTIME(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_DOWNTIME, TypeFlavor.READONLY_VOLATILE_NUMBER), BRIDGE_RELOAD(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_RELOAD, TypeFlavor.INITIATOR), BRIDGE_DO_DETECTION(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_DO_DETECTION, TypeFlavor.INITIATOR), - BRIDGE_FIRMWARE(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_FIRMWARE, TypeFlavor.READONLY_STATIC_STRING), - BRIDGE_IPADDRESS(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_IPADDRESS, TypeFlavor.READONLY_STATIC_STRING), - BRIDGE_SUBNETMASK(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_SUBNETMASK, TypeFlavor.READONLY_STATIC_STRING), - BRIDGE_DEFAULTGW(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_DEFAULTGW, TypeFlavor.READONLY_STATIC_STRING), - BRIDGE_DHCP(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_DHCP, TypeFlavor.READONLY_STATIC_SWITCH), - BRIDGE_WLANSSID(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_WLANSSID, TypeFlavor.READONLY_STATIC_STRING), - BRIDGE_WLANPASSWORD(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_WLANPASSWORD, TypeFlavor.READONLY_STATIC_STRING), - BRIDGE_PRODUCTS(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_PRODUCTS, TypeFlavor.READONLY_STATIC_STRING), - BRIDGE_SCENES(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_SCENES, TypeFlavor.READONLY_STATIC_STRING), - BRIDGE_CHECK(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_CHECK, TypeFlavor.READONLY_STATIC_STRING), + BRIDGE_FIRMWARE(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_FIRMWARE, TypeFlavor.PROPERTY), + BRIDGE_IPADDRESS(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_IPADDRESS, TypeFlavor.PROPERTY), + BRIDGE_SUBNETMASK(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_SUBNETMASK, TypeFlavor.PROPERTY), + BRIDGE_DEFAULTGW(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_DEFAULTGW, TypeFlavor.PROPERTY), + BRIDGE_DHCP(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_DHCP, TypeFlavor.PROPERTY), + BRIDGE_WLANSSID(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_WLANSSID, TypeFlavor.PROPERTY), + BRIDGE_WLANPASSWORD(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_WLANPASSWORD, TypeFlavor.PROPERTY), + BRIDGE_PRODUCTS(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_PRODUCTS, TypeFlavor.PROPERTY), + BRIDGE_SCENES(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_SCENES, TypeFlavor.PROPERTY), + BRIDGE_CHECK(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_CHECK, TypeFlavor.PROPERTY), // ACTUATOR_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION, TypeFlavor.MANIPULATOR_SHUTTER), ACTUATOR_STATE(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_STATE, TypeFlavor.MANIPULATOR_SWITCH), @@ -153,12 +150,21 @@ private enum TypeFlavor { * Used to manipulate an actuator. */ MANIPULATOR_SWITCH, + /** + * Used to present read-only non-volatile configuration parameter (being handled as property of aThing). + */ + PROPERTY, /** * Used to define an UNUSABLE entry. */ UNUSABLE, } + /* + * *************************** + * ***** Private Objects ***** + */ + private ThingTypeUID thingIdentifier; private String channelIdentifier; private Class itemClass; @@ -167,18 +173,28 @@ private enum TypeFlavor { private boolean itemIsExecutable; private boolean itemIsToBeRefreshed; private int itemsRefreshDivider; + private boolean itemIsChannel; + private boolean itemIsProperty; private static final Logger LOGGER = LoggerFactory.getLogger(VeluxItemType.class); private final Logger logger = LoggerFactory.getLogger(getClass()); private static final int REFRESH_CYCLE_FIRST_TIME = 0; private static final int REFRESH_ONCE_A_DAY = 8640; + private static final int REFRESH_EACH_HOUR = 360; private static final int REFRESH_EACH_MINUTE = 6; private static final int REFRESH_EVERY_CYCLE = 1; + /* + * ************************ + * ***** Constructors ***** + */ + VeluxItemType(ThingTypeUID thingIdentifier, String channelIdentifier, TypeFlavor typeFlavor) { this.thingIdentifier = thingIdentifier; this.channelIdentifier = channelIdentifier; + this.itemIsChannel = true; + this.itemIsProperty = false; switch (typeFlavor) { case READONLY_STATIC_STRING: this.itemClass = StringItem.class; @@ -257,6 +273,17 @@ private enum TypeFlavor { this.itemsRefreshDivider = REFRESH_EACH_MINUTE; break; + case PROPERTY: + this.itemClass = StringItem.class; + this.itemIsReadable = true; + this.itemIsWritable = false; + this.itemIsExecutable = false; + this.itemIsToBeRefreshed = true; + this.itemsRefreshDivider = REFRESH_EACH_HOUR; + this.itemIsChannel = false; + this.itemIsProperty = true; + break; + case UNUSABLE: default: this.itemClass = StringItem.class; @@ -265,21 +292,14 @@ private enum TypeFlavor { this.itemIsExecutable = false; this.itemIsToBeRefreshed = false; this.itemsRefreshDivider = REFRESH_ONCE_A_DAY; + this.itemIsChannel = false; } } - private VeluxItemType(ThingTypeUID thingIdentifier, String channelIdentifier, - Class itemClass, boolean isReadable, boolean isWritable, boolean isExecutable, - boolean isToBeRefreshed, int refreshDivider) { - this.thingIdentifier = thingIdentifier; - this.channelIdentifier = channelIdentifier; - this.itemClass = itemClass; - this.itemIsReadable = isReadable; - this.itemIsWritable = isWritable; - this.itemIsExecutable = isExecutable; - this.itemIsToBeRefreshed = isToBeRefreshed; - this.itemsRefreshDivider = refreshDivider; - } + /* + * ******************************** + * ***** Class access methods ***** + */ @Override public String toString() { @@ -292,10 +312,19 @@ public String toString() { * @return thingIdentifier of type String describing the value of the enum {@link VeluxItemType} * return */ - public ThingTypeUID getIdentifier() { + public ThingTypeUID getThingTypeUID() { return this.thingIdentifier; } + /** + * {@link VeluxItemType} access method to query common (channel/property) identifier on this type of item. + * + * @return channelIdentifier of type String describing the value of the enum {@link VeluxItemType}. + */ + public String getIdentifier() { + return this.channelIdentifier; + } + /** * {@link VeluxItemType} access method to query the appropriate type of item. * @@ -355,6 +384,26 @@ public int getRefreshDivider() { return this.itemsRefreshDivider; } + /** + * {@link VeluxItemType} access method to query the type of this item. + * + * @return isChannel of type boolean describing the need to be handled as channel. + */ + public boolean isChannel() { + logger.trace("isChannel() returns {}.", this.itemIsChannel); + return this.itemIsChannel; + } + + /** + * {@link VeluxItemType} access method to query the type of this item. + * + * @return itemIsProperty of type boolean describing the need to be handled as property. + */ + public boolean isProperty() { + logger.trace("isProperty() returns {}.", this.itemIsProperty); + return this.itemIsProperty; + } + /** * {@link VeluxItemType} access method to find an enum by itemTypeName. * @@ -379,49 +428,32 @@ public VeluxItemType getByString(String itemTypeName) { * @return veluxItemType of type VeluxItemType describing the appropriate enum. */ public static VeluxItemType getByThingAndChannel(ThingTypeUID thingIdentifier, String channelIdentifier) { - LOGGER.trace("getByThingAndChannel({},{}) called.", thingIdentifier, channelIdentifier); for (VeluxItemType v : VeluxItemType.values()) { - if (((thingIdentifier.equals(v.thingIdentifier)) || (thingIdentifier.equals(v.thingIdentifier))) - && (channelIdentifier.equals(v.channelIdentifier))) { - LOGGER.trace("getByThingAndChannel() returns enum {}.", v); + if (thingIdentifier.equals(v.thingIdentifier) && channelIdentifier.equals(v.channelIdentifier)) { + LOGGER.trace("getByThingAndChannel({},{}) returns enum {}.", thingIdentifier, channelIdentifier, v); return v; } } - LOGGER.trace("getByThingAndChannel() returns UNKNOWN."); + LOGGER.trace("getByThingAndChannel({},{}) returns enum UNKNOWN.", thingIdentifier, channelIdentifier); return UNKNOWN; } /** - * {@link VeluxItemType} access method to find an enum by name. - * - * @return veluxItemType of type VeluxItemType describing the appropriate enum. - */ - public static String[] getThingIdentifiers() { - LOGGER.trace("getThingIdentifiers() called."); - Set> uniqueSet = new HashSet>(); - for (VeluxItemType v : VeluxItemType.values()) { - LOGGER.trace("getThingIdentifiers() adding {}.", v.thingIdentifier); - uniqueSet.add(Arrays.asList(v.thingIdentifier)); - } - return uniqueSet.toArray(new String[uniqueSet.size()]); - } - - /** - * {@link VeluxItemType} access method to find an enum by name. + * {@link VeluxItemType} access method to find similar enum entries by thingIdentifier. * * @param thingIdentifier as name of requested Thing of type String. * - * @return veluxItemType of type VeluxItemType describing the appropriate enum. + * @return listOfveluxItemType of type List of VeluxItemType containing all similar enum entries. */ - public static String[] getChannelIdentifiers(ThingTypeUID thingIdentifier) { - LOGGER.trace("getChannelIdentifiers() called."); - Set> uniqueSet = new HashSet>(); + public static List getPropertyEntriesByThing(ThingTypeUID thingIdentifier) { + List list = new ArrayList(); for (VeluxItemType v : VeluxItemType.values()) { - if (thingIdentifier.equals(v.thingIdentifier)) { - uniqueSet.add(Arrays.asList(v.channelIdentifier)); + if (thingIdentifier.equals(v.thingIdentifier) && v.itemIsProperty) { + list.add(v); } } - return uniqueSet.toArray(new String[uniqueSet.size()]); + LOGGER.trace("getPropertyEntriesByThing({}) returns {}.", thingIdentifier, list); + return list; } /** diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxRSBindingConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxRSBindingConfig.java index 21c7d3f640033..c5eddfabfa48e 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxRSBindingConfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxRSBindingConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridge.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridge.java index 285470ff668b0..6471e33f06f4a 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridge.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridge.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeActuators.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeActuators.java index e4c65d5fa761b..8193fd2af431f 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeActuators.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeActuators.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDetectProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDetectProducts.java index b4b83775fc26a..4dc15c8b755aa 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDetectProducts.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDetectProducts.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceCheckLostNodes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceCheckLostNodes.java index 1a51d05345217..7aa93ab84ec69 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceCheckLostNodes.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceCheckLostNodes.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceStatus.java index c9723d885462f..303bd36b1e2ef 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceStatus.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceStatus.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetFirmware.java index e8bcf0210f9df..ae3d62f36d1b9 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetFirmware.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetFirmware.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetHouseStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetHouseStatus.java index 1126f52e3857d..745ae6694b781 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetHouseStatus.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetHouseStatus.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetLimitation.java index e6dcfb886a8c6..2e7a05fbafac1 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetLimitation.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetLimitation.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeInstance.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeInstance.java index 76b43fac14c06..edac94e74c4ae 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeInstance.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeInstance.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeLANConfig.java index e5422b2410f7e..0e98d7ed23931 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeLANConfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeLANConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeProvider.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeProvider.java index 710bf4bc02315..5339554b6bfa3 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeProvider.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeProvider.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunProductCommand.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunProductCommand.java index ae2e0390e4e1d..91c98bdc2c091 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunProductCommand.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunProductCommand.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunScene.java index 28925bb55059d..c4509699b3f8c 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunScene.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunScene.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeScenes.java index 7eadbab313d89..4cee44d2b7864 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeScenes.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeScenes.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetHouseStatusMonitor.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetHouseStatusMonitor.java index 07593ba6699fe..6e508b981e27f 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetHouseStatusMonitor.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetHouseStatusMonitor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetLimitation.java index 7dd39657a4d79..ffae910c35f42 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetLimitation.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetLimitation.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -14,6 +14,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.velux.internal.bridge.common.SetProductLimitation; +import org.openhab.binding.velux.internal.things.VeluxProductPosition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,7 +48,7 @@ public class VeluxBridgeSetLimitation { * @param limitationMinimum new value for minimum limit. * @return true if successful, and false otherwise. */ - public boolean setMinimumLimitation(VeluxBridge bridge, int nodeId, int limitationMinimum) { + public boolean setMinimumLimitation(VeluxBridge bridge, int nodeId, VeluxProductPosition limitationMinimum) { logger.trace("setMinimumLimitation(nodeId={}, limitation={}) called.", nodeId, limitationMinimum); SetProductLimitation bcp = bridge.bridgeAPI().setProductLimitation(); @@ -55,7 +56,7 @@ public boolean setMinimumLimitation(VeluxBridge bridge, int nodeId, int limitati logger.info("setMinimumLimitation(): aborting processing as there is handler available."); return false; } - bcp.setActuatorIdAndMinimumLimitation(nodeId, limitationMinimum); + bcp.setActuatorIdAndMinimumLimitation(nodeId, limitationMinimum.getPositionAsVeluxType()); if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { logger.trace("setMinimumLimitation() finished successfully."); return true; @@ -73,7 +74,7 @@ public boolean setMinimumLimitation(VeluxBridge bridge, int nodeId, int limitati * @param limitationMaximum new value for maximum limit. * @return true if successful, and false otherwise. */ - public boolean setMaximumLimitation(VeluxBridge bridge, int nodeId, int limitationMaximum) { + public boolean setMaximumLimitation(VeluxBridge bridge, int nodeId, VeluxProductPosition limitationMaximum) { logger.trace("setMaximumLimitation(nodeId={}, limitation={}) called.", nodeId, limitationMaximum); SetProductLimitation bcp = bridge.bridgeAPI().setProductLimitation(); @@ -81,7 +82,7 @@ public boolean setMaximumLimitation(VeluxBridge bridge, int nodeId, int limitati logger.info("setMaximumLimitation(): aborting processing as there is handler available."); return false; } - bcp.setActuatorIdAndMaximumLimitation(nodeId, limitationMaximum); + bcp.setActuatorIdAndMaximumLimitation(nodeId, limitationMaximum.getPositionAsVeluxType()); if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { logger.trace("setMaximumLimitation() finished successfully."); return true; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetSceneVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetSceneVelocity.java index e205d45eec476..d9c021c60299f 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetSceneVelocity.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetSceneVelocity.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeWLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeWLANConfig.java index b03128dbf1ef1..7949844d47b11 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeWLANConfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeWLANConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeAPI.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeAPI.java index a2a3a48ca79ca..fe89f92816c61 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeAPI.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeAPI.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeCommunicationProtocol.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeCommunicationProtocol.java index fbd0d5c3ef9e3..c71e26b602379 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeCommunicationProtocol.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeCommunicationProtocol.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetDeviceStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetDeviceStatus.java index 9a4b4243772c6..96451fc78beb4 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetDeviceStatus.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetDeviceStatus.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetFirmware.java index 728075fddc77a..b1e1fc6398b49 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetFirmware.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetFirmware.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetHouseStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetHouseStatus.java index eb1207be9d78c..1bc2355232580 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetHouseStatus.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetHouseStatus.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetLANConfig.java index 9ebdb032273bf..f65d55d49822f 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetLANConfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetLANConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProduct.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProduct.java index d133c69538f92..43fc41f0bc208 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProduct.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProduct.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProductLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProductLimitation.java index 915f8b47deaab..7c4d464633aa8 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProductLimitation.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProductLimitation.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProducts.java index 6b18c8e872b59..73f5d31661f42 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProducts.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProducts.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetScenes.java index 5d5391c86cdf4..eb94d8d0eec16 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetScenes.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetScenes.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetWLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetWLANConfig.java index b2bb33fa8d805..d8d51b17e359c 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetWLANConfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetWLANConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Login.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Login.java index 52ad72e9a6ad2..d2eefeb074d0c 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Login.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Login.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Logout.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Logout.java index 905ba230098c1..7bcd9c68f1e95 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Logout.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Logout.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductCommand.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductCommand.java index bcca33381090e..e70247b0a9913 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductCommand.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductCommand.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductDiscovery.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductDiscovery.java index 757000021c13e..8bd6bd571d3b7 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductDiscovery.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductDiscovery.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductIdentification.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductIdentification.java index 3e3f8888c79fb..5b7c813c7791f 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductIdentification.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductIdentification.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductSearch.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductSearch.java index 80d7a37d1e9b4..1b5e4777cf5f3 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductSearch.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductSearch.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunScene.java index 3f8e97d7fad54..4c31645a087b1 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunScene.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunScene.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetHouseStatusMonitor.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetHouseStatusMonitor.java index f36b85a75d09a..26f7c6f8f64ad 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetHouseStatusMonitor.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetHouseStatusMonitor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetProductLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetProductLimitation.java index 98187c32911cc..ccf6c66a67d56 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetProductLimitation.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetProductLimitation.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetSceneVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetSceneVelocity.java index b4036d490269c..df54d191c1d82 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetSceneVelocity.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetSceneVelocity.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/package-info.java index ccae7199ad06c..b17c564ad2d8c 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/package-info.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetDeviceStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetDeviceStatus.java index 2aafbe7a378a5..024322de4807f 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetDeviceStatus.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetDeviceStatus.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetFirmware.java index 95239620b1782..dc6dc7f4d5bff 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetFirmware.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetFirmware.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetLANConfig.java index b385850bfc710..27b40b8e24afb 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetLANConfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetLANConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetProducts.java index f89eeeee8bd24..c0aca4b0e632c 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetProducts.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetProducts.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetScenes.java index dc2ae33d133f2..127ef8c471a68 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetScenes.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetScenes.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetWLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetWLANConfig.java index 0c6ea2414b36b..46e40500fae7f 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetWLANConfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetWLANConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JClogin.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JClogin.java index d9ec484b96bc3..535b28db70b8b 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JClogin.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JClogin.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JClogout.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JClogout.java index 759899c0ca645..a0eef33608841 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JClogout.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JClogout.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductDiscovery.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductDiscovery.java index 036d72f5df987..e12c380d985c0 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductDiscovery.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductDiscovery.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductIdentification.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductIdentification.java index 1be68bfb6231a..a70b9227d699b 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductIdentification.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductIdentification.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductSearch.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductSearch.java index 24c44089a1f25..a42e29d62772a 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductSearch.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductSearch.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunScene.java index 272e38e69a684..a7cab20b2547b 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunScene.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunScene.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCsetSceneVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCsetSceneVelocity.java index efaa3d4867bc8..e463aca5894ee 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCsetSceneVelocity.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCsetSceneVelocity.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonBridgeAPI.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonBridgeAPI.java index e9fee7a503608..fdf452c511566 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonBridgeAPI.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonBridgeAPI.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonBridgeCommunicationProtocol.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonBridgeCommunicationProtocol.java index eebdc72f8bd60..59f77ab20202b 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonBridgeCommunicationProtocol.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonBridgeCommunicationProtocol.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonVeluxBridge.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonVeluxBridge.java index 728015b3cdd94..146f2d8f8413d 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonVeluxBridge.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonVeluxBridge.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/package-info.java index 101cedf512abb..1945de82632fb 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/package-info.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/package-info.java index e3629db152f8b..59eebdd716f15 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/package-info.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetDeviceStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetDeviceStatus.java index 456710b4fea6b..7ced9a1202c8c 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetDeviceStatus.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetDeviceStatus.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetFirmware.java index b7ee30794919f..83ae94889c091 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetFirmware.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetFirmware.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetHouseStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetHouseStatus.java index 0ad387c7bd882..e67458b1438b7 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetHouseStatus.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetHouseStatus.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLANConfig.java index 7466ad6d23380..38099fae545ea 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLANConfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLANConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLimitation.java index c1cde60af7e20..1f56ebd495af0 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLimitation.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLimitation.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProduct.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProduct.java index d008e50751bd2..b810f96346ece 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProduct.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProduct.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -16,13 +16,13 @@ import org.openhab.binding.velux.internal.bridge.common.GetProduct; import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; import org.openhab.binding.velux.internal.things.VeluxProduct; +import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex; import org.openhab.binding.velux.internal.things.VeluxProductName; import org.openhab.binding.velux.internal.things.VeluxProductSerialNo; import org.openhab.binding.velux.internal.things.VeluxProductType; -import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; -import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; -import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -212,7 +212,7 @@ public void setResponse(short responseCommand, byte[] thisResponseData, boolean break; } - if (ntfName.length() == 0) { + if ((ntfName.length() == 0) || ntfName.startsWith("_")) { ntfName = "#".concat(String.valueOf(ntfNodeID)); logger.debug("setResponse(): device provided invalid name, using '{}' instead.", ntfName); } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProducts.java index 3cdd513e3075e..31cc6862c4101 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProducts.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProducts.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -16,13 +16,13 @@ import org.openhab.binding.velux.internal.bridge.common.GetProducts; import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; import org.openhab.binding.velux.internal.things.VeluxProduct; +import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex; import org.openhab.binding.velux.internal.things.VeluxProductName; import org.openhab.binding.velux.internal.things.VeluxProductSerialNo; import org.openhab.binding.velux.internal.things.VeluxProductType; -import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; -import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; -import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -207,7 +207,7 @@ public void setResponse(short responseCommand, byte[] thisResponseData, boolean int ntfAliasFive = responseData.getFourByteValue(120); logger.trace("setResponse(): ntfAliasFive={}.", ntfAliasFive); - if (ntfName.length() == 0) { + if ((ntfName.length() == 0) || ntfName.startsWith("_")) { ntfName = "#".concat(String.valueOf(ntfNodeID)); logger.debug("setResponse(): device provided invalid name, using '{}' instead.", ntfName); } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetScenes.java index 6f5a2d32d61de..6499c5f4c50e7 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetScenes.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetScenes.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetWLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetWLANConfig.java index c54be98b632c9..532544e8dae85 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetWLANConfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetWLANConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogin.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogin.java index 08e4aeb26d6f6..f0c87007bcb31 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogin.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogin.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogout.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogout.java index cf73e71ba3058..2090ddcad7693 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogout.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogout.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductCommand.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductCommand.java index 030ce4368b268..da54c23d71bcf 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductCommand.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductCommand.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductDiscovery.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductDiscovery.java index 3cbcf681e53f5..85b1ffede0508 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductDiscovery.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductDiscovery.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductIdentification.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductIdentification.java index e37d5a4fc054b..1b62afa82ed25 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductIdentification.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductIdentification.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductSearch.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductSearch.java index 496ed06678a10..ee10d5e57d7bf 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductSearch.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductSearch.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunScene.java index 5a8b0923bfdbf..8a221aee1960e 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunScene.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunScene.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetHouseStatusMonitor.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetHouseStatusMonitor.java index 27044aaaa086d..a7d4ec71e3186 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetHouseStatusMonitor.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetHouseStatusMonitor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetLimitation.java index 7cc55c04e5abe..313eccd55ff4f 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetLimitation.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetLimitation.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetSceneVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetSceneVelocity.java index 4b808650feb53..9c0964f9c722e 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetSceneVelocity.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetSceneVelocity.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeAPI.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeAPI.java index f51cff941415a..c2e6d652bd1fc 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeAPI.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeAPI.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeCommunicationProtocol.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeCommunicationProtocol.java index 64840287066e2..265cc719eb223 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeCommunicationProtocol.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeCommunicationProtocol.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipVeluxBridge.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipVeluxBridge.java index ac8fc3bff931d..55db3eb064627 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipVeluxBridge.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipVeluxBridge.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/Connection.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/Connection.java index 49bc055116149..634c5cafb67c4 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/Connection.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/Connection.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/DataInputStreamWithTimeout.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/DataInputStreamWithTimeout.java index 317cedd947728..1a2db47e5c31e 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/DataInputStreamWithTimeout.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/DataInputStreamWithTimeout.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/SSLconnection.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/SSLconnection.java index df8f564333241..b1c177b1ac3c0 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/SSLconnection.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/SSLconnection.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -16,6 +16,8 @@ import java.io.IOException; import java.net.ConnectException; import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; @@ -120,8 +122,8 @@ public void checkServerTrusted(X509Certificate @Nullable [] arg0, @Nullable Stri try { ctx = SSLContext.getInstance("SSL"); ctx.init(null, trustAllCerts, null); - } catch (Exception e) { - throw new IOException("create of an empty trust store failed."); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + throw new IOException(String.format("create of an empty trust store failed: %s.", e.getMessage())); } logger.trace("SSLconnection(): creating socket..."); // Just for avoidance of Potential null pointer access diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/package-info.java index 6f714906883ae..be39bb832264f 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/package-info.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/package-info.java index 63549dfde14ce..cac9f3a6114ca 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/package-info.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/KLF200Response.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/KLF200Response.java index 4e0873f016013..d29aa9717f88f 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/KLF200Response.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/KLF200Response.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/Packet.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/Packet.java index 8150f784baada..b351d0e05ee16 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/Packet.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/Packet.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/SlipEncoding.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/SlipEncoding.java index d55108a5dfea8..afe9b6564518c 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/SlipEncoding.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/SlipEncoding.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/SlipRFC1055.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/SlipRFC1055.java index 57970eb45a6ac..accc7eccad750 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/SlipRFC1055.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/SlipRFC1055.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/package-info.java index d31d2d31d1c91..a4503a79b83e1 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/package-info.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxBridgeConfiguration.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxBridgeConfiguration.java index 4aea401bb6b3c..dbcef9ef5cfd2 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxBridgeConfiguration.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxBridgeConfiguration.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxThingConfiguration.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxThingConfiguration.java index 924a17903c4ed..df3b51f7ed3c0 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxThingConfiguration.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxThingConfiguration.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -25,9 +25,13 @@ *

          * There are three parts. Information for: *

            - *
          • {@link #sceneName Channel of type scene},
          • - *
          • {@link #serial Channels of type actuator, rollershutter and window},
          • - *
          • {@link #sceneLevels Channel of type virtualshutter}.
          • + *
          • {@link #sceneName} Name of a scene,
          • + *
          • {@link #serial} Unique identification of type actuator, rollershutter and window,
          • + *
          • {@link #name} Alternate Unique identification of type actuator, rollershutter and window,
          • + *
          • {@link #inverted} Value inversion for an actuator, rollershutter or window,
          • + *
          • {@link #velocity} Speed value for a scene,
          • + *
          • {@link #sceneLevels} Virtual shutter definition as a set of scenes imitating a shutter,
          • + *
          • {@link #currentLevel} Initial state of Virtual shutter definition.
          • *
          * * @author Guenther Schreiner - Initial contribution. @@ -45,8 +49,7 @@ public class VeluxThingConfiguration extends Configuration { *
        • {@link #sceneName} for identification of a set of settings, so called scene.
        • *
        */ - @SuppressWarnings("unused") - private String sceneName = VeluxBindingConstants.UNKNOWN; + String sceneName = VeluxBindingConstants.UNKNOWN; /** * {@link #serial} of type {@link String}, identifying a io-homecontrol device by its serial number (i.e. @@ -61,8 +64,7 @@ public class VeluxThingConfiguration extends Configuration { *
      • {@link #velocity} for modified action speed.
      • *
      */ - @SuppressWarnings("unused") - private String serial = VeluxProductSerialNo.UNKNOWN; + public String serial = VeluxProductSerialNo.UNKNOWN; /** * {@link #name} of type {@link String}, identifying a io-homecontrol device by its registration name especially * for somfy as they do not provide a valid serial number. @@ -70,8 +72,7 @@ public class VeluxThingConfiguration extends Configuration { * Part of the {@link #serial Configuration for the channels actuator, rollershutter and window}. *

      */ - @SuppressWarnings("unused") - private String name = VeluxBindingConstants.UNKNOWN; + String name = VeluxBindingConstants.UNKNOWN; /** * {@link #inverted} of type {@link Boolean}, inverts each Channel value. This means 0% will be handled as 100%, * and vice versa, 100% will be handled as 0%. @@ -79,8 +80,7 @@ public class VeluxThingConfiguration extends Configuration { * Part of the {@link #serial Configuration for the channels actuator, rollershutter and window}. *

      */ - @SuppressWarnings("unused") - private Boolean inverted = false; + Boolean inverted = false; /** * {@link #velocity} of type {@link String}, describes the intended speed of action. * Possible values are defined within VeluxProductVelocity. @@ -88,8 +88,7 @@ public class VeluxThingConfiguration extends Configuration { * Part of the {@link #serial Configuration for the channels actuator, rollershutter and window}. *

      */ - @SuppressWarnings("unused") - private String velocity = VeluxBindingConstants.UNKNOWN; + String velocity = VeluxBindingConstants.UNKNOWN; /** * {@link #sceneLevels} of type {@link String}, identifying a number of Velux scenes which act together as a virtual @@ -107,15 +106,12 @@ public class VeluxThingConfiguration extends Configuration { *
    • {@link #currentLevel} for identification of a set of settings, so called scene.
    • *
    */ - @SuppressWarnings("unused") - private String sceneLevels = VeluxBindingConstants.UNKNOWN; + String sceneLevels = VeluxBindingConstants.UNKNOWN; /** * {@link #currentLevel} of type {@link int}, which represents the current shutter level. *

    * Private part of the {@link #sceneLevels Configuration for the channel virtualshutter}. *

    */ - @SuppressWarnings("unused") - private int currentLevel = 0; - + int currentLevel = 0; } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/package-info.java index d7bd49ceeaf4e..61c03559a5d25 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/package-info.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/development/Threads.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/development/Threads.java index e406d4d52793e..5166206eeee01 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/development/Threads.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/development/Threads.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/development/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/development/package-info.java index 0f247bf197d8e..14c6e4454edc3 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/development/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/development/package-info.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -11,7 +11,7 @@ * SPDX-License-Identifier: EPL-2.0 */ /** - * Classes to support development of bindings. + * Generic classes to support development of bindings. * * @author Guenther Schreiner - Initial contribution */ diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/VeluxDiscoveryService.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/VeluxDiscoveryService.java index 0038fe50b086e..081ebd25ee378 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/VeluxDiscoveryService.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/VeluxDiscoveryService.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -34,12 +34,12 @@ import org.openhab.binding.velux.internal.utils.Localization; import org.openhab.binding.velux.internal.utils.ManifestInformation; import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * The {@link VeluxDiscoveryService} is responsible for discovering scenes on - * the current Velux Bridge. + * The {@link VeluxDiscoveryService} is responsible for discovering actuators and scenes on the current Velux Bridge. * * @author Guenther Schreiner - Initial contribution. */ @@ -53,11 +53,25 @@ public class VeluxDiscoveryService extends AbstractDiscoveryService implements Runnable { private final Logger logger = LoggerFactory.getLogger(VeluxDiscoveryService.class); + // Class internal + private static final int DISCOVER_TIMEOUT_SECONDS = 300; + private @NonNullByDefault({}) LocaleProvider localeProvider; + private @NonNullByDefault({}) TranslationProvider i18nProvider; private Localization localization = Localization.UNKNOWN; private static @Nullable VeluxBridgeHandler bridgeHandler = null; + // Private + + private void updateLocalization() { + if (localization == Localization.UNKNOWN && localeProvider != null && i18nProvider != null) { + logger.trace("updateLocalization(): creating Localization based on locale={},translation={}).", + localeProvider, i18nProvider); + localization = new Localization(localeProvider, i18nProvider); + } + } + /** * Constructor *

    @@ -68,6 +82,20 @@ public VeluxDiscoveryService() { logger.trace("VeluxDiscoveryService(without Bridge) just initialized."); } + @Reference + protected void setLocaleProvider(final LocaleProvider givenLocaleProvider) { + logger.trace("setLocaleProvider(): provided locale={}.", givenLocaleProvider); + localeProvider = givenLocaleProvider; + updateLocalization(); + } + + @Reference + protected void setTranslationProvider(TranslationProvider givenI18nProvider) { + logger.trace("setTranslationProvider(): provided translation={}.", givenI18nProvider); + i18nProvider = givenI18nProvider; + updateLocalization(); + } + /** * Constructor *

    @@ -231,10 +259,10 @@ private void discoverProducts() { ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, label); DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID) - .withProperty(VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER, identifier) + .withProperty(VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER, identifier) .withProperty(VeluxBindingProperties.PROPERTY_ACTUATOR_NAME, actuatorName) .withProperty(VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED, isInverted) - .withRepresentationProperty(VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER) + .withRepresentationProperty(VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER) .withBridge(bridgeUID).withLabel(actuatorName).build(); logger.debug("discoverProducts(): registering new thing {}.", discoveryResult); thingDiscovered(discoveryResult); diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/package-info.java index e72cf80c88a5f..bdfa9e4337563 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/package-info.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -11,7 +11,8 @@ * SPDX-License-Identifier: EPL-2.0 */ /** - * Classes for handling discovery of Velux devices beyond the Velux gateway. + * Classes to provide the discovery of things automatically and user-initiated after defining a valid Velux bridge + * configuration. * * @author Guenther Schreiner - Initial contribution */ diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxHandlerFactory.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/factory/VeluxHandlerFactory.java similarity index 68% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxHandlerFactory.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/factory/VeluxHandlerFactory.java index a8732421877cf..4fe4a637f86b6 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxHandlerFactory.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/factory/VeluxHandlerFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -10,10 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.internal; +package org.openhab.binding.velux.internal.factory; +import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; +import java.util.Map; import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -24,9 +26,11 @@ import org.eclipse.smarthome.core.thing.Bridge; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; import org.eclipse.smarthome.core.thing.binding.ThingHandler; import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.openhab.binding.velux.internal.VeluxBindingConstants; import org.openhab.binding.velux.internal.discovery.VeluxDiscoveryService; import org.openhab.binding.velux.internal.handler.VeluxBindingHandler; import org.openhab.binding.velux.internal.handler.VeluxBridgeHandler; @@ -52,26 +56,34 @@ public class VeluxHandlerFactory extends BaseThingHandlerFactory { // Class internal - private @Nullable ServiceRegistration discoveryServiceReg; + private final Map> discoveryServiceRegistrations = new HashMap<>(); + private Set veluxBindingHandlers = new HashSet(); private Set veluxBridgeHandlers = new HashSet(); private Set veluxHandlers = new HashSet(); - private Localization localization; + private @NonNullByDefault({}) LocaleProvider localeProvider; + private @NonNullByDefault({}) TranslationProvider i18nProvider; + private Localization localization = Localization.UNKNOWN; // Private private void registerDeviceDiscoveryService(VeluxBridgeHandler bridgeHandler) { VeluxDiscoveryService discoveryService = new VeluxDiscoveryService(bridgeHandler, localization); - discoveryServiceReg = bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, - new Hashtable()); + ServiceRegistration discoveryServiceReg = bundleContext.registerService(DiscoveryService.class.getName(), + discoveryService, new Hashtable()); + discoveryServiceRegistrations.put(bridgeHandler.getThing().getUID(), discoveryServiceReg); + } - private synchronized void unregisterDeviceDiscoveryService() { - logger.trace("unregisterDeviceDiscoveryService() called."); - if (discoveryServiceReg != null) { - discoveryServiceReg.unregister(); - discoveryServiceReg = null; + // Even if the compiler tells, that the value of cannot be null, it is possible! + // Therefore a @SuppressWarnings("null") is needed to suppress the warning + @SuppressWarnings("null") + private synchronized void unregisterDeviceDiscoveryService(ThingUID thingUID) { + logger.trace("unregisterDeviceDiscoveryService({}) called.", thingUID); + ServiceRegistration remove = discoveryServiceRegistrations.remove(thingUID); + if (remove != null) { + remove.unregister(); } } @@ -103,12 +115,37 @@ private void updateBindingState() { }); } + private void updateLocalization() { + if (localization == Localization.UNKNOWN && localeProvider != null && i18nProvider != null) { + logger.trace("updateLocalization(): creating Localization based on locale={},translation={}).", + localeProvider, i18nProvider); + localization = new Localization(localeProvider, i18nProvider); + } + } + // Constructor @Activate - public VeluxHandlerFactory(final @Reference LocaleProvider localeProvider, - final @Reference TranslationProvider i18nProvider) { - this.localization = new Localization(localeProvider, i18nProvider); + public VeluxHandlerFactory(final @Reference LocaleProvider givenLocaleProvider, + final @Reference TranslationProvider givenI18nProvider) { + logger.trace("VeluxHandlerFactory(locale={},translation={}) called.", givenLocaleProvider, givenI18nProvider); + localeProvider = givenLocaleProvider; + i18nProvider = givenI18nProvider; + + } + + @Reference + protected void setLocaleProvider(final LocaleProvider givenLocaleProvider) { + logger.trace("setLocaleProvider(): provided locale={}.", givenLocaleProvider); + localeProvider = givenLocaleProvider; + updateLocalization(); + } + + @Reference + protected void setTranslationProvider(TranslationProvider givenI18nProvider) { + logger.trace("setTranslationProvider(): provided translation={}.", givenI18nProvider); + i18nProvider = givenI18nProvider; + updateLocalization(); } // Utility methods @@ -147,21 +184,19 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { @Override protected void removeHandler(ThingHandler thingHandler) { - ThingTypeUID thingTypeUID = thingHandler.getThing().getThingTypeUID(); - // Handle Binding removal - if (VeluxBindingConstants.SUPPORTED_THINGS_BINDING.contains(thingTypeUID)) { + if (thingHandler instanceof VeluxBindingHandler) { logger.trace("removeHandler() removing information element '{}'.", thingHandler.toString()); veluxBindingHandlers.remove(thingHandler); } else // Handle Bridge removal - if (VeluxBindingConstants.SUPPORTED_THINGS_BRIDGE.contains(thingTypeUID)) { + if (thingHandler instanceof VeluxBridgeHandler) { logger.trace("removeHandler() removing bridge '{}'.", thingHandler.toString()); veluxBridgeHandlers.remove(thingHandler); - unregisterDeviceDiscoveryService(); + unregisterDeviceDiscoveryService(thingHandler.getThing().getUID()); } else // Handle removal of Things behind the Bridge - if (VeluxBindingConstants.SUPPORTED_THINGS_ITEMS.contains(thingTypeUID)) { + if (thingHandler instanceof VeluxHandler) { logger.trace("removeHandler() removing thing '{}'.", thingHandler.toString()); veluxHandlers.remove(thingHandler); } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/factory/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/factory/package-info.java new file mode 100644 index 0000000000000..a5a7fd46c35d1 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/factory/package-info.java @@ -0,0 +1,18 @@ +/** + * 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 + */ +/** + * Classes to provide the initiation of all handlers i.e. the factory for all services and things. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.internal.factory; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/BridgeChannels.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/BridgeChannels.java index f63adcde8aee0..2496b2e0fdf14 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/BridgeChannels.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/BridgeChannels.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -19,6 +19,7 @@ import org.eclipse.smarthome.core.thing.Channel; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.Thing; +import org.openhab.binding.velux.internal.handler.utils.ExtendedBaseBridgeHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorLimitation.java index 2b8f6b4ae20c5..27a9e5d39b637 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorLimitation.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorLimitation.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -21,6 +21,7 @@ import org.openhab.binding.velux.internal.VeluxBindingProperties; import org.openhab.binding.velux.internal.bridge.VeluxBridgeGetLimitation; import org.openhab.binding.velux.internal.bridge.VeluxBridgeSetLimitation; +import org.openhab.binding.velux.internal.handler.utils.ThingConfiguration; import org.openhab.binding.velux.internal.things.VeluxProduct; import org.openhab.binding.velux.internal.things.VeluxProductPosition; import org.openhab.binding.velux.internal.things.VeluxProductSerialNo; @@ -77,21 +78,21 @@ private ChannelActuatorLimitation() { if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { LOGGER.trace("handleRefresh(): there are some existing products."); } - if (!ThingProperty.exists(thisBridgeHandler, channelUID, - VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER)) { + if (!ThingConfiguration.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER)) { LOGGER.trace("handleRefresh(): aborting processing as {} is not set.", - VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER); + VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER); break; } - String actuatorSerial = (String) ThingProperty.getValue(thisBridgeHandler, channelUID, - VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER); + String actuatorSerial = (String) ThingConfiguration.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER); LOGGER.trace("handleRefresh(): actuatorSerial={}", actuatorSerial); // Handle value inversion boolean propertyInverted = false; - if (ThingProperty.exists(thisBridgeHandler, channelUID, + if (ThingConfiguration.exists(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) { - propertyInverted = (boolean) ThingProperty.getValue(thisBridgeHandler, channelUID, + propertyInverted = (boolean) ThingConfiguration.getValue(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED); } boolean isInverted = propertyInverted || VeluxProductSerialNo.indicatesRevertedValues(actuatorSerial); @@ -154,21 +155,21 @@ private ChannelActuatorLimitation() { if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { LOGGER.trace("handleCommand(): there are some existing products."); } - if (!ThingProperty.exists(thisBridgeHandler, channelUID, - VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER)) { + if (!ThingConfiguration.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER)) { LOGGER.trace("handleCommand(): aborting processing as {} is not set.", - VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER); + VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER); break; } - String actuatorSerial = (String) ThingProperty.getValue(thisBridgeHandler, channelUID, - VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER); + String actuatorSerial = (String) ThingConfiguration.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER); LOGGER.trace("handleCommand(): actuatorSerial={}", actuatorSerial); // Handle value inversion boolean propertyInverted = false; - if (ThingProperty.exists(thisBridgeHandler, channelUID, + if (ThingConfiguration.exists(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) { - propertyInverted = (boolean) ThingProperty.getValue(thisBridgeHandler, channelUID, + propertyInverted = (boolean) ThingConfiguration.getValue(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED); } boolean isInverted = propertyInverted || VeluxProductSerialNo.indicatesRevertedValues(actuatorSerial); @@ -190,18 +191,15 @@ private ChannelActuatorLimitation() { } LOGGER.trace("handleCommand(): found command of type PercentType."); - PercentType ptCommand = (PercentType) command; - if (isInverted) { - ptCommand = new PercentType(PercentType.HUNDRED.intValue() - ptCommand.intValue()); - } - LOGGER.trace("handleCommand(): found command to set level to {}.", ptCommand); + VeluxProductPosition posCommand = new VeluxProductPosition((PercentType) command, isInverted); + LOGGER.trace("handleCommand(): found command to set level to {}.", posCommand); if (setMinimum) { new VeluxBridgeSetLimitation().setMinimumLimitation(thisBridgeHandler.thisBridge, - thisProduct.getBridgeProductIndex().toInt(), ptCommand.intValue()); + thisProduct.getBridgeProductIndex().toInt(), posCommand); } else { new VeluxBridgeSetLimitation().setMaximumLimitation(thisBridgeHandler.thisBridge, - thisProduct.getBridgeProductIndex().toInt(), ptCommand.intValue()); + thisProduct.getBridgeProductIndex().toInt(), posCommand); } } while (false); // common exit LOGGER.trace("handleCommand() returns {}.", newValue); diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorPosition.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorPosition.java index 0a85953a9a7a7..6b3134ca16dc6 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorPosition.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorPosition.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -23,12 +23,10 @@ import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.State; -import org.openhab.binding.velux.internal.VeluxBindingProperties; import org.openhab.binding.velux.internal.bridge.VeluxBridgeRunProductCommand; import org.openhab.binding.velux.internal.bridge.common.GetProduct; -import org.openhab.binding.velux.internal.things.VeluxProduct; +import org.openhab.binding.velux.internal.handler.utils.Thing2VeluxActuator; import org.openhab.binding.velux.internal.things.VeluxProductPosition; -import org.openhab.binding.velux.internal.things.VeluxProductSerialNo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -53,17 +51,17 @@ final class ChannelActuatorPosition extends ChannelHandlerTemplate { private static final Logger LOGGER = LoggerFactory.getLogger(ChannelActuatorPosition.class); - /* - * ************************ - * ***** Constructors ***** - */ - - // Suppress default constructor for non-instantiability + // Constructors + /** + * Suppress default constructor for non-instantiability. + */ private ChannelActuatorPosition() { throw new AssertionError(); } + // Public methods + /** * Communication method to retrieve information to update the channel value. * @@ -81,58 +79,25 @@ private ChannelActuatorPosition() { if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { LOGGER.trace("handleRefresh(): there are some existing products."); } - if (!ThingProperty.exists(thisBridgeHandler, channelUID, - VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER)) { - LOGGER.trace("handleRefresh(): aborting processing as {} is not set.", - VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER); - break; - } - String actuatorSerial = (String) ThingProperty.getValue(thisBridgeHandler, channelUID, - VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER); - LOGGER.trace("handleRefresh(): actuatorSerial={}", actuatorSerial); - - // Handle value inversion - boolean propertyInverted = false; - if (ThingProperty.exists(thisBridgeHandler, channelUID, - VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) { - propertyInverted = (boolean) ThingProperty.getValue(thisBridgeHandler, channelUID, - VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED); - } - boolean isInverted = propertyInverted || VeluxProductSerialNo.indicatesRevertedValues(actuatorSerial); - LOGGER.trace("handleRefresh(): isInverted={}.", isInverted); - actuatorSerial = VeluxProductSerialNo.cleaned(actuatorSerial); - - if (!thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts - .isRegistered(actuatorSerial)) { - LOGGER.info("handleRefresh(): cannot work on unknown actuator with serial {}.", actuatorSerial); - break; - } - LOGGER.trace("handleRefresh(): fetching actuator for {}.", actuatorSerial); - VeluxProduct thisProduct = thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts - .get(actuatorSerial); - LOGGER.trace("handleRefresh(): found actuator {}.", thisProduct); - + Thing2VeluxActuator veluxActuator = thisBridgeHandler.channel2VeluxActuator.get(channelUID); GetProduct bcp = thisBridgeHandler.thisBridge.bridgeAPI().getProduct(); if (bcp == null) { LOGGER.trace("handleRefresh(): aborting processing as handler is null."); break; } - bcp.setProductId(thisProduct.getBridgeProductIndex().toInt()); - if (thisBridgeHandler.thisBridge.bridgeCommunicate(bcp)) { - thisProduct = bcp.getProduct(); - if (bcp.isCommunicationSuccessful()) { - try { - VeluxProductPosition position = new VeluxProductPosition(thisProduct.getCurrentPosition()); - if (position.isValid()) { - PercentType positionAsPercent = position.getPositionAsPercentType(isInverted); - LOGGER.trace("handleRefresh(): found actuator at level {}.", positionAsPercent); - newState = positionAsPercent; - } else { - LOGGER.trace("handleRefresh(): level of actuator is unknown."); - } - } catch (Exception e) { - LOGGER.warn("handleRefresh(): getProducts() exception: {}.", e.getMessage()); + bcp.setProductId(veluxActuator.getProductBridgeIndex().toInt()); + if (thisBridgeHandler.thisBridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + try { + VeluxProductPosition position = new VeluxProductPosition(bcp.getProduct().getCurrentPosition()); + if (position.isValid()) { + PercentType positionAsPercent = position.getPositionAsPercentType(veluxActuator.isInverted()); + LOGGER.trace("handleRefresh(): found actuator at level {}.", positionAsPercent); + newState = positionAsPercent; + } else { + LOGGER.trace("handleRefresh(): level of actuator is unknown."); } + } catch (Exception e) { + LOGGER.warn("handleRefresh(): getProducts() exception: {}.", e.getMessage()); } } } while (false); // common exit @@ -159,46 +124,16 @@ private ChannelActuatorPosition() { if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { LOGGER.trace("handleCommand(): there are some existing products."); } - if (!ThingProperty.exists(thisBridgeHandler, channelUID, - VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER)) { - LOGGER.trace("handleCommand(): aborting processing as actuatorSerial is not set."); - break; - } - String actuatorSerial = (String) ThingProperty.getValue(thisBridgeHandler, channelUID, - VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER); - LOGGER.trace("handleCommand(): actuatorSerial={}", actuatorSerial); - - // Handle value inversion - boolean propertyInverted = false; - if (ThingProperty.exists(thisBridgeHandler, channelUID, - VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) { - propertyInverted = (boolean) ThingProperty.getValue(thisBridgeHandler, channelUID, - VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED); - } - boolean isInverted = propertyInverted || VeluxProductSerialNo.indicatesRevertedValues(actuatorSerial); - LOGGER.trace("handleCommand(): isInverted={}.", isInverted); - actuatorSerial = VeluxProductSerialNo.cleaned(actuatorSerial); - - if (!thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts - .isRegistered(actuatorSerial)) { - LOGGER.info("handleCommand({},{}): cannot work on unknown actuator: {}.", channelUID.getAsString(), - command, actuatorSerial); - break; - } - LOGGER.trace("handleCommand(): fetching product for {}.", actuatorSerial); - VeluxProduct thisProduct = thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts - .get(actuatorSerial); - LOGGER.trace("handleCommand(): found product {}.", thisProduct); - + Thing2VeluxActuator veluxActuator = thisBridgeHandler.channel2VeluxActuator.get(channelUID); VeluxProductPosition targetLevel = VeluxProductPosition.UNKNOWN; if (channelId.equals(CHANNEL_ACTUATOR_POSITION)) { if ((command instanceof UpDownType) && (command == UpDownType.UP)) { LOGGER.trace("handleCommand(): found UP command."); - targetLevel = isInverted ? new VeluxProductPosition(PercentType.HUNDRED) + targetLevel = veluxActuator.isInverted() ? new VeluxProductPosition(PercentType.HUNDRED) : new VeluxProductPosition(PercentType.ZERO); } else if ((command instanceof UpDownType) && (command == UpDownType.DOWN)) { LOGGER.trace("handleCommand(): found DOWN command."); - targetLevel = isInverted ? new VeluxProductPosition(PercentType.ZERO) + targetLevel = veluxActuator.isInverted() ? new VeluxProductPosition(PercentType.ZERO) : new VeluxProductPosition(PercentType.HUNDRED); } else if ((command instanceof StopMoveType) && (command == StopMoveType.STOP)) { LOGGER.trace("handleCommand(): found STOP command."); @@ -206,7 +141,7 @@ private ChannelActuatorPosition() { } else if (command instanceof PercentType) { LOGGER.trace("handleCommand(): found command of type PercentType."); PercentType ptCommand = (PercentType) command; - if (isInverted) { + if (veluxActuator.isInverted()) { ptCommand = new PercentType(PercentType.HUNDRED.intValue() - ptCommand.intValue()); } LOGGER.trace("handleCommand(): found command to set level to {}.", ptCommand); @@ -218,11 +153,11 @@ private ChannelActuatorPosition() { } else { if ((command instanceof OnOffType) && (command == OnOffType.ON)) { LOGGER.trace("handleCommand(): found ON command."); - targetLevel = isInverted ? new VeluxProductPosition(PercentType.HUNDRED) + targetLevel = veluxActuator.isInverted() ? new VeluxProductPosition(PercentType.HUNDRED) : new VeluxProductPosition(PercentType.ZERO); } else if ((command instanceof OnOffType) && (command == OnOffType.OFF)) { LOGGER.trace("handleCommand(): found OFF command."); - targetLevel = isInverted ? new VeluxProductPosition(PercentType.ZERO) + targetLevel = veluxActuator.isInverted() ? new VeluxProductPosition(PercentType.ZERO) : new VeluxProductPosition(PercentType.HUNDRED); } else { LOGGER.info("handleCommand({},{}): ignoring command.", channelUID.getAsString(), command); @@ -231,7 +166,7 @@ private ChannelActuatorPosition() { } LOGGER.debug("handleCommand(): sending command with target level {}.", targetLevel); new VeluxBridgeRunProductCommand().sendCommand(thisBridgeHandler.thisBridge, - thisProduct.getBridgeProductIndex().toInt(), targetLevel); + veluxActuator.getProductBridgeIndex().toInt(), targetLevel); LOGGER.trace("handleCommand(): The new shutter level will be send through the home monitoring events."); if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeCheck.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeCheck.java index 10442c522f2a7..b25dbb02947c3 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeCheck.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeCheck.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -20,6 +20,8 @@ import org.eclipse.smarthome.core.types.State; import org.openhab.binding.velux.internal.VeluxBindingProperties; import org.openhab.binding.velux.internal.VeluxItemType; +import org.openhab.binding.velux.internal.handler.utils.StateUtils; +import org.openhab.binding.velux.internal.handler.utils.ThingConfiguration; import org.openhab.binding.velux.internal.things.VeluxScene; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -78,12 +80,12 @@ private ChannelBridgeCheck() { LOGGER.trace("handleCommand(): ignoring non SCENE_ACTION."); continue; } - if (!ThingProperty.exists(thisBridgeHandler, channelUID, + if (!ThingConfiguration.exists(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_SCENE_NAME)) { LOGGER.trace("handleCommand(): aborting processing as scene name is not set."); break; } - String sceneName = (String) ThingProperty.getValue(thisBridgeHandler, thisChannelUID, + String sceneName = (String) ThingConfiguration.getValue(thisBridgeHandler, thisChannelUID, VeluxBindingProperties.PROPERTY_SCENE_NAME); LOGGER.trace("handleCommand(): comparing {} with {}.", scene.getName().toString(), sceneName); if (scene.getName().toString().equals(sceneName)) { @@ -99,8 +101,8 @@ private ChannelBridgeCheck() { } String result; if (unusedScenes.size() > 0) { - result = thisBridgeHandler.localization.getText("channelValue.check-integrity-failed", - unusedScenes.toString()); + result = thisBridgeHandler.localization.getText("channelValue.check-integrity-failed") + .concat(unusedScenes.toString()); } else { result = thisBridgeHandler.localization.getText("channelValue.check-integrity-ok"); } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeDoDetection.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeDoDetection.java index 53934ffc256f1..1617df3c13086 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeDoDetection.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeDoDetection.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeFirmware.java index 8b5625fa9ecf0..40a89c8460003 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeFirmware.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeFirmware.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeLANconfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeLANconfig.java index 45db44d3b33d8..9cc6767ede369 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeLANconfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeLANconfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -19,6 +19,8 @@ import org.openhab.binding.velux.internal.VeluxBindingConstants; import org.openhab.binding.velux.internal.VeluxItemType; import org.openhab.binding.velux.internal.bridge.VeluxBridgeLANConfig; +import org.openhab.binding.velux.internal.handler.utils.StateUtils; +import org.openhab.binding.velux.internal.handler.utils.ThingProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -72,22 +74,22 @@ private ChannelBridgeLANconfig() { case BRIDGE_IPADDRESS: newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABipAddress); ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.CHANNEL_BRIDGE_IPADDRESS, - thisBridgeHandler.bridgeParameters.lanConfig.openHABipAddress); + thisBridgeHandler.bridgeParameters.lanConfig.openHABipAddress.toString()); break; case BRIDGE_SUBNETMASK: newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABsubnetMask); ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.CHANNEL_BRIDGE_SUBNETMASK, - thisBridgeHandler.bridgeParameters.lanConfig.openHABsubnetMask); + thisBridgeHandler.bridgeParameters.lanConfig.openHABsubnetMask.toString()); break; case BRIDGE_DEFAULTGW: newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABdefaultGW); ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.CHANNEL_BRIDGE_DEFAULTGW, - thisBridgeHandler.bridgeParameters.lanConfig.openHABdefaultGW); + thisBridgeHandler.bridgeParameters.lanConfig.openHABdefaultGW.toString()); break; case BRIDGE_DHCP: newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABenabledDHCP); ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.CHANNEL_BRIDGE_DHCP, - thisBridgeHandler.bridgeParameters.lanConfig.openHABenabledDHCP); + thisBridgeHandler.bridgeParameters.lanConfig.openHABenabledDHCP.toString()); default: } } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeProducts.java index cad458c273eef..4abe789afa620 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeProducts.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeProducts.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 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.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.internal.handler.utils.StateUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeScenes.java index 093b9053e051d..126306b891aff 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeScenes.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeScenes.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 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.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.internal.handler.utils.StateUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeStatus.java index cc96d02f849c1..22f59948e5f53 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeStatus.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeStatus.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeWLANconfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeWLANconfig.java index b37b4c9e5068f..36380e554d638 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeWLANconfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeWLANconfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -19,6 +19,8 @@ import org.openhab.binding.velux.internal.VeluxBindingConstants; import org.openhab.binding.velux.internal.VeluxItemType; import org.openhab.binding.velux.internal.bridge.VeluxBridgeWLANConfig; +import org.openhab.binding.velux.internal.handler.utils.StateUtils; +import org.openhab.binding.velux.internal.handler.utils.ThingProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -71,12 +73,12 @@ private ChannelBridgeWLANconfig() { case BRIDGE_WLANSSID: newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABipAddress); ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.CHANNEL_BRIDGE_WLANSSID, - thisBridgeHandler.bridgeParameters.wlanConfig.openHABwlanSSID); + thisBridgeHandler.bridgeParameters.wlanConfig.openHABwlanSSID.toString()); break; case BRIDGE_WLANPASSWORD: newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABsubnetMask); ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.CHANNEL_BRIDGE_WLANPASSWORD, - thisBridgeHandler.bridgeParameters.wlanConfig.openHABwlanPassword); + thisBridgeHandler.bridgeParameters.wlanConfig.openHABwlanPassword.toString()); break; default: } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelHandlerTemplate.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelHandlerTemplate.java index a6d4a215850de..6f23e63462f58 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelHandlerTemplate.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelHandlerTemplate.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneAction.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneAction.java index 11a5cbb12106e..07bc016d3dafd 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneAction.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneAction.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -19,6 +19,7 @@ import org.eclipse.smarthome.core.types.Command; import org.openhab.binding.velux.internal.VeluxBindingProperties; import org.openhab.binding.velux.internal.bridge.VeluxBridgeRunScene; +import org.openhab.binding.velux.internal.handler.utils.ThingConfiguration; import org.openhab.binding.velux.internal.things.VeluxProductVelocity; import org.openhab.binding.velux.internal.things.VeluxScene; import org.openhab.binding.velux.internal.things.VeluxScene.SceneName; @@ -86,11 +87,11 @@ private ChannelSceneAction() { LOGGER.trace("handleCommand(): ignoring OFF command."); break; } - if (!ThingProperty.exists(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_SCENE_NAME)) { + if (!ThingConfiguration.exists(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_SCENE_NAME)) { LOGGER.trace("handleCommand(): aborting processing as scene name is not set."); break; } - String sceneName = (String) ThingProperty.getValue(thisBridgeHandler, channelUID, + String sceneName = (String) ThingConfiguration.getValue(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_SCENE_NAME); if (!thisBridgeHandler.bridgeParameters.scenes.getChannel().existingScenes .isRegistered(new SceneName(sceneName))) { @@ -98,7 +99,7 @@ private ChannelSceneAction() { command, sceneName); break; } - String velocityName = (String) ThingProperty.getValue(thisBridgeHandler, channelUID, + String velocityName = (String) ThingConfiguration.getValue(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_SCENE_VELOCITY); LOGGER.debug("handleCommand(): activating known scene {}.", sceneName); VeluxScene thisScene = thisBridgeHandler.bridgeParameters.scenes.getChannel().existingScenes diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneSilentmode.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneSilentmode.java index f06b3e3d09211..89f3d204ba91a 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneSilentmode.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneSilentmode.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -19,6 +19,7 @@ import org.eclipse.smarthome.core.types.Command; import org.openhab.binding.velux.internal.VeluxBindingProperties; import org.openhab.binding.velux.internal.bridge.VeluxBridgeSetSceneVelocity; +import org.openhab.binding.velux.internal.handler.utils.ThingConfiguration; import org.openhab.binding.velux.internal.things.VeluxScene; import org.openhab.binding.velux.internal.things.VeluxScene.SceneName; import org.slf4j.Logger; @@ -73,11 +74,11 @@ private ChannelSceneSilentmode() { do { // just for common exit assert thisBridgeHandler.bridgeParameters.scenes .getChannel().existingScenes != null : "VeluxBridgeHandler.existingScenes not initialized."; - if (!ThingProperty.exists(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_SCENE_NAME)) { + if (!ThingConfiguration.exists(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_SCENE_NAME)) { LOGGER.trace("handleCommand(): aborting processing as scene name is not set."); break; } - String sceneName = (String) ThingProperty.getValue(thisBridgeHandler, channelUID, + String sceneName = (String) ThingConfiguration.getValue(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_SCENE_NAME); if (!thisBridgeHandler.bridgeParameters.scenes.getChannel().existingScenes .isRegistered(new SceneName(sceneName))) { diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelVShutterPosition.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelVShutterPosition.java index 259a3bc4b97d4..35cdfddad01da 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelVShutterPosition.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelVShutterPosition.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -27,6 +27,8 @@ import org.openhab.binding.velux.internal.VeluxItemType; import org.openhab.binding.velux.internal.VeluxRSBindingConfig; import org.openhab.binding.velux.internal.bridge.VeluxBridgeRunScene; +import org.openhab.binding.velux.internal.handler.utils.ThingConfiguration; +import org.openhab.binding.velux.internal.handler.utils.ThingProperty; import org.openhab.binding.velux.internal.things.VeluxScene; import org.openhab.binding.velux.internal.things.VeluxScene.SceneName; import org.slf4j.Logger; @@ -79,13 +81,13 @@ private ChannelVShutterPosition() { assert (channelId == CHANNEL_VSHUTTER_POSITION); State newState = null; do { // just for common exit - if (!ThingProperty.exists(thisBridgeHandler, channelUID, + if (!ThingConfiguration.exists(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_VSHUTTER_CURRENTLEVEL)) { LOGGER.trace("handleRefresh(): aborting processing as current scene level is not set."); break; } // Don't know why OH2 returns BigDecimal. - BigDecimal rollershutterLevelBC = (BigDecimal) ThingProperty.getValue(thisBridgeHandler, channelUID, + BigDecimal rollershutterLevelBC = (BigDecimal) ThingConfiguration.getValue(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_VSHUTTER_CURRENTLEVEL); int rollershutterLevel = rollershutterLevelBC.intValue(); LOGGER.trace("handleRefresh(): current level is {}.", rollershutterLevel); @@ -110,21 +112,21 @@ private ChannelVShutterPosition() { LOGGER.debug("handleCommand({},{},{},{}) called.", channelUID, channelId, command, thisBridgeHandler); Command newValue = null; // ThingProperty sceneLevels - if (!ThingProperty.exists(thisBridgeHandler, channelUID, + if (!ThingConfiguration.exists(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_VSHUTTER_SCENELEVELS)) { LOGGER.trace("handleCommand(): aborting processing as scene levels are not set."); return newValue; } - String sceneLevels = (String) ThingProperty.getValue(thisBridgeHandler, channelUID, + String sceneLevels = (String) ThingConfiguration.getValue(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_VSHUTTER_SCENELEVELS); // ThingProperty currentLevel - if (!ThingProperty.exists(thisBridgeHandler, channelUID, + if (!ThingConfiguration.exists(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_VSHUTTER_CURRENTLEVEL)) { LOGGER.trace("handleCommand(): aborting processing as current scene level is not set."); return newValue; } // Don't know why OH2 returns BigDecimal. - BigDecimal rollershutterLevelBC = (BigDecimal) ThingProperty.getValue(thisBridgeHandler, channelUID, + BigDecimal rollershutterLevelBC = (BigDecimal) ThingConfiguration.getValue(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_VSHUTTER_CURRENTLEVEL); int currentLevel = rollershutterLevelBC.intValue(); LOGGER.trace("handleCommand(): current level is {}.", currentLevel); @@ -158,7 +160,7 @@ private ChannelVShutterPosition() { LOGGER.trace("handleCommand(): updating level to {}.", currentLevel); ThingProperty.setValue(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_VSHUTTER_CURRENTLEVEL, - thisRSBindingConfig.getLevel()); + thisRSBindingConfig.getLevel().toString()); LOGGER.trace("handleCommand() returns {}.", newValue); return newValue; } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/PropertyHandlerTemplate.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/PropertyHandlerTemplate.java new file mode 100644 index 0000000000000..c9ff9047a0857 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/PropertyHandlerTemplate.java @@ -0,0 +1,50 @@ +/** + * 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.velux.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; + +/** + * Channel-specific retrieval and modification. + *

    + * This class implements the Channel VShutter : + *

      + *
    • Velux bridgeOpenHAB: + *

      + * Information retrieval by method {@link #handleRefresh}.

    • + *
    + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +abstract class PropertyHandlerTemplate { + + /** + * Communication method to retrieve information to update the property value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newValue The value retrieved for the passed channel, or null in case if there is no (new) + * value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + throw new IllegalStateException("handleRefresh hasn't been set up in the subclass"); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ThingProperty.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ThingProperty.java deleted file mode 100644 index abf17a30aa329..0000000000000 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ThingProperty.java +++ /dev/null @@ -1,162 +0,0 @@ -/** - * Copyright (c) 2010-2019 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.velux.internal.handler; - -import java.util.Map; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.core.thing.ChannelUID; -import org.eclipse.smarthome.core.thing.Thing; -import org.eclipse.smarthome.core.thing.ThingUID; -import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/*** - * The class {@link ThingProperty} provides methods for dealing with - * properties. - *
      - *
    • {@link #exists} Check existence of a property,
    • - *
    • {@link #getValue} Returns a property value,
    • - *
    • {@link #setValue} Modifies a property value.
    • - *
    - *

    - * Noninstantiable utility class - *

    - * - * @author Guenther Schreiner - Initial contribution - */ -@NonNullByDefault -public class ThingProperty { - private static final Logger LOGGER = LoggerFactory.getLogger(ThingProperty.class); - - /* - * ************************ - * ***** Constructors ***** - */ - - // Suppress default constructor for non-Instantiability - - private ThingProperty() { - throw new AssertionError(); - } - - /* - * ************************** - * ***** Public Methods ***** - */ - - /** - * Check existence of the property value for the given channel and - * desired propertyName which are defined within VeluxBindingProperties. - *

    - * - * @param bridge which handles the mentioned Things, - * @param channelUID describes the channel to by scrutinized, - * @param propertyName defines the property which is to be evaluated. - * @return exists of type boolean. - */ - static boolean exists(BaseBridgeHandler bridge, ChannelUID channelUID, String propertyName) { - ThingUID channelTUID = channelUID.getThingUID(); - Thing thingOfChannel = bridge.getThingByUID(channelTUID); - boolean exists = false; - if (thingOfChannel == null) { - LOGGER.warn("exists(): Channel {} does not belong to a thing.", channelUID); - } else { - if (thingOfChannel.getConfiguration().get(propertyName) != null) { - exists = true; - } - } - LOGGER.trace("exists({},{}) returns {}.", channelUID, propertyName, exists); - return exists; - } - - /** - * Return the property value of type Object for the given channel and - * desired propertyName which are defined within VeluxBindingProperties. - *

    - * - * @param bridge which handles the mentioned Things, - * @param channelUID describes the channel to by scrutinized, - * @param propertyName defines the property which is to be evaluated. - * @return propertyValue of type {@link Object}. Will return {@code null}, if not found, or if value itself - * is {@code null}. - */ - static Object getValue(BaseBridgeHandler bridge, ChannelUID channelUID, String propertyName) { - ThingUID channelTUID = channelUID.getThingUID(); - Thing thingOfChannel = bridge.getThingByUID(channelTUID); - if (thingOfChannel == null) { - LOGGER.warn("getValue(): Channel {} does not belong to a thing.", channelUID); - return true; - } - Object propertyValue = thingOfChannel.getConfiguration().get(propertyName); - LOGGER.trace("getValue({},{}) returns {}.", channelUID, propertyName, propertyValue); - return propertyValue; - } - - /** - * Modifies the property value for the givenpropertyName which are defined within - * VeluxBindingProperties. - *

    - * - * @param thing with property will be modified, - * @param propertyName defines the property which is to be modified. - * @param propertyValue defines the new property value. - */ - public static void setValue(Thing thing, String propertyName, Object propertyValue) { - thing.setProperty(propertyName, propertyValue.toString()); - LOGGER.trace("setValue() {} set to {}.", propertyName, propertyValue); - return; - } - - /** - * Modifies the property value for the given bridge and desired propertyName which are defined within - * VeluxBindingProperties. - *

    - * - * @param bridgeHandler which contains the properties, - * @param channelUID describes the channel to by scrutinized, - * @param propertyName defines the property which is to be modified. - * @param propertyValue defines the new property value. - */ - static void setValue(ExtendedBaseBridgeHandler bridgeHandler, ChannelUID channelUID, String propertyName, - Object propertyValue) { - ThingUID channelTUID = channelUID.getThingUID(); - Thing thingOfChannel = bridgeHandler.getThingByUID(channelTUID); - if (thingOfChannel == null) { - LOGGER.warn("setValue(): Channel {} does not belong to a thing.", channelUID); - return; - } - thingOfChannel.setProperty(propertyName, propertyValue.toString()); - LOGGER.trace("setValue() {} set to {}.", propertyName, propertyValue); - return; - } - - /** - * Modifies the property value for the given bridge and desired propertyName which are defined within - * VeluxBindingProperties. - *

    - * - * @param bridgeHandler which contains the properties, - * @param propertyName defines the property which is to be modified. - * @param propertyValue defines the new property value. - */ - static void setValue(ExtendedBaseBridgeHandler bridgeHandler, String propertyName, Object propertyValue) { - Map properties = bridgeHandler.editProperties(); - properties.put(propertyName, propertyValue.toString()); - bridgeHandler.updateProperties(properties); - LOGGER.trace("setValue() {} set to {}.", propertyName, propertyValue); - return; - } - -} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBindingHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBindingHandler.java index 34a2741f22b94..fae5a31ea9c36 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBindingHandler.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBindingHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -19,12 +19,14 @@ import org.eclipse.smarthome.core.thing.ThingStatus; import org.eclipse.smarthome.core.thing.ThingStatusDetail; import org.eclipse.smarthome.core.thing.ThingTypeUID; -import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.RefreshType; import org.eclipse.smarthome.core.types.State; import org.openhab.binding.velux.internal.VeluxBindingProperties; import org.openhab.binding.velux.internal.VeluxItemType; +import org.openhab.binding.velux.internal.handler.utils.ExtendedBaseThingHandler; +import org.openhab.binding.velux.internal.handler.utils.StateUtils; +import org.openhab.binding.velux.internal.handler.utils.ThingProperty; import org.openhab.binding.velux.internal.utils.Localization; import org.openhab.binding.velux.internal.utils.ManifestInformation; import org.slf4j.Logger; @@ -33,7 +35,7 @@ /*** * The class is responsible for representing the overall status of the Velux binding. *

    - * Beside the normal thing handling introduced by {@link BaseThingHandler}, it provides a method: + * Beside the normal thing handling introduced by {@link ExtendedBaseThingHandler}, it provides a method: *

      *
    • {@link #updateBindingState} to enable other classes to modify the number of activated Velux bridges and * Things.
    • @@ -119,8 +121,10 @@ private void updateVisibleInformation() { logger.trace("updateVisibleInformation(): updating properties."); ThingProperty.setValue(thing, VeluxBindingProperties.PROPERTY_BINDING_BUNDLEVERSION, ManifestInformation.getBundleVersion()); - ThingProperty.setValue(thing, VeluxBindingProperties.PROPERTY_BINDING_NOOFBRIDGES, currentNumberOfBridges); - ThingProperty.setValue(thing, VeluxBindingProperties.PROPERTY_BINDING_NOOFTHINGS, currentNumberOfThings); + ThingProperty.setValue(thing, VeluxBindingProperties.PROPERTY_BINDING_NOOFBRIDGES, + currentNumberOfBridges.toString()); + ThingProperty.setValue(thing, VeluxBindingProperties.PROPERTY_BINDING_NOOFTHINGS, + currentNumberOfThings.toString()); // BaseThingHandler is sensitive during initialization phase. Therefore, to avoid (wrong) warnings about: // "tried updating the thing status although the handler was already disposed." @@ -150,7 +154,11 @@ public void initialize() { // The framework requires you to return from this method quickly. // Setting the thing status to UNKNOWN temporarily and let the background task decide for the real status. updateStatus(ThingStatus.UNKNOWN); - + // Take care of unusual situations... + if (scheduler.isShutdown()) { + logger.warn("initialize(): scheduler is shutdown, aborting the initialization of this bridge."); + return; + } logger.trace("initialize(): preparing background initialization task."); // Background initialization... scheduler.execute(() -> { diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBridgeHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBridgeHandler.java index c4405b4e0c743..94cb40133b40f 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBridgeHandler.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBridgeHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -12,6 +12,7 @@ */ package org.openhab.binding.velux.internal.handler; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -24,17 +25,14 @@ import org.eclipse.smarthome.core.library.types.PercentType; import org.eclipse.smarthome.core.thing.Bridge; 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.ThingStatusDetail; import org.eclipse.smarthome.core.thing.ThingTypeUID; -import org.eclipse.smarthome.core.thing.binding.ThingHandler; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.RefreshType; import org.eclipse.smarthome.core.types.State; import org.openhab.binding.velux.internal.VeluxBinding; import org.openhab.binding.velux.internal.VeluxBindingConstants; -import org.openhab.binding.velux.internal.VeluxBindingProperties; import org.openhab.binding.velux.internal.VeluxItemType; import org.openhab.binding.velux.internal.bridge.VeluxBridge; import org.openhab.binding.velux.internal.bridge.VeluxBridgeActuators; @@ -53,11 +51,14 @@ import org.openhab.binding.velux.internal.bridge.slip.SlipVeluxBridge; import org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration; import org.openhab.binding.velux.internal.development.Threads; +import org.openhab.binding.velux.internal.handler.utils.ExtendedBaseBridgeHandler; +import org.openhab.binding.velux.internal.handler.utils.Thing2VeluxActuator; +import org.openhab.binding.velux.internal.handler.utils.ThingProperty; import org.openhab.binding.velux.internal.things.VeluxExistingProducts; import org.openhab.binding.velux.internal.things.VeluxExistingScenes; import org.openhab.binding.velux.internal.things.VeluxProduct; +import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex; import org.openhab.binding.velux.internal.things.VeluxProductPosition; -import org.openhab.binding.velux.internal.things.VeluxProductSerialNo; import org.openhab.binding.velux.internal.utils.Localization; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -86,10 +87,7 @@ public class VeluxBridgeHandler extends ExtendedBaseBridgeHandler implements VeluxBridgeInstance, VeluxBridgeProvider { private final Logger logger = LoggerFactory.getLogger(VeluxBridgeHandler.class); - /* - * *************************** - * ***** Private Objects ***** - */ + // Class internal /** * Scheduler for continuous refresh by scheduleWithFixedDelay. @@ -104,7 +102,7 @@ public class VeluxBridgeHandler extends ExtendedBaseBridgeHandler implements Vel /** * Dedicated thread pool for the long-running bridge communication threads. */ - private final ScheduledExecutorService handleScheduler = ThreadPoolManager + private ScheduledExecutorService handleScheduler = ThreadPoolManager .getScheduledPool(VeluxBindingConstants.BINDING_ID); private VeluxBridge myJsonBridge = new JsonVeluxBridge(this); @@ -116,9 +114,14 @@ public class VeluxBridgeHandler extends ExtendedBaseBridgeHandler implements Vel */ VeluxBridge thisBridge = myJsonBridge; - BridgeParameters bridgeParameters = new BridgeParameters(); + public BridgeParameters bridgeParameters = new BridgeParameters(); Localization localization; + /** + * Mapping from ChannelUID to class Thing2VeluxActuator, which return Velux device information, probably cached. + */ + ConcurrentHashMap channel2VeluxActuator = new ConcurrentHashMap(); + /** * Information retrieved by {@link VeluxBinding#VeluxBinding}. */ @@ -136,10 +139,7 @@ public VeluxBridgeHandler(final Bridge bridge, Localization localization) { logger.debug("Creating a VeluxBridgeHandler for thing '{}'.", getThing().getUID()); } - /* - * *************************** - * ***** Private Classes ***** - */ + // Private classes /** *

      @@ -157,7 +157,7 @@ public VeluxBridgeHandler(final Bridge bridge, Localization localization) { @NonNullByDefault public class BridgeParameters { /** Information retrieved by {@link VeluxBridgeActuators#getProducts} */ - VeluxBridgeActuators actuators = new VeluxBridgeActuators(); + public VeluxBridgeActuators actuators = new VeluxBridgeActuators(); /** Information retrieved by {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeScenes#getScenes} */ VeluxBridgeScenes scenes = new VeluxBridgeScenes(); @@ -175,10 +175,7 @@ public class BridgeParameters { VeluxBridgeWLANConfig.Channel wlanConfig = new VeluxBridgeWLANConfig().getChannel(); } - /* - * *************************** - * ***** Private Methods ***** - */ + // Private methods /** * Provide the ThingType for a given Channel. @@ -194,10 +191,7 @@ ThingTypeUID thingTypeUIDOf(ChannelUID channelUID) { return channelUID.getThingUID().getThingTypeUID(); } - /* - * ***************************************************************** - * ***** Objects and Methods for interface VeluxBridgeInstance ***** - */ + // Objects and Methods for interface VeluxBridgeInstance /** * Information retrieved by ... @@ -223,10 +217,7 @@ public VeluxExistingScenes existingScenes() { return bridgeParameters.scenes.getChannel().existingScenes; } - /* - * ***************************************************************** - * ***** Objects and Methods for interface VeluxBridgeProvider ***** - */ + // Objects and Methods for interface VeluxBridgeProvider ***** @Override public boolean bridgeCommunicate(BridgeCommunicationProtocol communication) { @@ -240,21 +231,25 @@ public boolean bridgeCommunicate(BridgeCommunicationProtocol communication) { return null; } - /* - * ************************************************** - * ******** Provisioning/Deprovisioning methods ***** - */ + // Provisioning/Deprovisioning methods ***** @Override public void initialize() { - logger.debug("initialize() called."); - logger.info("Initializing Velux veluxBridge handler for '{}'.", getThing().getUID()); + logger.info("Initializing Velux Bridge '{}'.", getThing().getUID()); // The framework requires you to return from this method quickly. // Setting the thing status to UNKNOWN temporarily and let the background task decide for the real status. + logger.trace("initialize() called."); updateStatus(ThingStatus.UNKNOWN); - + // Take care of unusual situations... + if (scheduler.isShutdown()) { + logger.warn("initialize(): scheduler is shutdown, aborting the initialization of this bridge."); + return; + } + if (handleScheduler.isShutdown()) { + logger.trace("initialize(): handleScheduler is shutdown, aborting the initialization of this bridge."); + return; + } logger.trace("initialize(): preparing background initialization task."); - // Background initialization... scheduler.execute(() -> { logger.trace("initialize.scheduled(): Further work within scheduler.execute()."); @@ -269,7 +264,7 @@ public void initialize() { try { refreshOpenHAB(); } catch (RuntimeException e) { - logger.warn("Exception occurred during activated refresh scheduler: {}.", e.getMessage()); + logger.warn("Exception occurred during activated refresh scheduler: {}.", e); } }, this.veluxBridgeConfiguration.refreshMSecs, this.veluxBridgeConfiguration.refreshMSecs, TimeUnit.MILLISECONDS); @@ -283,22 +278,23 @@ public void initialize() { */ @Override public synchronized void dispose() { - logger.debug("dispose({}) called.", this); - logger.trace("dispose(): shutting down scheduled bridge I/O tasks."); - handleScheduler.shutdownNow(); - logger.trace("Shutting down Velux veluxBridge '{}'.", getThing().getUID()); + logger.info("Shutting down Velux Bridge '{}'.", getThing().getUID()); + logger.trace("dispose(): shutting down continous refresh."); // Just for avoidance of Potential null pointer access - ScheduledFuture refreshJobX = refreshJob; - if (refreshJobX != null) { + ScheduledFuture currentRefreshJob = refreshJob; + if (currentRefreshJob != null) { logger.trace("dispose(): stopping the refresh."); - refreshJobX.cancel(true); + currentRefreshJob.cancel(true); } - logger.trace("dispose(): initiating logout."); - thisBridge.bridgeLogout(); - logger.trace("dispose(): shutting down JSON bridge."); - myJsonBridge.shutdown(); - logger.trace("dispose(): shutting down SLIP bridge."); - mySlipBridge.shutdown(); + // Background execution of dispose + scheduler.execute(() -> { + logger.trace("dispose.scheduled(): (synchronous) logout initiated."); + thisBridge.bridgeLogout(); + logger.trace("dispose.scheduled(): shutting down JSON bridge."); + myJsonBridge.shutdown(); + logger.trace("dispose.scheduled(): shutting down SLIP bridge."); + mySlipBridge.shutdown(); + }); logger.trace("dispose(): calling super class."); super.dispose(); logger.trace("dispose() done."); @@ -308,19 +304,15 @@ public synchronized void dispose() { * NOTE: It takes care by calling {@link #handleCommand} with the REFRESH command, that every used channel is * initialized. */ - @SuppressWarnings("unused") @Override public void channelLinked(ChannelUID channelUID) { - logger.trace("channelLinked({}) called.", channelUID.getAsString()); - - // TODO ... code eliminated for OH2.5 - if (false) { - if (thing.getStatus() == ThingStatus.ONLINE) { - logger.trace("channelLinked() refreshing channel value with help of handleCommand as Thing is online."); - handleCommand(channelUID, RefreshType.REFRESH); - } else { - logger.trace("channelLinked() doing nothing as Thing is not online."); - } + if (thing.getStatus() == ThingStatus.ONLINE) { + channel2VeluxActuator.put(channelUID, new Thing2VeluxActuator(this, channelUID)); + logger.trace("channelLinked({}) refreshing channel value with help of handleCommand as Thing is online.", + channelUID.getAsString()); + handleCommand(channelUID, RefreshType.REFRESH); + } else { + logger.trace("channelLinked({}) doing nothing as Thing is not online.", channelUID.getAsString()); } } @@ -329,22 +321,7 @@ public void channelUnlinked(ChannelUID channelUID) { logger.trace("channelUnlinked({}) called.", channelUID.getAsString()); } - @Override - public void childHandlerInitialized(ThingHandler childHandler, Thing childThing) { - logger.trace("childHandlerInitialized({},{}) called.", childHandler, childThing); - super.childHandlerInitialized(childHandler, childThing); - } - - @Override - public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) { - logger.trace("childHandlerDisposed({},{}) called.", childHandler, childThing); - super.childHandlerDisposed(childHandler, childThing); - } - - /* - * ************************************** - * ******** Reconfiguration methods ***** - */ + // Reconfiguration methods private void bridgeParamsUpdated() { logger.debug("bridgeParamsUpdated() called."); @@ -387,7 +364,6 @@ private void bridgeParamsUpdated() { bridgeParameters.scenes.getScenes(thisBridge); logger.info("Found {} scenes:\n\t{}", VeluxBindingConstants.BINDING_ID, bridgeParameters.scenes.getChannel().existingScenes.toString(false, "\n\t")); - logger.trace("bridgeParamsUpdated(): Fetching existing actuators/products."); bridgeParameters.actuators.getProducts(thisBridge); logger.info("Found {} actuators:\n\t{}", VeluxBindingConstants.BINDING_ID, @@ -411,14 +387,16 @@ private void bridgeParamsUpdated() { logger.trace("bridgeParamsUpdated() successfully finished."); } - /* - * ************************************************* - * ******** Continuous synchronization methods ***** - */ + // Continuous synchronization methods private synchronized void refreshOpenHAB() { logger.debug("refreshOpenHAB() initiated by {} starting cycle {}.", Thread.currentThread(), refreshCounter); + if (handleScheduler.isShutdown()) { + logger.trace("refreshOpenHAB(): handleScheduler is shutdown, recreating a scheduler pool."); + handleScheduler = ThreadPoolManager.getScheduledPool(VeluxBindingConstants.BINDING_ID); + } + logger.trace("refreshOpenHAB(): processing of possible HSM messages."); // Background execution of bridge related I/O handleScheduler.execute(() -> { @@ -434,10 +412,18 @@ private synchronized void refreshOpenHAB() { "refreshOpenHAB(): looping through all (both child things and bridge) linked channels for a need of refresh."); for (ChannelUID channelUID : BridgeChannels.getAllLinkedChannelUIDs(this)) { if (VeluxItemType.isToBeRefreshedNow(refreshCounter, thingTypeUIDOf(channelUID), channelUID.getId())) { - logger.trace("refreshOpenHAB(): refreshing item {}.", channelUID); + logger.trace("refreshOpenHAB(): refreshing channel {}.", channelUID); handleCommand(channelUID, RefreshType.REFRESH); } } + logger.trace("refreshOpenHAB(): looping through properties for a need of refresh."); + for (VeluxItemType veluxItem : VeluxItemType.getPropertyEntriesByThing(getThing().getThingTypeUID())) { + if (VeluxItemType.isToBeRefreshedNow(refreshCounter, getThing().getThingTypeUID(), + veluxItem.getIdentifier())) { + logger.trace("refreshOpenHAB(): refreshing property {}.", veluxItem.getIdentifier()); + handleCommand(new ChannelUID(getThing().getUID(), veluxItem.getIdentifier()), RefreshType.REFRESH); + } + } logger.debug("refreshOpenHAB() initiated by {} finished cycle {}.", Thread.currentThread(), refreshCounter); refreshCounter++; } @@ -450,57 +436,35 @@ private void syncChannelsWithProducts() { if (!bridgeParameters.actuators.getChannel().existingProducts.isDirty()) { return; } - logger.trace("syncChannelsWithProducts(): existingProducts have changed."); + logger.trace("syncChannelsWithProducts(): there are some existing products with changed parameters."); outer: for (VeluxProduct product : bridgeParameters.actuators.getChannel().existingProducts .valuesOfModified()) { logger.trace("syncChannelsWithProducts(): actuator {} has changed values.", product.getProductName()); + ProductBridgeIndex productPbi = product.getBridgeProductIndex(); + logger.trace("syncChannelsWithProducts(): bridge index is {}.", productPbi); for (ChannelUID channelUID : BridgeChannels.getAllLinkedChannelUIDs(this)) { - String itemName = channelUID.getAsString(); - VeluxItemType itemType = VeluxItemType.getByThingAndChannel(thingTypeUIDOf(channelUID), - channelUID.getId()); - switch (itemType) { - case ACTUATOR_POSITION: - case ACTUATOR_STATE: - case ROLLERSHUTTER_POSITION: - case WINDOW_POSITION: - // found on suitable entry of VeluxItemType. - break; - default: - continue; + if (!channel2VeluxActuator.containsKey(channelUID)) { + logger.trace("syncChannelsWithProducts(): channel {} not found.", channelUID); + continue; } - if (!ThingProperty.exists(this, channelUID, VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER)) { - logger.trace("syncChannelsWithProducts(): aborting processing as actuatorSerial is not set."); - break; + ProductBridgeIndex channelPbi = channel2VeluxActuator.get(channelUID).getProductBridgeIndex(); + if (!channelPbi.equals(productPbi)) { + continue; } - String actuatorSerial = (String) ThingProperty.getValue(this, channelUID, - VeluxBindingProperties.PROPERTY_ACTUATOR_SERIALNUMBER); - // Handle value inversion - boolean propertyInverted = false; - if (ThingProperty.exists(this, channelUID, VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) { - propertyInverted = (boolean) ThingProperty.getValue(this, channelUID, - VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED); - } - boolean isInverted = propertyInverted || VeluxProductSerialNo.indicatesRevertedValues(actuatorSerial); - logger.trace("syncChannelsWithProducts(): isInverted={}.", isInverted); - actuatorSerial = VeluxProductSerialNo.cleaned(actuatorSerial); - - logger.trace("syncChannelsWithProducts(): working on actuatorSerial {}.", actuatorSerial); - if (product.getSerialNumber().equals(actuatorSerial)) { - logger.trace("syncChannelsWithProducts(): product {}/{} used within item {}.", - product.getProductName(), product.getSerialNumber(), itemName); - VeluxProductPosition position = new VeluxProductPosition(product.getCurrentPosition()); - if (position.isValid()) { - PercentType positionAsPercent = position.getPositionAsPercentType(isInverted); - logger.debug("syncChannelsWithProducts(): updating item {} to position {}%.", itemName, - positionAsPercent); - updateState(channelUID, positionAsPercent); - } else { - logger.trace("syncChannelsWithProducts(): update of item {} to position {} skipped.", itemName, - position); - } - break outer; + boolean isInverted = channel2VeluxActuator.get(channelUID).isInverted(); + logger.trace("syncChannelsWithProducts(): isInverted is {}.", isInverted); + VeluxProductPosition position = new VeluxProductPosition(product.getCurrentPosition()); + if (position.isValid()) { + PercentType positionAsPercent = position.getPositionAsPercentType(isInverted); + logger.debug("syncChannelsWithProducts(): updating channel {} to position {}%.", channelUID, + positionAsPercent); + updateState(channelUID, positionAsPercent); + } else { + logger.trace("syncChannelsWithProducts(): update of channel {} to position {} skipped.", channelUID, + position); } + break outer; } } logger.trace("syncChannelsWithProducts(): resetting dirty flag."); @@ -508,15 +472,12 @@ private void syncChannelsWithProducts() { logger.trace("syncChannelsWithProducts() done."); } - /* - * ******************************************* - * ******** Processing of openHAB events ***** - */ + // Processing of openHAB events @Override public void handleCommand(ChannelUID channelUID, Command command) { - logger.trace("handleCommand({}): execution of command {} on channel {} will be scheduled.", - Thread.currentThread(), command, channelUID.getAsString()); + logger.trace("handleCommand({}): command {} on channel {} will be scheduled.", Thread.currentThread(), command, + channelUID.getAsString()); logger.debug("handleCommand({},{}) called.", channelUID.getAsString(), command); // Background execution of bridge related I/O @@ -540,8 +501,8 @@ public void handleCommand(ChannelUID channelUID, Command command) { * @param command the {@link Command}. */ private synchronized void handleCommandScheduled(ChannelUID channelUID, Command command) { - logger.trace("handleCommandScheduled({}): execution of command {} on channel {}.", Thread.currentThread(), - command, channelUID.getAsString()); + logger.trace("handleCommandScheduled({}): command {} on channel {}.", Thread.currentThread(), command, + channelUID.getAsString()); logger.debug("handleCommandScheduled({},{}) called.", channelUID.getAsString(), command); /* @@ -565,6 +526,11 @@ private synchronized void handleCommandScheduled(ChannelUID channelUID, Command return; } + // Build cache + if (!channel2VeluxActuator.containsKey(channelUID)) { + channel2VeluxActuator.put(channelUID, new Thing2VeluxActuator(this, channelUID)); + } + if (veluxBridgeConfiguration.hasChanged) { logger.trace("handleCommandScheduled(): work on updated bridge configuration parameters."); bridgeParamsUpdated(); @@ -582,129 +548,144 @@ private synchronized void handleCommandScheduled(ChannelUID channelUID, Command logger.debug("handleCommandScheduled(): received a Refresh command for a non-readable item."); } else { logger.trace("handleCommandScheduled(): refreshing item {} (type {}).", itemName, itemType); + try { // expecting an IllegalArgumentException for unknown Velux device + switch (itemType) { + // Bridge channels + case BRIDGE_STATUS: + newState = ChannelBridgeStatus.handleRefresh(channelUID, channelId, this); + break; + case BRIDGE_DOWNTIME: + newState = new DecimalType( + thisBridge.lastCommunication() - thisBridge.lastSuccessfulCommunication()); + break; + case BRIDGE_FIRMWARE: + newState = ChannelBridgeFirmware.handleRefresh(channelUID, channelId, this); + break; + case BRIDGE_IPADDRESS: + case BRIDGE_SUBNETMASK: + case BRIDGE_DEFAULTGW: + case BRIDGE_DHCP: + newState = ChannelBridgeLANconfig.handleRefresh(channelUID, channelId, this); + break; + case BRIDGE_WLANSSID: + case BRIDGE_WLANPASSWORD: + newState = ChannelBridgeWLANconfig.handleRefresh(channelUID, channelId, this); + break; + case BRIDGE_SCENES: + newState = ChannelBridgeScenes.handleRefresh(channelUID, channelId, this); + break; + case BRIDGE_PRODUCTS: + newState = ChannelBridgeProducts.handleRefresh(channelUID, channelId, this); + break; + case BRIDGE_CHECK: + newState = ChannelBridgeCheck.handleRefresh(channelUID, channelId, this); + break; + // Actuator channels + case ACTUATOR_POSITION: + case ACTUATOR_STATE: + case ROLLERSHUTTER_POSITION: + case WINDOW_POSITION: + newState = ChannelActuatorPosition.handleRefresh(channelUID, channelId, this); + break; + case ACTUATOR_LIMIT_MINIMUM: + case ROLLERSHUTTER_LIMIT_MINIMUM: + case WINDOW_LIMIT_MINIMUM: + newState = ChannelActuatorLimitation.handleRefresh(channelUID, "", this); + break; + case ACTUATOR_LIMIT_MAXIMUM: + case ROLLERSHUTTER_LIMIT_MAXIMUM: + case WINDOW_LIMIT_MAXIMUM: + newState = ChannelActuatorLimitation.handleRefresh(channelUID, channelId, this); + break; + + // VirtualShutter channels + case VSHUTTER_POSITION: + newState = ChannelVShutterPosition.handleRefresh(channelUID, channelId, this); + break; + + default: + logger.trace( + "handleCommandScheduled(): cannot handle REFRESH on channel {} as it is of type {}.", + itemName, channelId); + } + } catch (IllegalArgumentException e) { + logger.warn("Cannot handle REFRESH on channel {} as it isn't (yet) known to the bridge.", itemName); + } + if (newState != null) { + if (itemType.isChannel()) { + logger.debug("handleCommandScheduled(): updating channel {} to {}.", channelUID, newState); + updateState(channelUID, newState); + } + if (itemType.isProperty()) { + logger.debug("handleCommandScheduled(): updating property {} to {}.", channelUID, newState); + ThingProperty.setValue(this, itemType.getIdentifier(), newState.toString()); + + } + } else { + logger.info("handleCommandScheduled({},{}): updating of item {} (type {}) failed.", + channelUID.getAsString(), command, itemName, itemType); + } + } + } else { + /* + * =========================================================== + * Modification part + */ + logger.trace("handleCommandScheduled(): working on item {} (type {}) with COMMAND {}.", itemName, itemType, + command); + Command newValue = null; + try { // expecting an IllegalArgumentException for unknown Velux device switch (itemType) { // Bridge channels - case BRIDGE_STATUS: - newState = ChannelBridgeStatus.handleRefresh(channelUID, channelId, this); - break; - case BRIDGE_DOWNTIME: - newState = new DecimalType( - thisBridge.lastCommunication() - thisBridge.lastSuccessfulCommunication()); - break; - case BRIDGE_FIRMWARE: - newState = ChannelBridgeFirmware.handleRefresh(channelUID, channelId, this); - break; - case BRIDGE_IPADDRESS: - case BRIDGE_SUBNETMASK: - case BRIDGE_DEFAULTGW: - case BRIDGE_DHCP: - newState = ChannelBridgeLANconfig.handleRefresh(channelUID, channelId, this); + case BRIDGE_RELOAD: + if (command == OnOffType.ON) { + logger.trace("handleCommandScheduled(): about to reload informations from veluxBridge."); + bridgeParamsUpdated(); + } else { + logger.trace("handleCommandScheduled(): ignoring OFF command."); + } break; - case BRIDGE_WLANSSID: - case BRIDGE_WLANPASSWORD: - newState = ChannelBridgeWLANconfig.handleRefresh(channelUID, channelId, this); + case BRIDGE_DO_DETECTION: + ChannelBridgeDoDetection.handleCommand(channelUID, channelId, command, this); break; - case BRIDGE_SCENES: - newState = ChannelBridgeScenes.handleRefresh(channelUID, channelId, this); - break; - case BRIDGE_PRODUCTS: - newState = ChannelBridgeProducts.handleRefresh(channelUID, channelId, this); + + // Scene channels + case SCENE_ACTION: + ChannelSceneAction.handleCommand(channelUID, channelId, command, this); break; - case BRIDGE_CHECK: - newState = ChannelBridgeCheck.handleRefresh(channelUID, channelId, this); + case SCENE_SILENTMODE: + ChannelSceneSilentmode.handleCommand(channelUID, channelId, command, this); break; + // Actuator channels case ACTUATOR_POSITION: case ACTUATOR_STATE: case ROLLERSHUTTER_POSITION: case WINDOW_POSITION: - newState = ChannelActuatorPosition.handleRefresh(channelUID, channelId, this); + ChannelActuatorPosition.handleCommand(channelUID, channelId, command, this); break; case ACTUATOR_LIMIT_MINIMUM: case ROLLERSHUTTER_LIMIT_MINIMUM: case WINDOW_LIMIT_MINIMUM: - newState = ChannelActuatorLimitation.handleRefresh(channelUID, "", this); + ChannelActuatorLimitation.handleCommand(channelUID, channelId, command, this); break; case ACTUATOR_LIMIT_MAXIMUM: case ROLLERSHUTTER_LIMIT_MAXIMUM: case WINDOW_LIMIT_MAXIMUM: - newState = ChannelActuatorLimitation.handleRefresh(channelUID, channelId, this); + ChannelActuatorLimitation.handleCommand(channelUID, channelId, command, this); break; // VirtualShutter channels case VSHUTTER_POSITION: - newState = ChannelVShutterPosition.handleRefresh(channelUID, channelId, this); + newValue = ChannelVShutterPosition.handleCommand(channelUID, channelId, command, this); break; default: - logger.trace( - "handleCommandScheduled(): cannot handle REFRESH on channel {} as it is of type {}.", - itemName, channelId); - } - if (newState != null) { - logger.debug("handleCommandScheduled(): updating {} ({}) to {}.", itemName, channelUID, newState); - updateState(channelUID, newState); - } else { - logger.info("handleCommandScheduled({},{}): updating of item {} (type {}) failed.", - channelUID.getAsString(), command, itemName, itemType); + logger.warn("{} Cannot handle command {} on channel {} (type {}).", + VeluxBindingConstants.LOGGING_CONTACT, command, itemName, itemType); } - } - } else { - /* - * =========================================================== - * Modification part - */ - logger.trace("handleCommandScheduled(): working on item {} (type {}) with COMMAND {}.", itemName, itemType, - command); - Command newValue = null; - switch (itemType) { - // Bridge channels - case BRIDGE_RELOAD: - if (command == OnOffType.ON) { - logger.trace("handleCommandScheduled(): about to reload informations from veluxBridge."); - bridgeParamsUpdated(); - } else { - logger.trace("handleCommandScheduled(): ignoring OFF command."); - } - break; - case BRIDGE_DO_DETECTION: - ChannelBridgeDoDetection.handleCommand(channelUID, channelId, command, this); - break; - - // Scene channels - case SCENE_ACTION: - ChannelSceneAction.handleCommand(channelUID, channelId, command, this); - break; - case SCENE_SILENTMODE: - ChannelSceneSilentmode.handleCommand(channelUID, channelId, command, this); - break; - - // Actuator channels - case ACTUATOR_POSITION: - case ACTUATOR_STATE: - case ROLLERSHUTTER_POSITION: - case WINDOW_POSITION: - ChannelActuatorPosition.handleCommand(channelUID, channelId, command, this); - break; - case ACTUATOR_LIMIT_MINIMUM: - case ROLLERSHUTTER_LIMIT_MINIMUM: - case WINDOW_LIMIT_MINIMUM: - ChannelActuatorLimitation.handleCommand(channelUID, channelId, command, this); - break; - case ACTUATOR_LIMIT_MAXIMUM: - case ROLLERSHUTTER_LIMIT_MAXIMUM: - case WINDOW_LIMIT_MAXIMUM: - ChannelActuatorLimitation.handleCommand(channelUID, channelId, command, this); - break; - - // VirtualShutter channels - case VSHUTTER_POSITION: - newValue = ChannelVShutterPosition.handleCommand(channelUID, channelId, command, this); - break; - - default: - logger.warn("{} Cannot handle command {} on channel {} (type {}).", - VeluxBindingConstants.LOGGING_CONTACT, command, itemName, itemType); + } catch (IllegalArgumentException e) { + logger.warn("Cannot handle command on channel {} as it isn't (yet) known to the bridge.", itemName); } if (newValue != null) { postCommand(channelUID, newValue); diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxHandler.java index b4641441b96d0..bc0b780b36a26 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxHandler.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -16,7 +16,6 @@ import java.util.Map.Entry; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.thing.Bridge; import org.eclipse.smarthome.core.thing.ChannelUID; @@ -27,6 +26,7 @@ import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.RefreshType; import org.openhab.binding.velux.internal.config.VeluxThingConfiguration; +import org.openhab.binding.velux.internal.handler.utils.ExtendedBaseThingHandler; import org.openhab.binding.velux.internal.utils.Localization; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,8 +41,7 @@ public class VeluxHandler extends ExtendedBaseThingHandler { private final Logger logger = LoggerFactory.getLogger(VeluxHandler.class); - @SuppressWarnings("unused") - private @Nullable VeluxThingConfiguration configuration = null; + VeluxThingConfiguration configuration = new VeluxThingConfiguration(); public VeluxHandler(Thing thing, Localization localization) { super(thing); @@ -63,18 +62,14 @@ public void initialize() { logger.trace("initialize() updating ThingStatus to ONLINE."); updateStatus(ThingStatus.ONLINE); initializeProperties(); - } else { logger.trace("initialize() updating ThingStatus to OFFLINE/BRIDGE_OFFLINE."); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); - } logger.trace("initialize() done."); } private synchronized void initializeProperties() { - logger.trace("initializeProperties() called."); - configuration = getConfigAs(VeluxThingConfiguration.class); logger.trace("initializeProperties() done."); } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/package-info.java index 6ecc6e1b6bec0..c154d020febc1 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/package-info.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -11,7 +11,8 @@ * SPDX-License-Identifier: EPL-2.0 */ /** - * Class for handling any kind of openHAB events and requests. + * Classes for handling any kind of openHAB events and requests, providing a synchronisation between openHAB and the + * real world. * * @author Guenther Schreiner - Initial contribution */ diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ExtendedBaseBridgeHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ExtendedBaseBridgeHandler.java similarity index 89% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ExtendedBaseBridgeHandler.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ExtendedBaseBridgeHandler.java index ea057ebabc224..01c0674097adf 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ExtendedBaseBridgeHandler.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ExtendedBaseBridgeHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.internal.handler; +package org.openhab.binding.velux.internal.handler.utils; import java.util.Map; @@ -26,7 +26,7 @@ * @author Guenther Schreiner - Initial contribution. */ @NonNullByDefault -abstract class ExtendedBaseBridgeHandler extends BaseBridgeHandler { +public abstract class ExtendedBaseBridgeHandler extends BaseBridgeHandler { /* * ************************ @@ -37,7 +37,7 @@ abstract class ExtendedBaseBridgeHandler extends BaseBridgeHandler { * @see BaseBridgeHandler * @param bridge which will be created. */ - ExtendedBaseBridgeHandler(Bridge bridge) { + protected ExtendedBaseBridgeHandler(Bridge bridge) { super(bridge); } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ExtendedBaseThingHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ExtendedBaseThingHandler.java similarity index 88% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ExtendedBaseThingHandler.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ExtendedBaseThingHandler.java index bbc921e8c70e3..db0d248b8ff55 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ExtendedBaseThingHandler.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ExtendedBaseThingHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.internal.handler; +package org.openhab.binding.velux.internal.handler.utils; import java.util.Map; @@ -28,7 +28,7 @@ * @author Guenther Schreiner - Initial contribution. */ @NonNullByDefault -abstract class ExtendedBaseThingHandler extends BaseThingHandler { +public abstract class ExtendedBaseThingHandler extends BaseThingHandler { /* * ************************ @@ -39,7 +39,7 @@ abstract class ExtendedBaseThingHandler extends BaseThingHandler { * @see BaseThingHandler * @param thing which will be created. */ - ExtendedBaseThingHandler(Thing thing) { + protected ExtendedBaseThingHandler(Thing thing) { super(thing); } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/StateUtils.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/StateUtils.java similarity index 92% rename from bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/StateUtils.java rename to bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/StateUtils.java index ccc8d9399ddd2..cbc0d1176c4cb 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/StateUtils.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/StateUtils.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.velux.internal.handler; +package org.openhab.binding.velux.internal.handler.utils; import java.math.BigDecimal; @@ -32,7 +32,7 @@ * @author Guenther Schreiner - Initial contribution */ @NonNullByDefault -final class StateUtils { +public class StateUtils { /* * ************************ @@ -58,7 +58,7 @@ private StateUtils() { * @param propertyValue which should be converted, * @return state of type {@link State} in accordance with {@code dataType}. Will never be {@code null}. */ - protected static State createState(@Nullable Object propertyValue) { + public static State createState(@Nullable Object propertyValue) { if (propertyValue == null) { return UnDefType.NULL; } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/Thing2VeluxActuator.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/Thing2VeluxActuator.java new file mode 100644 index 0000000000000..ba2db6eb5e408 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/Thing2VeluxActuator.java @@ -0,0 +1,133 @@ +/** + * 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.velux.internal.handler.utils; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.openhab.binding.velux.internal.VeluxBindingProperties; +import org.openhab.binding.velux.internal.handler.VeluxBridgeHandler; +import org.openhab.binding.velux.internal.things.VeluxProduct; +import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex; +import org.openhab.binding.velux.internal.things.VeluxProductSerialNo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/*** + * The class {@link Thing2VeluxActuator} provides simplified access to Velux device behind the Velux bridge by + * evaluating the + * Thing property belonging to a channel and comparing them with the bridge registered objects. To put it in a nutshell, + * the methods provide a cache for faster access, + *

        + *
      • {@link #Thing2VeluxActuator} Constructor,
      • + *
      • {@link #getProductBridgeIndex} returns the Velux bridge index for access,
      • + *
      • {@link #isInverted} returns a flag about value inversion.
      • + *
      + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class Thing2VeluxActuator { + private final Logger logger = LoggerFactory.getLogger(Thing2VeluxActuator.class); + + // Class internal + + private VeluxBridgeHandler bridgeHandler; + private ChannelUID channelUID; + private boolean isInverted = false; + private VeluxProduct thisProduct = VeluxProduct.UNKNOWN; + + // Private + + private void mapThing2Velux() { + if (!ThingConfiguration.exists(bridgeHandler, channelUID, + VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER)) { + logger.warn("mapThing2Velux(): aborting processing as {} is not set.", + VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER); + return; + } + String actuatorSerial = (String) ThingConfiguration.getValue(bridgeHandler, channelUID, + VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER); + logger.trace("mapThing2Velux(): found actuatorSerial={}.", actuatorSerial); + + // Handle value inversion + boolean propertyInverted = false; + if (ThingConfiguration.exists(bridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) { + propertyInverted = (boolean) ThingConfiguration.getValue(bridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED); + } + isInverted = propertyInverted || VeluxProductSerialNo.indicatesRevertedValues(actuatorSerial); + logger.trace("mapThing2Velux(): found isInverted={}.", isInverted); + actuatorSerial = VeluxProductSerialNo.cleaned(actuatorSerial); + + if (!bridgeHandler.bridgeParameters.actuators.getChannel().existingProducts.isRegistered(actuatorSerial)) { + logger.warn("mapThing2Velux(): cannot work on unknown actuator with serial {}.", actuatorSerial); + return; + } + logger.trace("mapThing2Velux(): fetching actuator for {}.", actuatorSerial); + thisProduct = bridgeHandler.bridgeParameters.actuators.getChannel().existingProducts.get(actuatorSerial); + logger.debug("mapThing2Velux(): found actuator {}.", thisProduct); + return; + } + + // Constructor + + /** + * Constructor. + *

      + * + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @param thisChannelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + */ + public Thing2VeluxActuator(VeluxBridgeHandler thisBridgeHandler, ChannelUID thisChannelUID) { + bridgeHandler = thisBridgeHandler; + channelUID = thisChannelUID; + } + + // Public methods + + /** + * Returns the Velux gateway index for accessing a Velux device based on the Thing configuration which belongs to + * the channel passed during constructor. + *

      + * + * @return bridgeProductIndex for accessing the Velux device (or ProductBridgeIndex.UNKNOWN if not found). + */ + public ProductBridgeIndex getProductBridgeIndex() { + if (thisProduct == VeluxProduct.UNKNOWN) { + mapThing2Velux(); + } + if (thisProduct == VeluxProduct.UNKNOWN) { + return ProductBridgeIndex.UNKNOWN; + } + return thisProduct.getBridgeProductIndex(); + } + + /** + * Returns the flag whether a value inversion in intended for the Velux device based on the Thing configuration + * which belongs to the channel passed during constructor. + *

      + * + * @return isInverted for handling of values of the Velux device (or false if not found).. + */ + public boolean isInverted() { + if (thisProduct == VeluxProduct.UNKNOWN) { + mapThing2Velux(); + } + if (thisProduct == VeluxProduct.UNKNOWN) { + logger.warn("isInverted(): Thing not found in Velux Bridge."); + } + return isInverted; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ThingConfiguration.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ThingConfiguration.java new file mode 100644 index 0000000000000..642543c0e61c7 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ThingConfiguration.java @@ -0,0 +1,105 @@ +/** + * 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.velux.internal.handler.utils; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/*** + * The class {@link ThingConfiguration} provides methods for dealing with + * properties. + *

        + *
      • {@link #exists} Check existence of a property,
      • + *
      • {@link #getValue} Returns a property value.
      • + *
      + *

      + * Noninstantiable utility class + *

      + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class ThingConfiguration { + private static final Logger LOGGER = LoggerFactory.getLogger(ThingConfiguration.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-Instantiability + + private ThingConfiguration() { + throw new AssertionError(); + } + + /* + * ************************** + * ***** Public Methods ***** + */ + + /** + * Check existence of the configuration value for the given channel and + * desired configName which are defined within VeluxBindingProperties. + *

      + * + * @param bridge which handles the mentioned Things, + * @param channelUID describes the channel to by scrutinized, + * @param configName defines the configuration entry which is to be evaluated. + * @return exists of type boolean. + */ + public static boolean exists(BaseBridgeHandler bridge, ChannelUID channelUID, String configName) { + ThingUID channelTUID = channelUID.getThingUID(); + Thing thingOfChannel = bridge.getThingByUID(channelTUID); + boolean exists = false; + if (thingOfChannel == null) { + LOGGER.warn("exists(): Channel {} does not belong to a thing.", channelUID); + } else { + if (thingOfChannel.getConfiguration().get(configName) != null) { + exists = true; + } + } + LOGGER.trace("exists({},{}) returns {}.", channelUID, configName, exists); + return exists; + } + + /** + * Return the property value of type Object for the given channel and + * desired propertyName which are defined within VeluxBindingProperties. + *

      + * + * @param bridge which handles the mentioned Things, + * @param channelUID describes the channel to by scrutinized, + * @param configName defines the configuration entry which is to be evaluated. + * @return configurationValue of type {@link Object}. Will return {@code null}, if not found, or if value + * itself + * is {@code null}. + */ + public static Object getValue(BaseBridgeHandler bridge, ChannelUID channelUID, String configName) { + ThingUID channelTUID = channelUID.getThingUID(); + Thing thingOfChannel = bridge.getThingByUID(channelTUID); + if (thingOfChannel == null) { + LOGGER.warn("getValue(): Channel {} does not belong to a thing.", channelUID); + return true; + } + Object configurationValue = thingOfChannel.getConfiguration().get(configName); + LOGGER.trace("getValue({},{}) returns {}.", channelUID, configName, configurationValue); + return configurationValue; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ThingProperty.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ThingProperty.java new file mode 100644 index 0000000000000..0b27857d2e1af --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ThingProperty.java @@ -0,0 +1,104 @@ +/** + * 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.velux.internal.handler.utils; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/*** + * The class {@link ThingProperty} provides methods for dealing with + * properties. + *

        + *
      • {@link ThingConfiguration#exists} Check existence of a property,
      • + *
      • {@link ThingConfiguration#getValue} Returns a property value,
      • + *
      • {@link #setValue} Modifies a property value.
      • + *
      + *

      + * Noninstantiable utility class + *

      + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class ThingProperty { + private static final Logger LOGGER = LoggerFactory.getLogger(ThingProperty.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-Instantiability + + private ThingProperty() { + throw new AssertionError(); + } + + /* + * ************************** + * ***** Public Methods ***** + */ + + /** + * Modifies the property value of the given thing and the named property. + *

      + * + * @param thing which property will be modified, + * @param propertyName defines the property which is to be modified, + * @param propertyValue defines the new property value. + */ + public static void setValue(Thing thing, String propertyName, String propertyValue) { + thing.setProperty(propertyName, propertyValue); + LOGGER.trace("setValue() {} set to {}.", propertyName, propertyValue); + return; + } + + /** + * Modifies the property value for the given bridge, which is a dedicated thing, and the named property. + *

      + * + * @param bridgeHandler which contains the properties, + * @param propertyName defines the property which is to be modified. + * @param propertyValue defines the new property value. + */ + public static void setValue(ExtendedBaseBridgeHandler bridgeHandler, String propertyName, String propertyValue) { + setValue(bridgeHandler.getThing(), propertyName, propertyValue); + } + + /** + * Modifies the property value for the given propertyName, identified by the given bridge and channel.desired + * propertyName which are defined within + * VeluxBindingProperties. + *

      + * + * @param bridgeHandler which contains the properties, + * @param channelUID describes the channel to by scrutinized, + * @param propertyName defines the property which is to be modified. + * @param propertyValue defines the new property value. + */ + public static void setValue(ExtendedBaseBridgeHandler bridgeHandler, ChannelUID channelUID, String propertyName, + String propertyValue) { + ThingUID channelTUID = channelUID.getThingUID(); + Thing thingOfChannel = bridgeHandler.getThingByUID(channelTUID); + if (thingOfChannel == null) { + LOGGER.warn("setValue(): Channel {} does not belong to a thing.", channelUID); + return; + } + setValue(thingOfChannel, propertyName, propertyValue); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/package-info.java new file mode 100644 index 0000000000000..8d6e045afc4cc --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/package-info.java @@ -0,0 +1,18 @@ +/** + * 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 + */ +/** + * Helper classes for dealing with configurations, properties, things and channels. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.internal.handler.utils; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/package-info.java index 7a2c3c5275fee..2bf7252673457 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/package-info.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingProducts.java index efd86268149f8..cbced7517a9f8 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingProducts.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingProducts.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingScenes.java index 847079b262628..456faa821d76e 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingScenes.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingScenes.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwFirmware.java index 36a39c3005cf0..9b1ffe07f5641 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwFirmware.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwFirmware.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwLAN.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwLAN.java index 86f35cc10063e..1a34111901067 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwLAN.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwLAN.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwState.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwState.java index d832343ca83e6..fc7a7455dc887 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwState.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwState.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwWLAN.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwWLAN.java index d9eb5334fb97b..3effaa76bf8ca 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwWLAN.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwWLAN.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxKLFAPI.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxKLFAPI.java index 941e8c71cc2fc..7662a4e783320 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxKLFAPI.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxKLFAPI.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProduct.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProduct.java index 5e1f5265af6a8..41943610441a5 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProduct.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProduct.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -37,7 +37,7 @@ public class VeluxProduct { public static class ProductBridgeIndex { // Public definition - private static final ProductBridgeIndex UNKNOWN = new ProductBridgeIndex(0); + public static final ProductBridgeIndex UNKNOWN = new ProductBridgeIndex(0); // Class internal private int id; @@ -51,6 +51,11 @@ public ProductBridgeIndex(int id) { public int toInt() { return id; } + + @Override + public String toString() { + return Integer.toString(id); + } } // Class internal diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductName.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductName.java index c9c8d8eaa1542..9f0cbfd64389c 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductName.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductName.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductPosition.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductPosition.java index 88ee85af68730..8b0fda88963c4 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductPosition.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductPosition.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -83,6 +83,16 @@ public VeluxProductPosition(PercentType position) { this.isValid = true; } + /** + * Creation of a Position object to specify a distinct actuator setting. + * + * @param position A position as type {@link PercentType} (between 0 and 100). + * @param toBeInverted Flag whether the value should be handled as inverted. + */ + public VeluxProductPosition(PercentType position, boolean toBeInverted) { + this(toBeInverted ? new PercentType(PercentType.HUNDRED.intValue() - position.intValue()) : position); + } + /** * Creation of a Position object to specify a distinct actuator setting. * diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductReference.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductReference.java index 24196f37e24dc..ea30b7774261b 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductReference.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductReference.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductSerialNo.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductSerialNo.java index 48a40469bbdea..4bf12768b5b0e 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductSerialNo.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductSerialNo.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductState.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductState.java index 8f5ef464c10fd..9c42ab8da774e 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductState.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductState.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductType.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductType.java index 98fc5c86487e3..de056660b6370 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductType.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductType.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductVelocity.java index 5816853f67736..343aecb458529 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductVelocity.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductVelocity.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxScene.java index 4bcd5f1148d28..58731801002b8 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxScene.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxScene.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/package-info.java index a4e4b7b7f30e4..06702fbaac96a 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/package-info.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -11,7 +11,7 @@ * SPDX-License-Identifier: EPL-2.0 */ /** - * Classes for modeling the Velux devices and their characteristics in the openHAB environment. + * Classes for modeling the Velux devices and their properties in a general openHAB (OH1/OH2/OH3)-independent way. * * @author Guenther Schreiner - Initial contribution */ diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/Localization.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/Localization.java index 87965dba1effa..0897e51c6c314 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/Localization.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/Localization.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/ManifestInformation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/ManifestInformation.java index 20864db4df97f..627aba075890a 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/ManifestInformation.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/ManifestInformation.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/package-info.java index eac5caa511d87..4a90117dec99b 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/package-info.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -11,7 +11,7 @@ * SPDX-License-Identifier: EPL-2.0 */ /** - * Utility classes. + * Generic utility classes (might not be limited to the Velux binding). * * @author Guenther Schreiner - Initial contribution */ diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/package-info.java index 2a298b9284910..e58e8487cac24 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/package-info.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/package-info.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2019 Contributors to the openHAB project + * Copyright (c) 2010-2020 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -11,7 +11,8 @@ * SPDX-License-Identifier: EPL-2.0 */ /** - * Classes for common definitions for the binding, i.e. constants like String patterns. + * + * NOTE: All relevant classes of this binding are below the internal node. * * @author Guenther Schreiner - Initial contribution */ diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/config/config.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/config/config.xml index e5b61344d4fab..c30311f034f39 100644 --- a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/config/config.xml +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/config/config.xml @@ -47,7 +47,7 @@ @text/config.velux.bridge.timeoutMsecs.description false - 1000 + 500 true @@ -57,11 +57,11 @@ 5 true - + @text/config.velux.bridge.refreshMsecs.description false - 15000 + 10000 true diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux.properties b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux.properties index 20e63febeea38..c48e005de956c 100644 --- a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux.properties +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux.properties @@ -170,7 +170,7 @@ runtime.bridge-offline-login-sequence-failed = Login sequence failed. # # Thing status descriptions # -channelValue.check-integrity-failed = Integrity check failed: The following scenes are unused: {}. +channelValue.check-integrity-failed = Integrity check failed: The following scenes are unused: channelValue.check-integrity-ok = Integrity check ok. All scenes are used within Items. # # end-of-ESH-INF/i18n/velux.properties diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_de.properties b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_de.properties index 357a993d903e2..cede65bf1f723 100644 --- a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_de.properties +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_de.properties @@ -171,7 +171,7 @@ runtime.bridge-offline-login-sequence-failed = Anmeldung am Kopplungselement feh # # Thing status descriptions # -channelValue.check-integrity-failed = Integrittsprfung fehlgeschlagen. Nachfolgende Szenen werden nicht verwendet: {}. +channelValue.check-integrity-failed = Integrittsprfung fehlgeschlagen. Nachfolgende Szenen werden nicht verwendet: channelValue.check-integrity-ok = Integrittsprfung bestanden. Alle definierten Szenen werden verwendet. # # Note: this entry should overwrite the implicit naming within paperUI slider element diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_dk.properties b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_dk.properties index 9a271388e4b81..a3e3e8b861509 100644 --- a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_dk.properties +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_dk.properties @@ -165,7 +165,7 @@ runtime.bridge-offline-login -sequence-failed = Login-sekvens mislykkedes. # # Statusbeskrivelser # -channelValue.check-integrity-failed = Integritetskontrol mislykkedes: Følgende scener er ubrugte: {}. +channelValue.check-integrity-failed = Integritetskontrol mislykkedes: Følgende scener er ubrugte: channelValue.check-integrity-ok = Integrity check ok. Alle scener bruges i artikler. # # end-of-ESH-INF/i18n/velux.properties diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_nl.properties b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_nl.properties index 9019db1f65464..e4b3cd32d581c 100644 --- a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_nl.properties +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_nl.properties @@ -165,7 +165,7 @@ runtime.bridge-offline-login-sequence-failure = Aanmeldingsreeks mislukt. # # Ding statusbeschrijvingen # -channelValue.check-integrity-failed = Integriteitscontrole mislukt: de volgende scènes worden niet gebruikt: {}. +channelValue.check-integrity-failed = Integriteitscontrole mislukt: de volgende scènes worden niet gebruikt: channelValue.check-integrity-ok = Integriteitscontrole ok. Alle scènes worden gebruikt binnen Items. # # end-of-ESH-INF / i18n / velux_nl.properties diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/bridge.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/bridge.xml index 8ae902c5dabbd..66bb7587275d7 100644 --- a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/bridge.xml +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/bridge.xml @@ -15,21 +15,22 @@ - - - - - - - - - - - Velux + diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/channels.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/channels.xml index 49d9abd37efeb..8eb7d33c6d8f7 100644 --- a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/channels.xml +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/channels.xml @@ -48,6 +48,7 @@ NetworkAppliance + From 845707fbcbc7ab78b7af3729e006e8df9557ca32 Mon Sep 17 00:00:00 2001 From: Guenther Schreiner Date: Sun, 2 Feb 2020 15:43:35 +0100 Subject: [PATCH 5/9] [velux] Adapted pom.xml from 2.5.1 to 2.5.2. Signed-off-by: Guenther Schreiner --- bundles/org.openhab.binding.velux/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.velux/pom.xml b/bundles/org.openhab.binding.velux/pom.xml index 6d8d1cce56a0c..1876fcb2aa2e5 100644 --- a/bundles/org.openhab.binding.velux/pom.xml +++ b/bundles/org.openhab.binding.velux/pom.xml @@ -9,7 +9,7 @@ org.openhab.addons.bundles org.openhab.addons.reactor.bundles - 2.5.1-SNAPSHOT + 2.5.2-SNAPSHOT org.openhab.binding.velux From 4429a6903c84f98ca2e733c0db2f8e3630f88781 Mon Sep 17 00:00:00 2001 From: Guenther Schreiner Date: Mon, 3 Feb 2020 14:42:42 +0100 Subject: [PATCH 6/9] [velux] Reduced logging. Signed-off-by: Guenther Schreiner --- .../doc/conf/things/velux-bridge.things | 2 +- .../velux/internal/VeluxBindingConstants.java | 28 +++++----- .../binding/velux/internal/VeluxItemType.java | 55 ++++++++----------- .../internal/bridge/slip/SCgetLimitation.java | 2 +- .../bridge/slip/SCrunProductDiscovery.java | 31 +++-------- .../internal/bridge/slip/SCsetLimitation.java | 10 ++-- .../handler/ChannelBridgeLANconfig.java | 8 +-- .../handler/ChannelBridgeWLANconfig.java | 4 +- .../internal/things/VeluxExistingScenes.java | 3 +- .../velux/internal/utils/Localization.java | 4 +- 10 files changed, 61 insertions(+), 86 deletions(-) diff --git a/bundles/org.openhab.binding.velux/doc/conf/things/velux-bridge.things b/bundles/org.openhab.binding.velux/doc/conf/things/velux-bridge.things index b0e3d1f7b82d7..ae38a0b638191 100644 --- a/bundles/org.openhab.binding.velux/doc/conf/things/velux-bridge.things +++ b/bundles/org.openhab.binding.velux/doc/conf/things/velux-bridge.things @@ -21,7 +21,7 @@ // Definition of Velux bridge velux:klf200:home // -Bridge velux:klf200:gs28 [ ipAddress="192.168.45.9", tcpPort=51200, password="secret", isProtocolTraceEnabled=true ] +Bridge velux:klf200:gs28 [ ipAddress="192.168.45.1", tcpPort=51200, password="secret", isProtocolTraceEnabled=true ] // // vim: syntax=Xtend vim: noai:ts=4:sw=4 diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConstants.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConstants.java index 96453e0f4ba4f..6992a5bd05577 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConstants.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConstants.java @@ -104,25 +104,25 @@ public class VeluxBindingConstants { // List of all bridge channel ids - /** Channel identifier describing the current Bridge State. */ + /** Channel/Property identifier describing the current Bridge State. */ public static final String CHANNEL_BRIDGE_STATUS = "status"; public static final String CHANNEL_BRIDGE_RELOAD = "reload"; public static final String CHANNEL_BRIDGE_DOWNTIME = "downtime"; + public static final String CHANNEL_BRIDGE_DO_DETECTION = "doDetection"; + public static final String PROPERTY_BRIDGE_TIMESTAMP_SUCCESS = "connectionSuccess"; public static final String PROPERTY_BRIDGE_TIMESTAMP_ATTEMPT = "connectionAttempt"; - - public static final String CHANNEL_BRIDGE_FIRMWARE = "firmware"; - public static final String CHANNEL_BRIDGE_IPADDRESS = "ipAddress"; - public static final String CHANNEL_BRIDGE_SUBNETMASK = "subnetMask"; - public static final String CHANNEL_BRIDGE_DEFAULTGW = "defaultGW"; - public static final String CHANNEL_BRIDGE_DHCP = "DHCP"; - public static final String CHANNEL_BRIDGE_WLANSSID = "WLANSSID"; - public static final String CHANNEL_BRIDGE_WLANPASSWORD = "WLANPassword"; - - public static final String CHANNEL_BRIDGE_DO_DETECTION = "doDetection"; - public static final String CHANNEL_BRIDGE_CHECK = "check"; - public static final String CHANNEL_BRIDGE_PRODUCTS = "products"; - public static final String CHANNEL_BRIDGE_SCENES = "scenes"; + public static final String PROPERTY_BRIDGE_FIRMWARE = "firmware"; + public static final String PROPERTY_BRIDGE_IPADDRESS = "ipAddress"; + public static final String PROPERTY_BRIDGE_SUBNETMASK = "subnetMask"; + public static final String PROPERTY_BRIDGE_DEFAULTGW = "defaultGW"; + public static final String PROPERTY_BRIDGE_DHCP = "DHCP"; + public static final String PROPERTY_BRIDGE_WLANSSID = "WLANSSID"; + public static final String PROPERTY_BRIDGE_WLANPASSWORD = "WLANPassword"; + + public static final String PROPERTY_BRIDGE_CHECK = "check"; + public static final String PROPERTY_BRIDGE_PRODUCTS = "products"; + public static final String PROPERTY_BRIDGE_SCENES = "scenes"; // List of all scene channel ids public static final String CHANNEL_SCENE_ACTION = "action"; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxItemType.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxItemType.java index 6fc005d999163..e582e4f5983b0 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxItemType.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxItemType.java @@ -74,42 +74,43 @@ @NonNullByDefault public enum VeluxItemType { // @formatter:off - UNKNOWN(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.UNKNOWN, TypeFlavor.UNUSABLE), + UNKNOWN(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.UNKNOWN, TypeFlavor.UNUSABLE), // - BINDING_INFORMATION(VeluxBindingConstants.THING_TYPE_BINDING, VeluxBindingConstants.CHANNEL_BINDING_INFORMATION, TypeFlavor.READONLY_VOLATILE_STRING), + BINDING_INFORMATION(VeluxBindingConstants.THING_TYPE_BINDING, VeluxBindingConstants.CHANNEL_BINDING_INFORMATION, TypeFlavor.READONLY_VOLATILE_STRING), // - BRIDGE_STATUS(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_STATUS, TypeFlavor.READONLY_VOLATILE_STRING), - BRIDGE_DOWNTIME(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_DOWNTIME, TypeFlavor.READONLY_VOLATILE_NUMBER), - BRIDGE_RELOAD(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_RELOAD, TypeFlavor.INITIATOR), - BRIDGE_DO_DETECTION(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_DO_DETECTION, TypeFlavor.INITIATOR), - BRIDGE_FIRMWARE(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_FIRMWARE, TypeFlavor.PROPERTY), - BRIDGE_IPADDRESS(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_IPADDRESS, TypeFlavor.PROPERTY), - BRIDGE_SUBNETMASK(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_SUBNETMASK, TypeFlavor.PROPERTY), - BRIDGE_DEFAULTGW(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_DEFAULTGW, TypeFlavor.PROPERTY), - BRIDGE_DHCP(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_DHCP, TypeFlavor.PROPERTY), - BRIDGE_WLANSSID(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_WLANSSID, TypeFlavor.PROPERTY), - BRIDGE_WLANPASSWORD(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_WLANPASSWORD, TypeFlavor.PROPERTY), - BRIDGE_PRODUCTS(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_PRODUCTS, TypeFlavor.PROPERTY), - BRIDGE_SCENES(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_SCENES, TypeFlavor.PROPERTY), - BRIDGE_CHECK(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_CHECK, TypeFlavor.PROPERTY), + BRIDGE_STATUS(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_STATUS, TypeFlavor.READONLY_VOLATILE_STRING), + BRIDGE_DOWNTIME(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_DOWNTIME, TypeFlavor.READONLY_VOLATILE_NUMBER), + BRIDGE_RELOAD(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_RELOAD, TypeFlavor.INITIATOR), + BRIDGE_DO_DETECTION(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_DO_DETECTION, TypeFlavor.INITIATOR), + + BRIDGE_FIRMWARE(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_FIRMWARE, TypeFlavor.PROPERTY), + BRIDGE_IPADDRESS(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_IPADDRESS, TypeFlavor.PROPERTY), + BRIDGE_SUBNETMASK(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_SUBNETMASK, TypeFlavor.PROPERTY), + BRIDGE_DEFAULTGW(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_DEFAULTGW, TypeFlavor.PROPERTY), + BRIDGE_DHCP(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_DHCP, TypeFlavor.PROPERTY), + BRIDGE_WLANSSID(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_WLANSSID, TypeFlavor.PROPERTY), + BRIDGE_WLANPASSWORD(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_WLANPASSWORD, TypeFlavor.PROPERTY), + BRIDGE_PRODUCTS(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_PRODUCTS, TypeFlavor.PROPERTY), + BRIDGE_SCENES(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_SCENES, TypeFlavor.PROPERTY), + BRIDGE_CHECK(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_CHECK, TypeFlavor.PROPERTY), // - ACTUATOR_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION, TypeFlavor.MANIPULATOR_SHUTTER), - ACTUATOR_STATE(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_STATE, TypeFlavor.MANIPULATOR_SWITCH), + ACTUATOR_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION, TypeFlavor.MANIPULATOR_SHUTTER), + ACTUATOR_STATE(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_STATE, TypeFlavor.MANIPULATOR_SWITCH), ACTUATOR_LIMIT_MINIMUM(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MINIMUM,TypeFlavor.MANIPULATOR_SHUTTER), ACTUATOR_LIMIT_MAXIMUM(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MAXIMUM,TypeFlavor.MANIPULATOR_SHUTTER), // - ROLLERSHUTTER_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_ROLLERSHUTTER,VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION, TypeFlavor.MANIPULATOR_SHUTTER), + ROLLERSHUTTER_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_ROLLERSHUTTER,VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION, TypeFlavor.MANIPULATOR_SHUTTER), ROLLERSHUTTER_LIMIT_MINIMUM(VeluxBindingConstants.THING_TYPE_VELUX_ROLLERSHUTTER,VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MINIMUM,TypeFlavor.MANIPULATOR_SHUTTER), ROLLERSHUTTER_LIMIT_MAXIMUM(VeluxBindingConstants.THING_TYPE_VELUX_ROLLERSHUTTER,VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MAXIMUM,TypeFlavor.MANIPULATOR_SHUTTER), // - WINDOW_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_WINDOW, VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION, TypeFlavor.MANIPULATOR_SHUTTER), + WINDOW_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_WINDOW, VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION, TypeFlavor.MANIPULATOR_SHUTTER), WINDOW_LIMIT_MINIMUM(VeluxBindingConstants.THING_TYPE_VELUX_WINDOW, VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MINIMUM,TypeFlavor.MANIPULATOR_SHUTTER), WINDOW_LIMIT_MAXIMUM(VeluxBindingConstants.THING_TYPE_VELUX_WINDOW, VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MAXIMUM,TypeFlavor.MANIPULATOR_SHUTTER), // - SCENE_ACTION(VeluxBindingConstants.THING_TYPE_VELUX_SCENE, VeluxBindingConstants.CHANNEL_SCENE_ACTION, TypeFlavor.INITIATOR), - SCENE_SILENTMODE(VeluxBindingConstants.THING_TYPE_VELUX_SCENE, VeluxBindingConstants.CHANNEL_SCENE_SILENTMODE, TypeFlavor.WRITEONLY_VOLATILE_SWITCH), + SCENE_ACTION(VeluxBindingConstants.THING_TYPE_VELUX_SCENE, VeluxBindingConstants.CHANNEL_SCENE_ACTION, TypeFlavor.INITIATOR), + SCENE_SILENTMODE(VeluxBindingConstants.THING_TYPE_VELUX_SCENE, VeluxBindingConstants.CHANNEL_SCENE_SILENTMODE, TypeFlavor.WRITEONLY_VOLATILE_SWITCH), // - VSHUTTER_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_VSHUTTER, VeluxBindingConstants.CHANNEL_VSHUTTER_POSITION, TypeFlavor.MANIPULATOR_SHUTTER), + VSHUTTER_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_VSHUTTER, VeluxBindingConstants.CHANNEL_VSHUTTER_POSITION, TypeFlavor.MANIPULATOR_SHUTTER), ; // @formatter:on @@ -177,7 +178,6 @@ private enum TypeFlavor { private boolean itemIsProperty; private static final Logger LOGGER = LoggerFactory.getLogger(VeluxItemType.class); - private final Logger logger = LoggerFactory.getLogger(getClass()); private static final int REFRESH_CYCLE_FIRST_TIME = 0; private static final int REFRESH_ONCE_A_DAY = 8640; @@ -340,7 +340,6 @@ public Class getItemClass() { * @return itemIsReadable of type boolean describing the ability to perform a write operation. */ public boolean isReadable() { - logger.trace("isReadable() returns {}.", this.itemIsReadable); return this.itemIsReadable; } @@ -350,7 +349,6 @@ public boolean isReadable() { * @return itemIsWritable of type boolean describing the ability to perform a write operation. */ public boolean isWritable() { - logger.trace("isWritable() returns {}.", this.itemIsWritable); return this.itemIsWritable; } @@ -360,7 +358,6 @@ public boolean isWritable() { * @return isExecute of type boolean describing the ability to perform a write operation. */ public boolean isExecutable() { - logger.trace("isExecutable() returns {}.", this.itemIsExecutable); return this.itemIsExecutable; } @@ -370,7 +367,6 @@ public boolean isExecutable() { * @return isExecute of type boolean describing the ability to perform a write operation. */ public boolean isToBeRefreshed() { - logger.trace("isToBeRefreshed() returns {}.", this.itemIsToBeRefreshed); return this.itemIsToBeRefreshed; } @@ -380,7 +376,6 @@ public boolean isToBeRefreshed() { * @return refreshDivider of type int describing the factor. */ public int getRefreshDivider() { - logger.trace("getRefreshDivider() returns {}.", this.itemsRefreshDivider); return this.itemsRefreshDivider; } @@ -390,7 +385,6 @@ public int getRefreshDivider() { * @return isChannel of type boolean describing the need to be handled as channel. */ public boolean isChannel() { - logger.trace("isChannel() returns {}.", this.itemIsChannel); return this.itemIsChannel; } @@ -400,7 +394,6 @@ public boolean isChannel() { * @return itemIsProperty of type boolean describing the need to be handled as property. */ public boolean isProperty() { - logger.trace("isProperty() returns {}.", this.itemIsProperty); return this.itemIsProperty; } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLimitation.java index 1f56ebd495af0..84b7966d9bbaf 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLimitation.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLimitation.java @@ -25,7 +25,7 @@ /** * Protocol specific bridge communication supported by the Velux bridge: - * Retrieve Product + * Retrieve Product Limitations *

      * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class * itself. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductDiscovery.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductDiscovery.java index 85b1ffede0508..6b906a22d3f6c 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductDiscovery.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductDiscovery.java @@ -22,38 +22,23 @@ import org.slf4j.LoggerFactory; /** - * Specific bridge communication message supported by the Velux bridge. + * Protocol specific bridge communication supported by the Velux bridge: + * Ask the Bridge to detect (new) products/actuators *

      - * Message semantic: Ask the Bridge to detect (new) products/actuators. + * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. *

      - * - * It defines informations how to send query and receive answer through the + * As 3rd level class it defines informations how to send query and receive answer through the * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol + * as described by the interface {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol * SlipBridgeCommunicationProtocol}. + *

      + * There are no methods in addition to the mentioned interface. * - * @author Guenther Schreiner - Initial contribution. - * - * Protocol specific bridge communication supported by the Velux bridge: - * Detect Products/Actuators - *

      - * Common Message semantic: Communication with the bridge and (optionally) storing returned information within - * the class itself. - *

      - * Implementing the protocol-independent class {@link RunProductDiscovery}. - *

      - * As 3rd level class it defines informations how to send query and receive answer through the - * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} - * as described by the interface {@link SlipBridgeCommunicationProtocol}. - *

      - * There are no methods in addition to the mentioned interface. - * - * @see RunProductDiscovery * @see SlipBridgeCommunicationProtocol * * @author Guenther Schreiner - Initial contribution. */ -// ToDo: Velux has to provide an updated API doc. @NonNullByDefault class SCrunProductDiscovery extends RunProductDiscovery implements SlipBridgeCommunicationProtocol { private final Logger logger = LoggerFactory.getLogger(SCrunProductDiscovery.class); diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetLimitation.java index 313eccd55ff4f..6f6626be6498f 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetLimitation.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetLimitation.java @@ -15,7 +15,6 @@ import java.util.Random; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.velux.internal.bridge.common.GetProductLimitation; import org.openhab.binding.velux.internal.bridge.common.SetProductLimitation; import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; @@ -27,7 +26,7 @@ /** * Protocol specific bridge communication supported by the Velux bridge: - * Retrieve Product + * Modify Product Limitations *

      * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class * itself. @@ -38,11 +37,12 @@ *

      * Methods in addition to the mentioned interface: *

        - *
      • {@link #setActuatorIdAndMinimumLimitation(int,int)} to set the lower limitation of one specific product.
      • - *
      • {@link #setActuatorIdAndMaximumLimitation(int,int)} to set the higher limitation of one specific product.
      • + *
      • {@link #setActuatorIdAndMinimumLimitation} to set the lower limitation of one specific product.
      • + *
      • {@link #setActuatorIdAndMaximumLimitation} to set the higher limitation of one specific product.
      • + *
      • {@link #setActuatorIdAndResetLimitation} to reset any limitation of one specific product.
      • *
      * - * @see GetProductLimitation + * @see SetProductLimitation * @see SlipBridgeCommunicationProtocol * * @author Guenther Schreiner - Initial contribution. diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeLANconfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeLANconfig.java index 9cc6767ede369..b50fdeede3b5f 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeLANconfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeLANconfig.java @@ -73,22 +73,22 @@ private ChannelBridgeLANconfig() { switch (itemType) { case BRIDGE_IPADDRESS: newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABipAddress); - ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.CHANNEL_BRIDGE_IPADDRESS, + ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.PROPERTY_BRIDGE_IPADDRESS, thisBridgeHandler.bridgeParameters.lanConfig.openHABipAddress.toString()); break; case BRIDGE_SUBNETMASK: newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABsubnetMask); - ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.CHANNEL_BRIDGE_SUBNETMASK, + ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.PROPERTY_BRIDGE_SUBNETMASK, thisBridgeHandler.bridgeParameters.lanConfig.openHABsubnetMask.toString()); break; case BRIDGE_DEFAULTGW: newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABdefaultGW); - ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.CHANNEL_BRIDGE_DEFAULTGW, + ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.PROPERTY_BRIDGE_DEFAULTGW, thisBridgeHandler.bridgeParameters.lanConfig.openHABdefaultGW.toString()); break; case BRIDGE_DHCP: newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABenabledDHCP); - ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.CHANNEL_BRIDGE_DHCP, + ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.PROPERTY_BRIDGE_DHCP, thisBridgeHandler.bridgeParameters.lanConfig.openHABenabledDHCP.toString()); default: } diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeWLANconfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeWLANconfig.java index 36380e554d638..5d542e00c9b3f 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeWLANconfig.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeWLANconfig.java @@ -72,12 +72,12 @@ private ChannelBridgeWLANconfig() { switch (itemType) { case BRIDGE_WLANSSID: newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABipAddress); - ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.CHANNEL_BRIDGE_WLANSSID, + ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.PROPERTY_BRIDGE_WLANSSID, thisBridgeHandler.bridgeParameters.wlanConfig.openHABwlanSSID.toString()); break; case BRIDGE_WLANPASSWORD: newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABsubnetMask); - ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.CHANNEL_BRIDGE_WLANPASSWORD, + ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.PROPERTY_BRIDGE_WLANPASSWORD, thisBridgeHandler.bridgeParameters.wlanConfig.openHABwlanPassword.toString()); break; default: diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingScenes.java index 456faa821d76e..040c0b0295e02 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingScenes.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingScenes.java @@ -50,7 +50,6 @@ public class VeluxExistingScenes { // Constructor methods public VeluxExistingScenes() { - logger.trace("VeluxExistingScenes(constructor) called."); existingScenesBySceneName = new ConcurrentHashMap(); memberCount = 0; logger.trace("VeluxExistingScenes(constructor) done."); @@ -69,8 +68,8 @@ public boolean isRegistered(VeluxScene scene) { } public boolean register(VeluxScene newScene) { - logger.trace("register({}) called.", newScene); if (isRegistered(newScene)) { + logger.trace("register() ignoring scene {} as already known.", newScene); return false; } logger.trace("register() registering new scene {}.", newScene); diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/Localization.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/Localization.java index 0897e51c6c314..76cc54d637850 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/Localization.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/Localization.java @@ -97,12 +97,10 @@ public Localization(final LocaleProvider localeProvider, final TranslationProvid * @return localizedMessageString the resulted message of type {@link String}. */ public String getText(String key, Object... arguments) { - logger.trace("getText({}) called.", key); - if (i18nProvider == null) { + logger.trace("getText() returns default as no i18nProvider existant."); return key; } - Bundle bundle = FrameworkUtil.getBundle(this.getClass()).getBundleContext().getBundle(); Locale locale = localeProvider.getLocale(); String defaultText = OPENBRACKET.concat(key).concat(CLOSEBRACKET); From 9bb2b50e18f98967ee0afd680c2d3132a78a094c Mon Sep 17 00:00:00 2001 From: Guenther Schreiner Date: Mon, 3 Feb 2020 16:23:36 +0100 Subject: [PATCH 7/9] [velux] Fixed logging. Signed-off-by: Guenther Schreiner --- .../velux/internal/bridge/slip/SCrunProductDiscovery.java | 2 +- .../binding/velux/internal/handler/VeluxBridgeHandler.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductDiscovery.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductDiscovery.java index 6b906a22d3f6c..dbbb97d1bbae7 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductDiscovery.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductDiscovery.java @@ -103,7 +103,7 @@ public void setResponse(short responseCommand, byte[] thisResponseData, boolean Packet responseData = new Packet(thisResponseData); switch (Command.get(responseCommand)) { case GW_CS_DISCOVER_NODES_CFM: - logger.trace("setResponse({}): received confirmation for discovery mode."); + logger.trace("setResponse(): received confirmation for discovery mode."); break; case GW_CS_DISCOVER_NODES_NTF: diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBridgeHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBridgeHandler.java index 94cb40133b40f..5b847434f0dc3 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBridgeHandler.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBridgeHandler.java @@ -264,7 +264,7 @@ public void initialize() { try { refreshOpenHAB(); } catch (RuntimeException e) { - logger.warn("Exception occurred during activated refresh scheduler: {}.", e); + logger.warn("Exception occurred during activated refresh scheduler: {}.", e.getMessage()); } }, this.veluxBridgeConfiguration.refreshMSecs, this.veluxBridgeConfiguration.refreshMSecs, TimeUnit.MILLISECONDS); From 72bf5a317cbfcf4a2f0f391f3be99dffd859314e Mon Sep 17 00:00:00 2001 From: Guenther Schreiner Date: Tue, 25 Feb 2020 15:39:51 +0100 Subject: [PATCH 8/9] Update bundles/org.openhab.binding.velux/pom.xml Co-Authored-By: J-N-K Signed-off-by: Guenther Schreiner --- bundles/org.openhab.binding.velux/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.velux/pom.xml b/bundles/org.openhab.binding.velux/pom.xml index 1876fcb2aa2e5..7f7534990e766 100644 --- a/bundles/org.openhab.binding.velux/pom.xml +++ b/bundles/org.openhab.binding.velux/pom.xml @@ -9,7 +9,7 @@ org.openhab.addons.bundles org.openhab.addons.reactor.bundles - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT org.openhab.binding.velux From bea824b93777c44be300cd3eb3d3fffea5143025 Mon Sep 17 00:00:00 2001 From: Guenther Schreiner Date: Tue, 25 Feb 2020 15:58:17 +0100 Subject: [PATCH 9/9] [velux] Reduced log level during initialization.. Co-Authored-By: J-N-K Signed-off-by: Guenther Schreiner --- .../java/org/openhab/binding/velux/internal/VeluxBinding.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java index 3f11984c1e095..fbcdaa68abbc0 100644 --- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java @@ -108,7 +108,7 @@ public VeluxBinding(@Nullable VeluxBridgeConfiguration uncheckedConfiguration) { */ public VeluxBridgeConfiguration checked() { logger.trace("checked() called."); - logger.info("{}Config[{}={},{}={},{}={},{}={},{}={},{}={},{}={},{}={},{}={},{}={}]", + logger.debug("{}Config[{}={},{}={},{}={},{}={},{}={},{}={},{}={},{}={},{}={},{}={}]", VeluxBindingConstants.BINDING_ID, VeluxBridgeConfiguration.BRIDGE_PROTOCOL, protocol, VeluxBridgeConfiguration.BRIDGE_IPADDRESS, this.ipAddress, VeluxBridgeConfiguration.BRIDGE_TCPPORT, tcpPort, VeluxBridgeConfiguration.BRIDGE_PASSWORD, password.replaceAll(".", "*"),

    + * See KLF200 + * GatewayState value Description + *