+ * Accessible via + * {@link org.openhab.binding.velux.internal.VeluxBindingConfig#getBindingItemType + * getBindingItemType} as representation of the Velux device is filed in the Velux bridge.
+ * 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).
+ * 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: + *+ * 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: + *
+ * accessable via + * {@link org.openhab.binding.velux.internal.VeluxRSBindingConfig#getBindingItemType + * getBindingItemType} as representation of the Velux device is filed in the Velux bridge.
+ * 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).
+ * 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
+ * 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.
+ *
+ * If possible, it should be overwritten by protocol specific implementation.
+ *
+ * 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.internal.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/internal/bridge/VeluxBridgeActuators.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeActuators.java
new file mode 100644
index 0000000000000..8193fd2af431f
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeActuators.java
@@ -0,0 +1,168 @@
+/**
+ * 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.bridge;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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;
+
+/**
+ * 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:
+ *
+ * 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.internal.things.VeluxExistingProducts
+ * VeluxExistingProducts}.
+ *
+ * @param bridge Initialized Velux bridge (communication) handler.
+ * @return true if successful, and false otherwise.
+ */
+ public boolean getProducts(VeluxBridge bridge) {
+ logger.trace("getProducts() called.");
+
+ GetProducts bcp = bridge.bridgeAPI().getProducts();
+ GetProduct bcpSbS = bridge.bridgeAPI().getProduct();
+ if ((bcpSbS != null) && !bridge.bridgeInstance.veluxBridgeConfiguration().isBulkRetrievalEnabled) {
+ logger.trace("getProducts() working on step-by-step retrieval.");
+ for (int nodeId = 0; nodeId < VeluxKLFAPI.KLF_SYSTEMTABLE_MAX
+ && nodeId < VELUXBINDING_SYSTEMTABLE_MAX; nodeId++) {
+ 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);
+ }
+ }
+ }
+ }
+ } 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);
+ }
+ }
+ } else {
+ logger.trace("getProducts() finished with failure.");
+ return false;
+ }
+ }
+ logger.debug("getProducts() finally has found products {}.", channel.existingProducts);
+ return true;
+ }
+
+ /**
+ * 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) {
+ int numberOfActuators = channel.existingProducts.getNoMembers();
+ if (numberOfActuators == 0) {
+ logger.trace("autoRefresh(): is about to fetch existing products.");
+ getProducts(bridge);
+ }
+ return (numberOfActuators > 0);
+ }
+
+}
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
new file mode 100644
index 0000000000000..4dc15c8b755aa
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDetectProducts.java
@@ -0,0 +1,82 @@
+/**
+ * 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.bridge;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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;
+
+/**
+ * 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:
+ *
+ * It therefore provides a method
+ *
+ * It therefore provides a method
+ *
+ * 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/internal/bridge/VeluxBridgeGetFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetFirmware.java
new file mode 100644
index 0000000000000..ae3d62f36d1b9
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetFirmware.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.bridge;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.smarthome.core.library.types.StringType;
+import org.openhab.binding.velux.internal.VeluxBindingConstants;
+import org.openhab.binding.velux.internal.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:
+ *
+ * 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/internal/bridge/VeluxBridgeGetHouseStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetHouseStatus.java
new file mode 100644
index 0000000000000..745ae6694b781
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetHouseStatus.java
@@ -0,0 +1,62 @@
+/**
+ * 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.bridge;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.velux.internal.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/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..2e7a05fbafac1
--- /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-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.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
+ *
+ * Configuration
+ *
+ * Status
+ *
+ * It provides the following methods:
+ *
+ * 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/internal/bridge/VeluxBridgeProvider.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeProvider.java
new file mode 100644
index 0000000000000..5339554b6bfa3
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeProvider.java
@@ -0,0 +1,59 @@
+/**
+ * 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.bridge;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+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.
+ *
+ * Communication
+ *
+ * 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/internal/bridge/VeluxBridgeRunScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunScene.java
new file mode 100644
index 0000000000000..c4509699b3f8c
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunScene.java
@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.velux.internal.bridge;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.velux.internal.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/internal/bridge/VeluxBridgeScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeScenes.java
new file mode 100644
index 0000000000000..4cee44d2b7864
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeScenes.java
@@ -0,0 +1,130 @@
+/**
+ * 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.bridge;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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;
+
+/**
+ * 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:
+ *
+ * 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.internal.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/internal/bridge/VeluxBridgeSetHouseStatusMonitor.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetHouseStatusMonitor.java
new file mode 100644
index 0000000000000..6e508b981e27f
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetHouseStatusMonitor.java
@@ -0,0 +1,63 @@
+/**
+ * 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.bridge;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.velux.internal.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/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..ffae910c35f42
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetLimitation.java
@@ -0,0 +1,94 @@
+/**
+ * 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.bridge;
+
+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;
+
+/**
+ * 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
+ *
+ * It therefore provides a method
+ *
+ * It provides the following methods:
+ *
+ *
+ * @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:
+ *
+ * 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/internal/bridge/common/BridgeAPI.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeAPI.java
new file mode 100644
index 0000000000000..fe89f92816c61
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeAPI.java
@@ -0,0 +1,108 @@
+/**
+ * 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.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:
+ *
+ * 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 {
+
+ Login login();
+
+ Logout logout();
+
+ @Nullable
+ SetHouseStatusMonitor setHouseStatusMonitor();
+
+ @Nullable
+ GetHouseStatus getHouseStatus();
+
+ RunProductDiscovery runProductDiscovery();
+
+ RunProductSearch runProductSearch();
+
+ RunProductIdentification runProductIdentification();
+
+ GetDeviceStatus getDeviceStatus();
+
+ GetFirmware getFirmware();
+
+ GetLANConfig getLANConfig();
+
+ GetWLANConfig getWLANConfig();
+
+ GetProducts getProducts();
+
+ @Nullable
+ GetProduct getProduct();
+
+ @Nullable
+ GetProductLimitation getProductLimitation();
+
+ @Nullable
+ SetProductLimitation setProductLimitation();
+
+ @Nullable
+ RunProductCommand runProductCommand();
+
+ GetScenes getScenes();
+
+ SetSceneVelocity setSceneVelocity();
+
+ RunScene runScene();
+
+}
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
new file mode 100644
index 0000000000000..c71e26b602379
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeCommunicationProtocol.java
@@ -0,0 +1,52 @@
+/**
+ * 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.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:
+ *
+ * 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:
+ *
+ * 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:
+ *
+ * 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/internal/bridge/common/GetLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetLANConfig.java
new file mode 100644
index 0000000000000..f65d55d49822f
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetLANConfig.java
@@ -0,0 +1,43 @@
+/**
+ * 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.bridge.common;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.velux.internal.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:
+ *
+ * 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:
+ *
+ * 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:
+ *
+ * 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:
+ *
+ * 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:
+ *
+ * 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:
+ *
+ * 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:
+ *
+ * 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/internal/bridge/common/RunProductCommand.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductCommand.java
new file mode 100644
index 0000000000000..e70247b0a9913
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductCommand.java
@@ -0,0 +1,44 @@
+/**
+ * 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.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:
+ *
+ * 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/internal/bridge/common/RunProductIdentification.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductIdentification.java
new file mode 100644
index 0000000000000..5b7c813c7791f
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductIdentification.java
@@ -0,0 +1,45 @@
+/**
+ * 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.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:
+ *
+ * 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/internal/bridge/common/RunScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunScene.java
new file mode 100644
index 0000000000000..4c31645a087b1
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunScene.java
@@ -0,0 +1,55 @@
+/**
+ * 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.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:
+ *
+ * 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:
+ *
+ * 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:
+ *
+ * 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:
+ *
+ * Message semantic: Retrieval of current bridge state.
+ *
+ *
+ * It defines information 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.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.internal.bridge.json.JsonVeluxBridge JsonVeluxBridge}
+ * for serializing.
+ *
+ * Resulting JSON:
+ *
+ *
+ * Expected JSON (sample):
+ *
+ *
+ * Message semantic: Retrieval of Bridge configuration.
+ *
+ *
+ * It defines information 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.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.internal.bridge.json.JsonVeluxBridge JsonVeluxBridge}
+ * for serializing.
+ *
+ * Resulting JSON:
+ *
+ *
+ * Used within structure {@link JCgetFirmware} to describe the software of the Bridge.
+ */
+ @NonNullByDefault
+ 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.internal.bridge.json.JsonVeluxBridge} for
+ * deserializing with including component access methods
+ *
+ * Expected JSON (sample):
+ *
+ *
+ * Message semantic: Retrieval of LAN configuration.
+ *
+ *
+ * It defines information 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.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:
+ *
+ *
+ * Used within structure {@link JCgetLANConfig} to describe the network connectivity of the Bridge.
+ *
+ *
+ * Expected JSON (sample):
+ *
+ *
+ * Message semantic: Retrieval of products.
+ *
+ *
+ * It defines information 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.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
+ *
+ *
+ * Resulting JSON:
+ *
+ *
+ * Expected JSON (sample):
+ *
+ *
+ * Message semantic: Retrieval of scene configurations.
+ *
+ *
+ * It defines information 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.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.
+ *
+ *
+ * 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.
+ *
+ *
+ * Resulting JSON:
+ *
+ *
+ * Message semantic: Retrieval of WLAN configuration.
+ *
+ *
+ * It defines information 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.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.internal.bridge.json.JsonVeluxBridge JsonVeluxBridge}
+ * for serializing.
+ *
+ * Resulting JSON:
+ *
+ *
+ * Used within structure {@link JCgetWLANConfig} to describe the network connectivity of the Bridge.
+ *
+ *
+ * Expected JSON (sample):
+ *
+ *
+ * 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.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider}
+ * as described by the {@link org.openhab.binding.velux.internal.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
+ */
+ @NonNullByDefault
+ private static class ParamsLogin {
+ @SuppressWarnings("unused")
+ private String password = VeluxBindingConstants.UNKNOWN;
+ }
+
+ /**
+ * Bridge I/O Request message used by {@link JsonVeluxBridge}
+ * for serializing.
+ *
+ * Resulting JSON:
+ *
+ *
+ * Expected JSON (sample):
+ *
+ *
+ * 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.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider}
+ * as described by the {@link org.openhab.binding.velux.internal.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:
+ *
+ *
+ * Expected JSON (sample):
+ *
+ *
+ * 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.internal.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:
+ *
+ *
+ * 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.internal.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
+ */
+ @NonNullByDefault
+ 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):
+ *
+ *
+ * Expected JSON (sample):
+ *
+ *
+ * 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.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider}
+ * as described by the {@link org.openhab.binding.velux.internal.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.internal.bridge.json.JsonVeluxBridge JsonVeluxBridge}
+ * for serializing.
+ *
+ * Resulting JSON:
+ *
+ *
+ * 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.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider}
+ * as described by the {@link org.openhab.binding.velux.internal.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
+ */
+ @NonNullByDefault
+ private static class ParamsRunScene {
+ @SuppressWarnings("unused")
+ private int id;
+ }
+
+ /**
+ * Bridge I/O Request message used by {@link JsonVeluxBridge}
+ * for serializing.
+ *
+ * Resulting JSON (sample):
+ *
+ *
+ * Expected JSON (sample):
+ *
+ *
+ * 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.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider}
+ * as described by the {@link org.openhab.binding.velux.internal.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
+ */
+ @NonNullByDefault
+ 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):
+ *
+ *
+ * Expected JSON (sample):
+ *
+ *
+ * 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:
+ *
+ * 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.internal.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 @Nullable SetProductLimitation setProductLimitation() {
+ 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/internal/bridge/json/JsonBridgeCommunicationProtocol.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonBridgeCommunicationProtocol.java
new file mode 100644
index 0000000000000..59f77ab20202b
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonBridgeCommunicationProtocol.java
@@ -0,0 +1,93 @@
+/**
+ * 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.bridge.json;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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.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.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.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider}.
+ *
+ * (Methods in this interface for the appropriate interaction:
+ *
+ * It provides methods for pre- and postcommunication
+ * as well as a common method for the real communication.
+ *
+ * The following class access methods exist:
+ *
+ * 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.internal.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
+ * 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 long lastCommunication() {
+ return lastCommunicationInMSecs;
+ }
+
+ /**
+ * Returns the timestamp in milliseconds since Unix epoch
+ * of last successful communication.
+ *
+ * @return timestamp in milliseconds.
+ */
+ @Override
+ public 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
+ * 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 org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol
+ * SlipBridgeCommunicationProtocol}.
+ *
+ * Methods in addition to the mentioned interface:
+ *
+ * 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 {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol
+ * SlipBridgeCommunicationProtocol}.
+ *
+ * Methods in addition to the mentioned interface:
+ *
+ * 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.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:
+ *
+ * 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/internal/bridge/slip/SCgetLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLANConfig.java
new file mode 100644
index 0000000000000..38099fae545ea
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLANConfig.java
@@ -0,0 +1,153 @@
+/**
+ * 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.bridge.slip;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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;
+
+/**
+ * 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.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:
+ *
+ * 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:
+ *
+ * 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 {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol
+ * SlipBridgeCommunicationProtocol}.
+ *
+ * Methods in addition to the mentioned interface:
+ *
+ * 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 {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol
+ * SlipBridgeCommunicationProtocol}.
+ *
+ * Methods in addition to the mentioned interface:
+ *
+ * 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 org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol
+ * SlipBridgeCommunicationProtocol}.
+ *
+ * Methods in addition to the mentioned interface:
+ *
+ * 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/internal/bridge/slip/SCgetWLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetWLANConfig.java
new file mode 100644
index 0000000000000..532544e8dae85
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetWLANConfig.java
@@ -0,0 +1,124 @@
+/**
+ * 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.bridge.slip;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+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;
+
+/**
+ * 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.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider}
+ * as described by the interface {@link SlipBridgeCommunicationProtocol}.
+ *
+ * Methods in addition to the mentioned interface:
+ *
+ * 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 org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol
+ * SlipBridgeCommunicationProtocol}.
+ *
+ * Methods in addition to the mentioned interface:
+ *
+ * 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 org.openhab.binding.velux.internal.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/internal/bridge/slip/SCrunProductCommand.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductCommand.java
new file mode 100644
index 0000000000000..da54c23d71bcf
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductCommand.java
@@ -0,0 +1,313 @@
+/**
+ * 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.bridge.slip;
+
+import java.util.Random;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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;
+
+/**
+ * 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.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:
+ *
+ * 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 org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol
+ * SlipBridgeCommunicationProtocol}.
+ *
+ * There are no methods in addition to the mentioned interface.
+ *
+ * @see SlipBridgeCommunicationProtocol
+ *
+ * @author Guenther Schreiner - Initial contribution.
+ */
+@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/internal/bridge/slip/SCrunProductIdentification.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductIdentification.java
new file mode 100644
index 0000000000000..1b62afa82ed25
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductIdentification.java
@@ -0,0 +1,204 @@
+/**
+ * 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.bridge.slip;
+
+import java.util.Random;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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;
+
+/**
+ * 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.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:
+ *
+ * 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/internal/bridge/slip/SCrunProductSearch.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductSearch.java
new file mode 100644
index 0000000000000..ee10d5e57d7bf
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductSearch.java
@@ -0,0 +1,113 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.velux.internal.bridge.slip;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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;
+
+/**
+ * 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.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider}
+ * as described by the interface {@link SlipBridgeCommunicationProtocol}.
+ *
+ * Methods in addition to the mentioned interface:
+ *
+ * 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 org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol
+ * SlipBridgeCommunicationProtocol}.
+ *
+ * Methods in addition to the mentioned interface:
+ *
+ * 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/internal/bridge/slip/SCsetHouseStatusMonitor.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetHouseStatusMonitor.java
new file mode 100644
index 0000000000000..a7d4ec71e3186
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetHouseStatusMonitor.java
@@ -0,0 +1,145 @@
+/**
+ * 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.bridge.slip;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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;
+
+/**
+ * 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.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:
+ *
+ * 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:
+ *
+ * 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 org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol
+ * SlipBridgeCommunicationProtocol}.
+ *
+ * Methods in addition to the mentioned interface:
+ *
+ * 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/internal/bridge/slip/SlipBridgeAPI.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeAPI.java
new file mode 100644
index 0000000000000..c2e6d652bd1fc
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeAPI.java
@@ -0,0 +1,214 @@
+/**
+ * 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.bridge.slip;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+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;
+
+/**
+ * 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:
+ *
+ * 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 SetProductLimitation SETPRODUCTLIMITATION = new SCsetLimitation();
+ 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 @Nullable SetProductLimitation setProductLimitation() {
+ return SETPRODUCTLIMITATION;
+ }
+
+ @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/internal/bridge/slip/SlipBridgeCommunicationProtocol.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeCommunicationProtocol.java
new file mode 100644
index 0000000000000..265cc719eb223
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeCommunicationProtocol.java
@@ -0,0 +1,85 @@
+/**
+ * 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.bridge.slip;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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:
+ * 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:
+ *
+ * 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/internal/bridge/slip/SlipVeluxBridge.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipVeluxBridge.java
new file mode 100644
index 0000000000000..55db3eb064627
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipVeluxBridge.java
@@ -0,0 +1,385 @@
+/**
+ * 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.bridge.slip;
+
+import java.text.ParseException;
+import java.util.TreeSet;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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;
+
+/**
+ * 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.
+ *
+ * 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 *****
+ */
+
+ 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.internal.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
+ * 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 long lastCommunication() {
+ return connection.lastCommunication();
+ }
+
+ /**
+ * Returns the timestamp in milliseconds since Unix epoch
+ * of last successful communication.
+ *
+ * @return timestamp in milliseconds.
+ */
+ @Override
+ public 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");
+
+ long communicationStartInMSecs = System.currentTimeMillis();
+
+ 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();
+
+ 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.");
+ 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 {
+ 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) {
+ 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, 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/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
new file mode 100644
index 0000000000000..634c5cafb67c4
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/Connection.java
@@ -0,0 +1,241 @@
+/**
+ * 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.bridge.slip.io;
+
+import java.io.IOException;
+import java.net.ConnectException;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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;
+
+/**
+ * 2nd Level I/O interface towards the Velux bridge.
+ * It provides methods for a pure client/server communication.
+ *
+ * The following class access methods exist:
+ *
+ * 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
+ * This method blocks until input data is available, end of file is
+ * detected, or an exception is thrown until the given timeout.
+ *
+ *
+ * If
+ * The first byte read is stored into element
+ * In every case, elements
+ * 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.
+ *
+ * Methods available:
+ *
+ * Static methods are:
+ *
+ * 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/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
new file mode 100644
index 0000000000000..b351d0e05ee16
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/Packet.java
@@ -0,0 +1,280 @@
+/**
+ * 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.bridge.slip.utils;
+
+import java.util.Arrays;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Utility class for handling of packets i.e. array of bytes.
+ *
+ *
+ * Methods available:
+ *
+ * Note: Big-endian LSB-0 encoding.
+ *
+ * Note: Big-endian LSB-0 encoding.
+ *
+ * Note: Big-endian LSB-0 encoding.
+ *
+ * Note: Big-endian LSB-0 encoding.
+ *
+ * Note: Any trailing null char will be eliminated.
+ *
+ * Note: The trailing null char will not be stored.
+ *
+ * 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.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider}
+ * as described by the {@link org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol
+ * BridgeCommunicationProtocol}.
+ *
+ * Methods available:
+ *
+ * Methods available:
+ *
+ * It contains the factory default values as well.
+ *
+ *
+ * @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..df3b51f7ed3c0
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxThingConfiguration.java
@@ -0,0 +1,117 @@
+/**
+ * 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.config;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.smarthome.config.core.Configuration;
+import org.openhab.binding.velux.internal.VeluxBindingConstants;
+import org.openhab.binding.velux.internal.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:
+ *
+ * Configuration for the channel scene:
+ *
+ * Configuration for the channels actuator, rollershutter and window:
+ *
+ * Part of the {@link #serial Configuration for the channels actuator, rollershutter and window}.
+ *
+ * Part of the {@link #serial Configuration for the channels actuator, rollershutter and window}.
+ *
+ * Part of the {@link #serial Configuration for the channels actuator, rollershutter and window}.
+ *
+ * Configuration for the channel virtualshutter:
+ *
+ * Additionally it contains an internal variable for keeping the actual virtual shutter level.
+ *
+ * Private part of the {@link #sceneLevels Configuration for the channel virtualshutter}.
+ *
+ *
+ * @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..14c6e4454edc3
--- /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-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
+ */
+/**
+ * Generic 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/internal/discovery/VeluxDiscoveryService.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/VeluxDiscoveryService.java
new file mode 100644
index 0000000000000..081ebd25ee378
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/VeluxDiscoveryService.java
@@ -0,0 +1,273 @@
+/**
+ * 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.discovery;
+
+import static org.openhab.binding.velux.internal.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.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.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.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 actuators and scenes on the current Velux Bridge.
+ *
+ * @author Guenther Schreiner - Initial contribution.
+ */
+//
+// To-be-discussed: 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);
+
+ // 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
+ *
+ * Initializes the {@link VeluxDiscoveryService} without any further information.
+ */
+ public VeluxDiscoveryService() {
+ super(VeluxBindingConstants.SUPPORTED_THINGS_ITEMS, DISCOVER_TIMEOUT_SECONDS);
+ 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
+ *
+ * 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, Localization localizationHandler) {
+ super(VeluxBindingConstants.SUPPORTED_THINGS_ITEMS, DISCOVER_TIMEOUT_SECONDS);
+ 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
+ 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");
+ 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);
+
+ 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.bridgeHandler 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);
+
+ 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();
+ 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);
+ 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.");
+ thingTypeUID = THING_TYPE_VELUX_WINDOW;
+ isInverted = true;
+ break;
+
+ case SLIDER_SHUTTER:
+ logger.trace("discoverProducts(): creating rollershutter.");
+ thingTypeUID = THING_TYPE_VELUX_ROLLERSHUTTER;
+ break;
+
+ default:
+ logger.trace("discoverProducts(): creating actuator.");
+ thingTypeUID = THING_TYPE_VELUX_ACTUATOR;
+ }
+ ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, label);
+
+ DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID)
+ .withProperty(VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER, identifier)
+ .withProperty(VeluxBindingProperties.PROPERTY_ACTUATOR_NAME, actuatorName)
+ .withProperty(VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED, isInverted)
+ .withRepresentationProperty(VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER)
+ .withBridge(bridgeUID).withLabel(actuatorName).build();
+ 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/internal/discovery/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/package-info.java
new file mode 100644
index 0000000000000..bdfa9e4337563
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/package-info.java
@@ -0,0 +1,19 @@
+/**
+ * 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 discovery of things automatically and user-initiated after defining a valid Velux bridge
+ * configuration.
+ *
+ * @author Guenther Schreiner - Initial contribution
+ */
+package org.openhab.binding.velux.internal.discovery;
diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/factory/VeluxHandlerFactory.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/factory/VeluxHandlerFactory.java
new file mode 100644
index 0000000000000..4fe4a637f86b6
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/factory/VeluxHandlerFactory.java
@@ -0,0 +1,207 @@
+/**
+ * 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.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;
+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.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;
+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;
+
+/**
+ * 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 final Map
+ * Noninstantiable utility class
+ *
+ *
+ * @param bridge which will be scrutinized for things.
+ * @return channelUIDs of type {@link Set} of {@link ChannelUID}s.
+ */
+ static Set
+ *
+ * @param bridge which will be scrutinized for things.
+ * @return channelUIDs of type {@link Set} of {@link ChannelUID}s.
+ */
+ static Set
+ * This class implements the Channel position of the Thing actuator:
+ *
+ * Information retrieval by method {@link #handleRefresh}.
+ * Sending commands and value updates by method {@link #handleCommand}.
+ * This class implements the Channel position of the Thing actuator:
+ *
+ * Information retrieval by method {@link #handleRefresh}.
+ * Sending commands and value updates by method {@link #handleCommand}.
+ * This class implements the Channel check of the Thing klf200 :
+ *
+ * Information retrieval by method {@link #handleRefresh}.
+ * This class implements the Channel doDetection of the Thing klf200 :
+ *
+ * Information retrieval by method {@link #handleRefresh}.
+ * Sending commands and value updates by method {@link #handleCommand}.
+ * This class implements the Channel firmware of the Thing klf200 :
+ *
+ * Information retrieval by method {@link #handleRefresh}.
+ * This class implements the Channels ipAddress, subnetMask, defaultGW and DHCP of the Thing
+ * klf200 :
+ *
+ * Information retrieval by method {@link #handleRefresh}.
+ * This class implements the Channel products of the Thing klf200:
+ *
+ * Information retrieval by method {@link #handleRefresh}.
+ * This class implements the Channel scenes of the Thing klf200:
+ *
+ * Information retrieval by method {@link #handleRefresh}.
+ * This class implements the Channel status of the Thing klf200:
+ *
+ * Information retrieval by method {@link #handleRefresh}.
+ * This class implements the Channels WLANSSID and WLANPassword of the Thing klf200 :
+ *
+ * Information retrieval by method {@link #handleRefresh}.
+ * This class implements the Channel VShutter :
+ *
+ * Information retrieval by method {@link #handleRefresh}.
+ * Sending commands and value updates by method {@link #handleCommand}.
+ * This class implements the Channel action of the Thing scene :
+ *
+ * Information retrieval by method {@link #handleRefresh}.
+ * Sending commands and value updates by method {@link #handleCommand}.
+ * This class implements the Channel silentMode of the Thing scene :
+ *
+ * Information retrieval by method {@link #handleRefresh}.
+ * Sending commands and value updates by method {@link #handleCommand}.
+ * This class implements the Channel position of the Thing vshutter :
+ *
+ * Information retrieval by method {@link #handleRefresh}.
+ * Sending commands and value updates by method {@link #handleCommand}.
+ * This class implements the Channel VShutter :
+ *
+ * Information retrieval by method {@link #handleRefresh}.
+ * Beside the normal thing handling introduced by {@link ExtendedBaseThingHandler}, it provides a method:
+ *
+ * Separated into this private method to deal with the deprecated method.
+ *
+ * Provided for instrumentation of factory class to update this set of information.
+ *
+ * It implements the communication between OpenHAB and the Velux Bridge:
+ *
+ * Sending commands and value updates.
+ * 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);
+
+ // Class internal
+
+ /**
+ * Scheduler for continuous refresh by scheduleWithFixedDelay.
+ */
+ private @Nullable ScheduledFuture> refreshJob = null;
+
+ /**
+ * Counter of refresh invocations by {@link refreshJob}.
+ */
+ private int refreshCounter = 0;
+
+ /**
+ * Dedicated thread pool for the long-running bridge communication threads.
+ */
+ private ScheduledExecutorService handleScheduler = ThreadPoolManager
+ .getScheduledPool(VeluxBindingConstants.BINDING_ID);
+
+ private VeluxBridge myJsonBridge = new JsonVeluxBridge(this);
+ private VeluxBridge mySlipBridge = new SlipVeluxBridge(this);
+
+ /*
+ * **************************************
+ * ***** Default visibility Objects *****
+ */
+
+ VeluxBridge thisBridge = myJsonBridge;
+ public BridgeParameters bridgeParameters = new BridgeParameters();
+ Localization localization;
+
+ /**
+ * Mapping from ChannelUID to class Thing2VeluxActuator, which return Velux device information, probably cached.
+ */
+ ConcurrentHashMap
+ * Set of information retrieved from the bridge/gateway:
+ *
+ * Separated into this private method to deal with the deprecated method.
+ *
+ * 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}.
+ */
+ private synchronized void handleCommandScheduled(ChannelUID channelUID, Command command) {
+ logger.trace("handleCommandScheduled({}): command {} on channel {}.", Thread.currentThread(), command,
+ channelUID.getAsString());
+ logger.debug("handleCommandScheduled({},{}) called.", channelUID.getAsString(), command);
+
+ /*
+ * ===========================================================
+ * Common part
+ */
+
+ if (veluxBridgeConfiguration.isProtocolTraceEnabled) {
+ 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("{} Cannot determine type of Channel {}, ignoring command {}.",
+ VeluxBindingConstants.LOGGING_CONTACT, channelUID, command);
+ logger.trace("handleCommandScheduled() aborting.");
+ 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();
+ }
+
+ syncChannelsWithProducts();
+
+ if (command instanceof RefreshType) {
+ /*
+ * ===========================================================
+ * Refresh part
+ */
+ logger.trace("handleCommandScheduled(): work on refresh.");
+ if (!itemType.isReadable()) {
+ 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_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);
+ }
+ }
+ 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/internal/handler/VeluxHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxHandler.java
new file mode 100644
index 0000000000000..bc0b780b36a26
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxHandler.java
@@ -0,0 +1,128 @@
+/**
+ * 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 java.util.Map;
+import java.util.Map.Entry;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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.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.handler.utils.ExtendedBaseThingHandler;
+import org.openhab.binding.velux.internal.utils.Localization;
+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);
+
+ VeluxThingConfiguration configuration = new VeluxThingConfiguration();
+
+ public VeluxHandler(Thing thing, Localization localization) {
+ super(thing);
+ logger.trace("VeluxHandler(thing={},localization={}) constructor called.", thing, localization);
+ }
+
+ @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() {
+ 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);
+ }
+ }
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ 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 {
+ 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.");
+ }
+
+ @Override
+ public void handleConfigurationUpdate(Map
+ * It is recommended to extend this abstract base class.
+ *
+ *
+ * @author Guenther Schreiner - Initial contribution.
+ */
+@NonNullByDefault
+public abstract class ExtendedBaseThingHandler extends BaseThingHandler {
+
+ /*
+ * ************************
+ * ***** Constructors *****
+ */
+
+ /**
+ * @see BaseThingHandler
+ * @param thing which will be created.
+ */
+ protected 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
+ * If {@code propertyValue} is {@code null}, {@link UnDefType#NULL} will be returned.
+ *
+ * Copied/adapted from the org.openhab.binding.koubachi binding.
+ *
+ *
+ * @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.
+ *
+ * Noninstantiable utility class
+ *
+ *
+ * @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.
+ *
+ * Noninstantiable utility class
+ *
+ *
+ * @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
new file mode 100644
index 0000000000000..2bf7252673457
--- /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-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 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/things/VeluxExistingProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingProducts.java
new file mode 100644
index 0000000000000..cbced7517a9f8
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingProducts.java
@@ -0,0 +1,205 @@
+/**
+ * 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.things;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.velux.internal.VeluxBindingConstants;
+import org.openhab.binding.velux.internal.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:
+ *
+ * The following class access methods exist:
+ *
+ * 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/internal/things/VeluxGwLAN.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwLAN.java
new file mode 100644
index 0000000000000..1a34111901067
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwLAN.java
@@ -0,0 +1,77 @@
+/**
+ * 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.things;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.velux.internal.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/internal/things/VeluxGwState.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwState.java
new file mode 100644
index 0000000000000..fc7a7455dc887
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwState.java
@@ -0,0 +1,175 @@
+/**
+ * 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.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.
+ *
+ * See KLF200
+ * GatewayState value Description
+ *
+ * Methods in handle this type of information:
+ * *
+ *
+ * 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/internal/things/VeluxKLFAPI.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxKLFAPI.java
new file mode 100644
index 0000000000000..7662a4e783320
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxKLFAPI.java
@@ -0,0 +1,358 @@
+/**
+ * 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.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:
+ *
+ *
+ * @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:
+ *
+ * Methods available:
+ *
+ * See Appendix
+ * 3: List of Gateway commands
+ *
+ * Methods available:
+ *
+ * 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
+
+ @NonNullByDefault
+ public static class ProductBridgeIndex {
+
+ // Public definition
+ public 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;
+ }
+
+ @Override
+ public String toString() {
+ return Integer.toString(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) {
+ 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) {
+ 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) {
+ 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/internal/things/VeluxProductName.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductName.java
new file mode 100644
index 0000000000000..9f0cbfd64389c
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductName.java
@@ -0,0 +1,49 @@
+/**
+ * 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.things;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.velux.internal.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);
+
+ // 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/internal/things/VeluxProductPosition.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductPosition.java
new file mode 100644
index 0000000000000..8b0fda88963c4
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductPosition.java
@@ -0,0 +1,174 @@
+/**
+ * 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.things;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.smarthome.core.library.types.PercentType;
+import org.openhab.binding.velux.internal.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:
+ *
+ * Combined set of information which describes a current state of a single Velux product.
+ *
+ *
+ * @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/internal/things/VeluxProductSerialNo.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductSerialNo.java
new file mode 100644
index 0000000000000..4bf12768b5b0e
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductSerialNo.java
@@ -0,0 +1,138 @@
+/**
+ * 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.things;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * This class support handling of Serial Number used with Velux.
+ *
+ * Example:
+ *
+ * Combined set of information which describes a current state of a single Velux product.
+ *
+ * See KLF200
+ * List of actuator types and their use of Main Parameter and Functional Parameters
+ *
+ * Methods in handle this type of information:
+ *
+ * See KLF200
+ * Velocity parameter
+ *
+ * Methods in handle this type of information:
+ *
+ * Combined set of information with references towards multiple Velux product states.
+ *
+ * Methods in handle this type of information:
+ *
+ *
+ * @author Guenther Schreiner - Initial contribution
+ */
+@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;
+ }
+ }
+
+ /*
+ * ************************
+ * ***** Constructors *****
+ */
+
+ /**
+ * Constructor
+ *
+ * Initializes the {@link Localization} module without any framework informations.
+ */
+ 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;
+ }
+
+ /**
+ * 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 String getText(String key, Object... arguments) {
+ 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);
+
+ 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/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..627aba075890a
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/ManifestInformation.java
@@ -0,0 +1,57 @@
+/**
+ * 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.utils;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.osgi.framework.FrameworkUtil;
+
+/**
+ * This is a helper class for dealing with information from MANIFEST file.
+ *
+ * It provides the following methods:
+ *
+ *
+ * @author Guenther Schreiner - Initial contribution
+ */
+@NonNullByDefault
+public class ManifestInformation {
+
+ /*
+ * ************************
+ * ***** Constructors *****
+ */
+
+ /**
+ * Suppress default constructor for creating a non-instantiable class.
+ */
+ 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 = FrameworkUtil.getBundle(ManifestInformation.class).getBundleContext().getBundle()
+ .toString();
+ 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..4a90117dec99b
--- /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-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
+ */
+/**
+ * Generic utility classes (might not be limited to the Velux binding).
+ *
+ * @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..e58e8487cac24
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/package-info.java
@@ -0,0 +1,19 @@
+/**
+ * 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
+ */
+/**
+ *
+ * NOTE: All relevant classes of this binding are below the internal node.
+ *
+ * @author Guenther Schreiner - Initial contribution
+ */
+package org.openhab.binding.velux;
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 @@
+
+
+ *
+ *
+ * @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.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.
+ */
+ private static final int VELUXBINDING_SYSTEMTABLE_MAX = 16;
+
+ // Type definitions, class-internal variables
+
+ /**
+ * Actuator information consisting of:
+ *
+ *
+ */
+ @NonNullByDefault
+ public class Channel {
+ public VeluxExistingProducts existingProducts = new VeluxExistingProducts();
+ }
+
+ private Channel channel;
+
+ // Constructor methods
+
+ /**
+ * Constructor.
+ *
+ *
+ *
+ * @see VeluxBridgeProvider
+ *
+ * @author Guenther Schreiner - Initial contribution
+ */
+@NonNullByDefault
+public class VeluxBridgeDetectProducts {
+ private final Logger logger = LoggerFactory.getLogger(VeluxBridgeDetectProducts.class);
+
+ // 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);
+ }
+ }
+ } 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/internal/bridge/VeluxBridgeDeviceCheckLostNodes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceCheckLostNodes.java
new file mode 100644
index 0000000000000..7aa93ab84ec69
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceCheckLostNodes.java
@@ -0,0 +1,55 @@
+/**
+ * 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.bridge;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.velux.internal.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.
+ *
+ *
+ *
+ * @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/internal/bridge/VeluxBridgeDeviceStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceStatus.java
new file mode 100644
index 0000000000000..303bd36b1e2ef
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceStatus.java
@@ -0,0 +1,109 @@
+/**
+ * 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.bridge;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.smarthome.core.library.types.StringType;
+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;
+
+/**
+ * The {@link VeluxBridgeDeviceStatus} represents a complete set of transactions
+ * for querying device status on the Velux bridge.
+ *
+ *
+ *
+ * @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:
+ *
+ *
+ */
+ @NonNullByDefault
+ public class Channel {
+ public boolean isRetrieved = false;
+ public StringType gwState = new StringType(VeluxBindingConstants.UNKNOWN);
+ public StringType gwStateDescription = new StringType(VeluxBindingConstants.UNKNOWN);
+ }
+
+ private Channel channel;
+
+ // Constructor methods
+
+ /**
+ * Constructor.
+ *
+ *
+ *
+ * @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:
+ *
+ *
+ */
+ @NonNullByDefault
+ public class Channel {
+ public boolean isRetrieved = false;
+ public StringType firmwareVersion = new StringType(VeluxBindingConstants.UNKNOWN);
+ }
+
+ private Channel channel;
+
+ // Constructor methods
+
+ /**
+ * Constructor.
+ *
+ *
+ *
+ * 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/internal/bridge/VeluxBridgeInstance.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeInstance.java
new file mode 100644
index 0000000000000..edac94e74c4ae
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeInstance.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.velux.internal.bridge;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration;
+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.
+ *
+ *
+ *
+ *
+ *
+ *
+ * @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.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.internal.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/internal/bridge/VeluxBridgeLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeLANConfig.java
new file mode 100644
index 0000000000000..0e98d7ed23931
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeLANConfig.java
@@ -0,0 +1,115 @@
+/**
+ * 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.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.internal.VeluxBindingConstants;
+import org.openhab.binding.velux.internal.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.
+ *
+ *
+ *
+ * @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:
+ *
+ *
+ */
+ @NonNullByDefault
+ 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.
+ *
+ *
+ *
+ * @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/internal/bridge/VeluxBridgeRunProductCommand.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunProductCommand.java
new file mode 100644
index 0000000000000..91c98bdc2c091
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunProductCommand.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.velux.internal.bridge;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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;
+
+/**
+ * The {@link VeluxBridgeRunProductCommand} represents a complete set of transactions
+ * for executing a scene defined on the Velux bridge.
+ *
+ *
+ *
+ * @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:
+ *
+ *
+ */
+ @NonNullByDefault
+ public class Channel {
+ public VeluxExistingScenes existingScenes = new VeluxExistingScenes();
+ }
+
+ private Channel channel;
+
+ // Constructor methods
+
+ /**
+ * Constructor.
+ *
+ *
+ * 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, VeluxProductPosition 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.getPositionAsVeluxType());
+ 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, VeluxProductPosition 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.getPositionAsVeluxType());
+ 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/internal/bridge/VeluxBridgeSetSceneVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetSceneVelocity.java
new file mode 100644
index 0000000000000..d9c021c60299f
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetSceneVelocity.java
@@ -0,0 +1,61 @@
+/**
+ * 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.bridge;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.velux.internal.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.
+ *
+ *
+ * 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.trace("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/internal/bridge/VeluxBridgeWLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeWLANConfig.java
new file mode 100644
index 0000000000000..7949844d47b11
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeWLANConfig.java
@@ -0,0 +1,109 @@
+/**
+ * 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.bridge;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.smarthome.core.library.types.StringType;
+import org.openhab.binding.velux.internal.VeluxBindingConstants;
+import org.openhab.binding.velux.internal.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.
+ *
+ *
+ *
+ *
+ */
+ @NonNullByDefault
+ 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.
+ *
+ *
+ *
+ *
+ *
+ * @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/internal/bridge/common/GetDeviceStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetDeviceStatus.java
new file mode 100644
index 0000000000000..96451fc78beb4
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetDeviceStatus.java
@@ -0,0 +1,41 @@
+/**
+ * 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.bridge.common;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.velux.internal.things.VeluxGwState;
+
+/**
+ * Common bridge communication message scheme supported by the Velux bridge.
+ *
+ *
+ *
+ * @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/internal/bridge/common/GetFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetFirmware.java
new file mode 100644
index 0000000000000..b1e1fc6398b49
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetFirmware.java
@@ -0,0 +1,43 @@
+/**
+ * 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.bridge.common;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.velux.internal.things.VeluxGwFirmware;
+
+/**
+ * Common bridge communication message scheme supported by the Velux bridge.
+ *
+ *
+ *
+ * @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/internal/bridge/common/GetHouseStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetHouseStatus.java
new file mode 100644
index 0000000000000..1bc2355232580
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetHouseStatus.java
@@ -0,0 +1,32 @@
+/**
+ * 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.bridge.common;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Common bridge communication message scheme supported by the Velux bridge.
+ *
+ *
+ *
+ * @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/internal/bridge/common/GetProduct.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProduct.java
new file mode 100644
index 0000000000000..43fc41f0bc208
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProduct.java
@@ -0,0 +1,51 @@
+/**
+ * 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.bridge.common;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.velux.internal.things.VeluxProduct;
+
+/**
+ * Common bridge communication message scheme supported by the Velux bridge.
+ *
+ *
+ *
+ * @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/internal/bridge/common/GetProductLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProductLimitation.java
new file mode 100644
index 0000000000000..7c4d464633aa8
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProductLimitation.java
@@ -0,0 +1,51 @@
+/**
+ * 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.bridge.common;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Common bridge communication message scheme supported by the Velux bridge.
+ *
+ *
+ *
+ * @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 getLimitationMinimum true, if we query for Minimum.
+ */
+ public abstract void setActuatorIdAndLimitationType(int nodeId, boolean getLimitationMinimum);
+
+ /**
+ * 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/internal/bridge/common/GetProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProducts.java
new file mode 100644
index 0000000000000..73f5d31661f42
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProducts.java
@@ -0,0 +1,44 @@
+/**
+ * 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.bridge.common;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.velux.internal.things.VeluxProduct;
+
+/**
+ * Common bridge communication message scheme supported by the Velux bridge.
+ *
+ *
+ *
+ * @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/internal/bridge/common/GetScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetScenes.java
new file mode 100644
index 0000000000000..eb94d8d0eec16
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetScenes.java
@@ -0,0 +1,43 @@
+/**
+ * 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.bridge.common;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.velux.internal.things.VeluxScene;
+
+/**
+ * Common bridge communication message scheme supported by the Velux bridge.
+ *
+ *
+ *
+ * @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/internal/bridge/common/GetWLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetWLANConfig.java
new file mode 100644
index 0000000000000..d8d51b17e359c
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetWLANConfig.java
@@ -0,0 +1,42 @@
+/**
+ * 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.bridge.common;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.velux.internal.things.VeluxGwWLAN;
+
+/**
+ * Common bridge communication message scheme supported by the Velux bridge.
+ *
+ *
+ *
+ * @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/internal/bridge/common/Login.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Login.java
new file mode 100644
index 0000000000000..d2eefeb074d0c
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Login.java
@@ -0,0 +1,53 @@
+/**
+ * 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.bridge.common;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Specific bridge communication message supported by the Velux bridge.
+ *
+ *
+ *
+ * @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/internal/bridge/common/Logout.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Logout.java
new file mode 100644
index 0000000000000..7bcd9c68f1e95
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Logout.java
@@ -0,0 +1,31 @@
+/**
+ * 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.bridge.common;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Specific bridge communication message supported by the Velux bridge.
+ *
+ *
+ *
+ * @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/internal/bridge/common/RunProductDiscovery.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductDiscovery.java
new file mode 100644
index 0000000000000..8bd6bd571d3b7
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductDiscovery.java
@@ -0,0 +1,32 @@
+/**
+ * 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.bridge.common;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Common bridge communication message scheme supported by the Velux bridge.
+ *
+ *
+ *
+ * @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/internal/bridge/common/RunProductSearch.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductSearch.java
new file mode 100644
index 0000000000000..1b5e4777cf5f3
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductSearch.java
@@ -0,0 +1,32 @@
+/**
+ * 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.bridge.common;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Common bridge communication message scheme supported by the Velux bridge.
+ *
+ *
+ *
+ * @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/internal/bridge/common/SetHouseStatusMonitor.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetHouseStatusMonitor.java
new file mode 100644
index 0000000000000..26f7c6f8f64ad
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetHouseStatusMonitor.java
@@ -0,0 +1,43 @@
+/**
+ * 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.bridge.common;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Common bridge communication message scheme supported by the Velux bridge.
+ *
+ *
+ *
+ * @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/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..ccf6c66a67d56
--- /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-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.bridge.common;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Common bridge communication message scheme supported by the Velux bridge.
+ *
+ *
+ *
+ * @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/internal/bridge/common/SetSceneVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetSceneVelocity.java
new file mode 100644
index 0000000000000..df54d191c1d82
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetSceneVelocity.java
@@ -0,0 +1,37 @@
+/**
+ * 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.bridge.common;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Common bridge communication message scheme supported by the Velux bridge.
+ *
+ *
+ *
+ * @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/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
new file mode 100644
index 0000000000000..b17c564ad2d8c
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/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
+ */
+/**
+ * Interface definitions being used to implement the protocol-dependent interactions to IO-homecontrolled devices.
+ *
+ * @author Guenther Schreiner - Initial contribution
+ */
+package org.openhab.binding.velux.internal.bridge.common;
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
new file mode 100644
index 0000000000000..024322de4807f
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetDeviceStatus.java
@@ -0,0 +1,170 @@
+/**
+ * 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.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.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.
+ *
+ * {"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.
+ */
+ @NonNullByDefault
+ private static class Request {
+
+ @SuppressWarnings("unused")
+ private String action;
+
+ @SuppressWarnings("unused")
+ private Map
+ * {
+ * "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+ * "result":true,
+ * "deviceStatus":"discovering", or "IDLE"
+ * "data":{},
+ * "errors":[]
+ * }
+ *
+ */
+ @NonNullByDefault
+ 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
+ * {"action":"getFirmware","params":{}}
+ *
+ */
+ @NonNullByDefault
+ private static class Request {
+
+ @SuppressWarnings("unused")
+ private String action;
+
+ @SuppressWarnings("unused")
+ private Map
+ * {
+ * "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+ * "result":true,
+ * "deviceStatus":"IDLE",
+ * "data":{"version":"0.1.1.0.41.0"},
+ * "errors":[]
+ * }
+ *
+ */
+ @NonNullByDefault
+ 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
+ * {"action":"get","params":{}}
+ *
+ */
+ @NonNullByDefault
+ private static class Request {
+
+ @SuppressWarnings("unused")
+ private String action;
+
+ @SuppressWarnings("unused")
+ private Map
+ * {"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;
+ 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
+ *
+ * {
+ * "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":[]
+ * }
+ *
+ */
+ @NonNullByDefault
+ 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
+ * "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"
+ * ]
+ *
+ */
+ @NonNullByDefault
+ 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.internal.bridge.json.JsonVeluxBridge JsonVeluxBridge}
+ * for serializing.
+ *
+ * {"action":"get","params":{}}
+ *
+ */
+ @NonNullByDefault
+ private static class Request {
+
+ @SuppressWarnings("unused")
+ private String action;
+
+ @SuppressWarnings("unused")
+ private Map
+ * {
+ * "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": []
+ * }
+ *
+ */
+ @NonNullByDefault
+ 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
+ * "typeId": 2,
+ * "name": "Rolladen Schlafzimmer",
+ * "actuator": 0,
+ * "status": 0
+ *
+ */
+ @NonNullByDefault
+ 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.
+ *
+ * {
+ * "name": "V_DG_Shutter_West_100",
+ * "id": 0,
+ * "silent": true,
+ * "bCproductStates": [
+ * {
+ * "typeId": 2,
+ * "name": "Rolladen Schlafzimmer",
+ * "actuator": 0,
+ * "status": 100
+ * }
+ * ]
+ * },
+ *
+ */
+ @NonNullByDefault
+ 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.internal.bridge.json.JsonVeluxBridge JsonVeluxBridge}
+ * for serializing.
+ *
+ * {"action":"get","params":{}}
+ *
+ */
+ @NonNullByDefault
+ private static class Request {
+
+ @SuppressWarnings("unused")
+ private String action;
+ @SuppressWarnings("unused")
+ private Map
+ * {
+ * "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": []
+ * }
+ *
+ */
+ @NonNullByDefault
+ 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
+ * {"action":"wifi","params":{}}
+ *
+ */
+ @NonNullByDefault
+ private static class Request {
+
+ @SuppressWarnings("unused")
+ private String action;
+
+ @SuppressWarnings("unused")
+ private Map
+ * {"password":"Esf56mxqFY","name":"VELUX_KLF_847C"}
+ *
+ */
+ @NonNullByDefault
+ 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
+ *
+ * {
+ * "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+ * "result":true,
+ * "deviceStatus":"IDLE",
+ * "data":{"password":"Esf56mxqFY","name":"VELUX_KLF_847C"},
+ * "errors":[]
+ * }
+ *
+ */
+ @NonNullByDefault
+ 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
+ * {"action":"login","params":{"password":"PASSWORD"}}
+ *
+ */
+ @NonNullByDefault
+ 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
+ *
+ * '{"token": "PHPnfLda71xfGlxoYEOTGQ==", "result": true, "deviceStatus": "IDLE", "data": {}, "errors": [] }'
+ *
+ */
+ @NonNullByDefault
+ 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.");
+ }
+
+ /*
+ * 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
+ * {"action":"logout","params":{}}
+ *
+ */
+ @NonNullByDefault
+ private static class Request {
+
+ @SuppressWarnings("unused")
+ private String action;
+ @SuppressWarnings("unused")
+ private Map
+ * '{"token": "PHPnfLda71xfGlxoYEOTGQ==", "result": true, "deviceStatus": "IDLE", "data": {}, "errors": [] }'
+ *
+ */
+ @NonNullByDefault
+ 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
+ * {"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.
+ */
+ @NonNullByDefault
+ private static class Request {
+
+ @SuppressWarnings("unused")
+ private String action;
+
+ @SuppressWarnings("unused")
+ private Map
+ * {
+ * "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+ * "result":true,
+ * "deviceStatus":"discovering",
+ * "data":{},
+ * "errors":[]
+ * }
+ *
+ */
+ @NonNullByDefault
+ 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
+ * {"action":"identify","params":{"id":23,"time":254}}
+ *
+ */
+ @NonNullByDefault
+ 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
+ *
+ * {
+ * "token": "NkR/AA5xXj7iL6NiIW8keA==",
+ * "result": false,
+ * "deviceStatus": "IDLE",
+ * "data": {},
+ * "errors": [ 104 ]
+ * }
+ *
+ */
+ @NonNullByDefault
+ 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
+ * {"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.
+ */
+ @NonNullByDefault
+ private static class Request {
+
+ @SuppressWarnings("unused")
+ private String action;
+
+ @SuppressWarnings("unused")
+ private Map
+ * {
+ * "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+ * "result":true,
+ * "deviceStatus":"IDLE",
+ * "data":[],
+ * "errors":[]
+ * }
+ *
+ */
+ @NonNullByDefault
+ 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
+ * {"action":"run","params":{"id":9}}
+ *
+ */
+ @NonNullByDefault
+ 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
+ *
+ * {
+ * "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+ * "result":true,
+ * "deviceStatus":"IDLE",
+ * "data":{},
+ * "errors":[]
+ * }
+ *
+ */
+ @NonNullByDefault
+ 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
+ * {"action":"setSilentMode","params":{"id":9,"silent":false}}}
+ *
+ */
+ @NonNullByDefault
+ 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
+ *
+ * {
+ * "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+ * "result":true,
+ * "deviceStatus":"IDLE",
+ * "data":{},
+ * "errors":[]
+ * }
+ *
+ */
+ @NonNullByDefault
+ 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
+ *
+ *
+ *
+ *
+ *
+ * @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/internal/bridge/json/JsonVeluxBridge.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonVeluxBridge.java
new file mode 100644
index 0000000000000..146f2d8f8413d
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonVeluxBridge.java
@@ -0,0 +1,322 @@
+/**
+ * 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.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.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;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+
+/**
+ * JSON-based 2nd Level I/O interface towards the Velux bridge.
+ *
+ *
+ *
+ *
+ *
+ * @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/internal/bridge/slip/SCgetFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetFirmware.java
new file mode 100644
index 0000000000000..83ae94889c091
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetFirmware.java
@@ -0,0 +1,165 @@
+/**
+ * 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.bridge.slip;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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;
+
+/**
+ * Protocol specific bridge communication supported by the Velux bridge:
+ * Get 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/internal/bridge/slip/SCgetHouseStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetHouseStatus.java
new file mode 100644
index 0000000000000..e67458b1438b7
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetHouseStatus.java
@@ -0,0 +1,184 @@
+/**
+ * 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.bridge.slip;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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;
+
+/**
+ * Protocol specific bridge communication supported by the Velux bridge:
+ * Retrieve House Status
+ *
+ *
+ *
+ *
+ *
+ * @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/internal/bridge/slip/SCgetLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLimitation.java
new file mode 100644
index 0000000000000..84b7966d9bbaf
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLimitation.java
@@ -0,0 +1,300 @@
+/**
+ * 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.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.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;
+
+/**
+ * Protocol specific bridge communication supported by the Velux bridge:
+ * Retrieve Product Limitations
+ *
+ *
+ *
+ * @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 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;
+
+ private int limitationValue = 0;
+
+ /*
+ * ===========================================================
+ * 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[25]);
+ reqSessionID = (reqSessionID + 1) & 0xffff;
+ request.setTwoByteValue(0, reqSessionID);
+ request.setOneByteValue(2, reqIndexArrayCount);
+ request.setOneByteValue(3, reqIndexArray01);
+ request.setOneByteValue(23, reqParameterID);
+ request.setOneByteValue(24, reqLimitationType);
+ logger.trace("getRequestDataAsArrayOfBytes(): ntfSessionID={}.", reqSessionID);
+ 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;
+ }
+ 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);
+
+ if (!KLF200Response.check4matchingSessionID(logger, ntfSessionID, reqSessionID)) {
+ finished = true;
+ break;
+ }
+
+ 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;
+
+ 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 limitationValue;
+ }
+
+}
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
new file mode 100644
index 0000000000000..b810f96346ece
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProduct.java
@@ -0,0 +1,268 @@
+/**
+ * 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.bridge.slip;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Protocol specific bridge communication supported by the Velux bridge:
+ * Retrieve 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.startsWith("_")) {
+ 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/internal/bridge/slip/SCgetProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProducts.java
new file mode 100644
index 0000000000000..31cc6862c4101
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProducts.java
@@ -0,0 +1,276 @@
+/**
+ * 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.bridge.slip;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Protocol specific bridge communication supported by the Velux bridge:
+ * Retrieve 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.startsWith("_")) {
+ 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/internal/bridge/slip/SCgetScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetScenes.java
new file mode 100644
index 0000000000000..6499c5f4c50e7
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetScenes.java
@@ -0,0 +1,175 @@
+/**
+ * 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.bridge.slip;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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;
+
+/**
+ * Protocol specific bridge communication supported by the Velux bridge:
+ * Retrieve 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;
+ }
+
+ /**
+ * ===========================================================
+ *
+ *
+ *
+ * @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/internal/bridge/slip/SClogin.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogin.java
new file mode 100644
index 0000000000000..f0c87007bcb31
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogin.java
@@ -0,0 +1,166 @@
+/**
+ * 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.bridge.slip;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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;
+
+/**
+ * Protocol specific bridge communication supported by the Velux bridge:
+ * Authenticate / login
+ *
+ *
+ *
+ * @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.warn("{} bridge connection successfully established but login failed.",
+ VeluxBindingConstants.BINDING_ID);
+ 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.replaceAll(".", "*"));
+ reqPassword = thisPassword;
+ return;
+ }
+
+ @Override
+ public String getAuthToken() {
+ logger.trace("getAuthToken() called, returning {}.", reqPassword.replaceAll(".", "*"));
+ return reqPassword;
+ }
+
+}
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
new file mode 100644
index 0000000000000..2090ddcad7693
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogout.java
@@ -0,0 +1,102 @@
+/**
+ * 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.bridge.slip;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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;
+
+/**
+ * Protocol specific bridge communication supported by the Velux bridge:
+ * Retrieve LAN configuration
+ *
+ *
+ *
+ * @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/internal/bridge/slip/SCrunProductDiscovery.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductDiscovery.java
new file mode 100644
index 0000000000000..dbbb97d1bbae7
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductDiscovery.java
@@ -0,0 +1,154 @@
+/**
+ * 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.bridge.slip;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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;
+
+/**
+ * Protocol specific bridge communication supported by the Velux bridge:
+ * Ask the Bridge to detect (new) products/actuators
+ *
+ *
+ *
+ * @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.
+ *
+ *
+ *
+ * @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/internal/bridge/slip/SCrunScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunScene.java
new file mode 100644
index 0000000000000..8a221aee1960e
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunScene.java
@@ -0,0 +1,291 @@
+/**
+ * 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.bridge.slip;
+
+import java.util.Random;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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;
+
+/**
+ * Protocol specific bridge communication supported by the Velux bridge:
+ * Run Scene
+ *
+ *
+ *
+ * @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.
+ *
+ *
+ *
+ * @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/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..6f6626be6498f
--- /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-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.bridge.slip;
+
+import java.util.Random;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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:
+ * Modify Product Limitations
+ *
+ *
+ *
+ * @see SetProductLimitation
+ * @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/internal/bridge/slip/SCsetSceneVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetSceneVelocity.java
new file mode 100644
index 0000000000000..9c0964f9c722e
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetSceneVelocity.java
@@ -0,0 +1,175 @@
+/**
+ * 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.bridge.slip;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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;
+
+/**
+ * Protocol specific bridge communication supported by the Velux bridge:
+ * Modify Velocity of an 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.
+ *
+ *
+ *
+ *
+ *
+ *
+ * the following class access methods provides the protocol-specific implementation:
+ *
+ *
+ *
+ * @author Guenther Schreiner - Initial contribution.
+ */
+@NonNullByDefault
+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.
+ *
+ *
+ *
+ * @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.");
+
+ lastCommunicationInMSecs = System.currentTimeMillis();
+
+ /** 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;
+ int timeoutMsecs = bridgeInstance.veluxBridgeConfiguration().timeoutMsecs;
+
+ logger.trace("io(): connecting to {}:{}.", host, port);
+ connectivity = new SSLconnection(host, port);
+ connectivity.setTimeout(timeoutMsecs);
+ } catch (ConnectException ce) {
+ throw new ConnectException(String
+ .format("raised a non-recoverable error during connection setup: %s", ce.getMessage()));
+ } 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;
+ }
+ }
+ 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 (IOException e) {
+ logger.info("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.info("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.info("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/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..1a2db47e5c31e
--- /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-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.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.
+ * 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.
+ *
+ * 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
.
+ *
+ * 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.
+ *
+ * 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
+ *
+ *
+ * @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 DataInputStreamWithTimeout dIn;
+ private int ioTimeoutMSecs = 60000;
+
+ /**
+ * 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 (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
+ 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 DataInputStreamWithTimeout(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, ioTimeoutMSecs);
+ 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
+ DataInputStreamWithTimeout 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.");
+ }
+
+ /**
+ * 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/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
new file mode 100644
index 0000000000000..be39bb832264f
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/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
+ */
+/**
+ * SLIP-protocol I/O with the bridge.
+ *
+ * @author Guenther Schreiner - Initial contribution
+ */
+package org.openhab.binding.velux.internal.bridge.slip.io;
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
new file mode 100644
index 0000000000000..cac9f3a6114ca
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/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
+ */
+/**
+ * SLIP-protocol specific implementations of the interactions to IO-homecontrolled devices.
+ *
+ * @author Guenther Schreiner - Initial contribution
+ */
+package org.openhab.binding.velux.internal.bridge.slip;
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
new file mode 100644
index 0000000000000..d29aa9717f88f
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/KLF200Response.java
@@ -0,0 +1,164 @@
+/**
+ * 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.bridge.slip.utils;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command;
+import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber;
+import org.slf4j.Logger;
+
+/**
+ * Utility class for handling of KLF200 response packets.
+ *
+ *
+ *
+ *
+ * @author Guenther Schreiner - Initial contribution.
+ */
+@NonNullByDefault
+public class KLF200Response {
+
+ /**
+ * Provides user-oriented logging in two log levels for monitoring KLF behavior.
+ *
+ *
+ * Static methods are:
+ *
+ *
+ *
+ * @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.internal.bridge.slip.utils.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.internal.bridge.slip.utils.Packet Packet}..
+ *
+ * @return packetLength
+ * of type int.
+ */
+ public int length() {
+ return data.length;
+ }
+
+ /**
+ * 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.
+ */
+ public byte[] toByteArray() {
+ return data;
+ }
+
+ /**
+ * 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.
+ * @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.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) {
+ 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.internal.bridge.slip.utils.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.
+ *
+ *
+ *
+ * @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/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
new file mode 100644
index 0000000000000..accc7eccad750
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/SlipRFC1055.java
@@ -0,0 +1,126 @@
+/**
+ * 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.bridge.slip.utils;
+
+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.
+ *
+ *
+ *
+ * @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));
+ 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));
+ return payload;
+ }
+
+}
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
new file mode 100644
index 0000000000000..a4503a79b83e1
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/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
+ */
+/**
+ * Utility classes for the SLIP-protocol.
+ *
+ * @author Guenther Schreiner - Initial contribution
+ */
+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
new file mode 100644
index 0000000000000..dbcef9ef5cfd2
--- /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-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.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.internal.bridge.VeluxBridgeProvider}
+ * device.
+ *
+ *
+ *
+ *
+ *
+ * @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.
+ *
+ *
+ */
+ String sceneName = VeluxBindingConstants.UNKNOWN;
+
+ /**
+ * {@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).
+ *
+ *
+ */
+ 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.
+ *
+ *
+ *
+ *
+ */
+ String sceneLevels = VeluxBindingConstants.UNKNOWN;
+ /**
+ * {@link #currentLevel} of type {@link int}, which represents the current shutter level.
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * @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 (!ThingConfiguration.exists(thisBridgeHandler, channelUID,
+ VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER)) {
+ LOGGER.trace("handleRefresh(): aborting processing as {} is not set.",
+ VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER);
+ break;
+ }
+ String actuatorSerial = (String) ThingConfiguration.getValue(thisBridgeHandler, channelUID,
+ VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER);
+ LOGGER.trace("handleRefresh(): actuatorSerial={}", actuatorSerial);
+
+ // Handle value inversion
+ boolean propertyInverted = false;
+ if (ThingConfiguration.exists(thisBridgeHandler, channelUID,
+ VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) {
+ propertyInverted = (boolean) ThingConfiguration.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 (!ThingConfiguration.exists(thisBridgeHandler, channelUID,
+ VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER)) {
+ LOGGER.trace("handleCommand(): aborting processing as {} is not set.",
+ VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER);
+ break;
+ }
+ String actuatorSerial = (String) ThingConfiguration.getValue(thisBridgeHandler, channelUID,
+ VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER);
+ LOGGER.trace("handleCommand(): actuatorSerial={}", actuatorSerial);
+
+ // Handle value inversion
+ boolean propertyInverted = false;
+ if (ThingConfiguration.exists(thisBridgeHandler, channelUID,
+ VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) {
+ propertyInverted = (boolean) ThingConfiguration.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.");
+ 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(), posCommand);
+ } else {
+ new VeluxBridgeSetLimitation().setMaximumLimitation(thisBridgeHandler.thisBridge,
+ thisProduct.getBridgeProductIndex().toInt(), posCommand);
+ }
+ } 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/internal/handler/ChannelActuatorPosition.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorPosition.java
new file mode 100644
index 0000000000000..6b3134ca16dc6
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorPosition.java
@@ -0,0 +1,179 @@
+/**
+ * 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 static org.openhab.binding.velux.internal.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.internal.bridge.VeluxBridgeRunProductCommand;
+import org.openhab.binding.velux.internal.bridge.common.GetProduct;
+import org.openhab.binding.velux.internal.handler.utils.Thing2VeluxActuator;
+import org.openhab.binding.velux.internal.things.VeluxProductPosition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Channel-specific retrieval and modification.
+ *
+ *
+ *
+ *
+ *
+ * @author Guenther Schreiner - Initial contribution.
+ */
+@NonNullByDefault
+final class ChannelActuatorPosition extends ChannelHandlerTemplate {
+ private static final Logger LOGGER = LoggerFactory.getLogger(ChannelActuatorPosition.class);
+
+ // Constructors
+
+ /**
+ * Suppress default constructor for non-instantiability.
+ */
+ private ChannelActuatorPosition() {
+ throw new AssertionError();
+ }
+
+ // Public methods
+
+ /**
+ * 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.");
+ }
+ 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(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
+ 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.");
+ }
+ 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 = 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 = veluxActuator.isInverted() ? new VeluxProductPosition(PercentType.ZERO)
+ : new VeluxProductPosition(PercentType.HUNDRED);
+ } else if ((command instanceof 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 (veluxActuator.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) && (command == OnOffType.ON)) {
+ LOGGER.trace("handleCommand(): found ON command.");
+ 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 = veluxActuator.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,
+ 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)) {
+ 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/handler/ChannelBridgeCheck.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeCheck.java
new file mode 100644
index 0000000000000..b25dbb02947c3
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeCheck.java
@@ -0,0 +1,115 @@
+/**
+ * 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 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.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;
+
+/**
+ * Channel-specific retrieval and modification.
+ *
+ *
+ *
+ * @author Guenther Schreiner - Initial contribution.
+ */
+@NonNullByDefault
+final class ChannelBridgeCheck extends ChannelHandlerTemplate {
+ 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
+ *
+ *
+ *
+ *
+ * @author Guenther Schreiner - Initial contribution.
+ */
+@NonNullByDefault
+final class ChannelBridgeDoDetection extends ChannelHandlerTemplate {
+ 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 == 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/internal/handler/ChannelBridgeFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeFirmware.java
new file mode 100644
index 0000000000000..40a89c8460003
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeFirmware.java
@@ -0,0 +1,72 @@
+/**
+ * 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;
+import org.openhab.binding.velux.internal.bridge.VeluxBridgeGetFirmware;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Channel-specific retrieval and modification.
+ *
+ *
+ *
+ * @author Guenther Schreiner - Initial contribution.
+ */
+@NonNullByDefault
+final class ChannelBridgeFirmware extends ChannelHandlerTemplate {
+ 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/internal/handler/ChannelBridgeLANconfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeLANconfig.java
new file mode 100644
index 0000000000000..b50fdeede3b5f
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeLANconfig.java
@@ -0,0 +1,100 @@
+/**
+ * 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;
+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;
+
+/**
+ * Channel-specific retrieval and modification.
+ *
+ *
+ *
+ * @author Guenther Schreiner - Initial contribution.
+ */
+@NonNullByDefault
+final class ChannelBridgeLANconfig extends ChannelHandlerTemplate {
+ 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.PROPERTY_BRIDGE_IPADDRESS,
+ thisBridgeHandler.bridgeParameters.lanConfig.openHABipAddress.toString());
+ break;
+ case BRIDGE_SUBNETMASK:
+ newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABsubnetMask);
+ 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.PROPERTY_BRIDGE_DEFAULTGW,
+ thisBridgeHandler.bridgeParameters.lanConfig.openHABdefaultGW.toString());
+ break;
+ case BRIDGE_DHCP:
+ newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABenabledDHCP);
+ ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.PROPERTY_BRIDGE_DHCP,
+ thisBridgeHandler.bridgeParameters.lanConfig.openHABenabledDHCP.toString());
+ default:
+ }
+ }
+ LOGGER.trace("handleRefresh() returns {}.", newState);
+ return newState;
+ }
+
+}
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
new file mode 100644
index 0000000000000..4abe789afa620
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeProducts.java
@@ -0,0 +1,74 @@
+/**
+ * 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;
+import org.openhab.binding.velux.internal.handler.utils.StateUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Channel-specific retrieval and modification.
+ *
+ *
+ *
+ * @author Guenther Schreiner - Initial contribution.
+ */
+@NonNullByDefault
+final class ChannelBridgeProducts extends ChannelHandlerTemplate {
+ 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/internal/handler/ChannelBridgeScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeScenes.java
new file mode 100644
index 0000000000000..126306b891aff
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeScenes.java
@@ -0,0 +1,74 @@
+/**
+ * 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;
+import org.openhab.binding.velux.internal.handler.utils.StateUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Channel-specific retrieval and modification.
+ *
+ *
+ *
+ * @author Guenther Schreiner - Initial contribution.
+ */
+@NonNullByDefault
+final class ChannelBridgeScenes extends ChannelHandlerTemplate {
+ 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/internal/handler/ChannelBridgeStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeStatus.java
new file mode 100644
index 0000000000000..22f59948e5f53
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeStatus.java
@@ -0,0 +1,72 @@
+/**
+ * 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;
+import org.openhab.binding.velux.internal.bridge.VeluxBridgeDeviceStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Channel-specific retrieval and modification.
+ *
+ *
+ *
+ * @author Guenther Schreiner - Initial contribution.
+ */
+@NonNullByDefault
+final class ChannelBridgeStatus extends ChannelHandlerTemplate {
+ 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/internal/handler/ChannelBridgeWLANconfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeWLANconfig.java
new file mode 100644
index 0000000000000..5d542e00c9b3f
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeWLANconfig.java
@@ -0,0 +1,90 @@
+/**
+ * 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;
+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;
+
+/**
+ * Channel-specific retrieval and modification.
+ *
+ *
+ *
+ * @author Guenther Schreiner - Initial contribution.
+ */
+@NonNullByDefault
+final class ChannelBridgeWLANconfig extends ChannelHandlerTemplate {
+ 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.PROPERTY_BRIDGE_WLANSSID,
+ thisBridgeHandler.bridgeParameters.wlanConfig.openHABwlanSSID.toString());
+ break;
+ case BRIDGE_WLANPASSWORD:
+ newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABsubnetMask);
+ ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.PROPERTY_BRIDGE_WLANPASSWORD,
+ thisBridgeHandler.bridgeParameters.wlanConfig.openHABwlanPassword.toString());
+ break;
+ default:
+ }
+ }
+ LOGGER.trace("handleRefresh() returns {}.", newState);
+ return newState;
+ }
+
+}
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
new file mode 100644
index 0000000000000..6f23e63462f58
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelHandlerTemplate.java
@@ -0,0 +1,72 @@
+/**
+ * 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.Command;
+import org.eclipse.smarthome.core.types.State;
+
+/**
+ * Channel-specific retrieval and modification.
+ *
+ *
+ *
+ *
+ *
+ * @author Guenther Schreiner - Initial contribution.
+ */
+@NonNullByDefault
+abstract class ChannelHandlerTemplate {
+
+ /**
+ * 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 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");
+ }
+
+ /**
+ * 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 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,
+ 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/ChannelSceneAction.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneAction.java
new file mode 100644
index 0000000000000..07bc016d3dafd
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneAction.java
@@ -0,0 +1,122 @@
+/**
+ * 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.library.types.OnOffType;
+import org.eclipse.smarthome.core.thing.ChannelUID;
+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;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Channel-specific retrieval and modification.
+ *
+ *
+ *
+ *
+ *
+ * @author Guenther Schreiner - Initial contribution.
+ */
+@NonNullByDefault
+final class ChannelSceneAction extends ChannelHandlerTemplate {
+ 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 (!ThingConfiguration.exists(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_SCENE_NAME)) {
+ LOGGER.trace("handleCommand(): aborting processing as scene name is not set.");
+ break;
+ }
+ String sceneName = (String) ThingConfiguration.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) ThingConfiguration.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/internal/handler/ChannelSceneSilentmode.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneSilentmode.java
new file mode 100644
index 0000000000000..89f3d204ba91a
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneSilentmode.java
@@ -0,0 +1,102 @@
+/**
+ * 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.library.types.OnOffType;
+import org.eclipse.smarthome.core.thing.ChannelUID;
+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;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Channel-specific retrieval and modification.
+ *
+ *
+ *
+ *
+ *
+ * @author Guenther Schreiner - Initial contribution.
+ */
+@NonNullByDefault
+final class ChannelSceneSilentmode extends ChannelHandlerTemplate {
+ 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 (!ThingConfiguration.exists(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_SCENE_NAME)) {
+ LOGGER.trace("handleCommand(): aborting processing as scene name is not set.");
+ break;
+ }
+ String sceneName = (String) ThingConfiguration.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/internal/handler/ChannelVShutterPosition.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelVShutterPosition.java
new file mode 100644
index 0000000000000..35cdfddad01da
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelVShutterPosition.java
@@ -0,0 +1,168 @@
+/**
+ * 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 static org.openhab.binding.velux.internal.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.internal.VeluxBindingProperties;
+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;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Channel-specific retrieval and modification.
+ *
+ *
+ *
+ *
+ *
+ * @author Guenther Schreiner - Initial contribution.
+ */
+@NonNullByDefault
+final class ChannelVShutterPosition extends ChannelHandlerTemplate {
+ 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 (!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) ThingConfiguration.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 (!ThingConfiguration.exists(thisBridgeHandler, channelUID,
+ VeluxBindingProperties.PROPERTY_VSHUTTER_SCENELEVELS)) {
+ LOGGER.trace("handleCommand(): aborting processing as scene levels are not set.");
+ return newValue;
+ }
+ String sceneLevels = (String) ThingConfiguration.getValue(thisBridgeHandler, channelUID,
+ VeluxBindingProperties.PROPERTY_VSHUTTER_SCENELEVELS);
+ // ThingProperty currentLevel
+ 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) ThingConfiguration.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().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.
+ *
+ *
+ *
+ * @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/VeluxBindingHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBindingHandler.java
new file mode 100644
index 0000000000000..fae5a31ea9c36
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBindingHandler.java
@@ -0,0 +1,241 @@
+/**
+ * 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.smarthome.core.thing.Channel;
+import org.eclipse.smarthome.core.thing.ChannelUID;
+import org.eclipse.smarthome.core.thing.Thing;
+import org.eclipse.smarthome.core.thing.ThingStatus;
+import org.eclipse.smarthome.core.thing.ThingStatusDetail;
+import org.eclipse.smarthome.core.thing.ThingTypeUID;
+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;
+import org.slf4j.LoggerFactory;
+
+/***
+ * The class is responsible for representing the overall status of the Velux binding.
+ *
+ *
+ *
+ * @author Guenther Schreiner - Initial contribution
+ */
+@NonNullByDefault
+public class VeluxBindingHandler extends ExtendedBaseThingHandler {
+ private final Logger logger = LoggerFactory.getLogger(VeluxBindingHandler.class);
+
+ /*
+ * ***************************
+ * ***** Private Objects *****
+ */
+ private Thing thing;
+ private Localization localization;
+ private Integer currentNumberOfBridges = 0;
+ private Integer currentNumberOfThings = 0;
+
+ /*
+ * ************************
+ * ***** Constructors *****
+ */
+
+ public VeluxBindingHandler(Thing thing, final Localization localization) {
+ super(thing);
+ this.thing = thing;
+ this.localization = localization;
+ logger.trace("VeluxBindingHandler(constructor) called.");
+ }
+
+ /*
+ * ***************************
+ * ***** Private Methods *****
+ */
+
+ /**
+ * Provide the ThingType for a given Channel.
+ *
+ *
+ */
+ 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.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."
+ 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 *****
+ */
+
+ @Override
+ public void initialize() {
+ 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);
+ // 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(() -> {
+ logger.trace("initialize.scheduled(): Setting ThingStatus to ONLINE.");
+ updateStatus(ThingStatus.ONLINE);
+ updateVisibleInformation();
+ logger.trace("initialize.scheduled(): done.");
+ });
+ logger.trace("initialize() done.");
+ }
+
+ @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
+ */
+ switch (channelId) {
+ default:
+ logger.warn("handleCommand() cannot handle command {} on channel {} (type {}).", command, itemName,
+ itemType);
+ }
+ }
+ logger.trace("handleCommand() done.");
+ }
+
+ /*
+ * **********************************
+ * ***** (Other) Public Methods *****
+ */
+
+ /**
+ * Update the information about bridges and things.
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+ @NonNullByDefault
+ public class BridgeParameters {
+ /** Information retrieved by {@link VeluxBridgeActuators#getProducts} */
+ public VeluxBridgeActuators actuators = new VeluxBridgeActuators();
+
+ /** Information retrieved by {@link org.openhab.binding.velux.internal.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.
+ *
+ *
+ *
+ * @author Guenther Schreiner - Initial contribution
+ */
+@NonNullByDefault
+public 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}.
+ *
+ *
+ *
+ * @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.
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * @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
+ *
+ *
+ * @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
+ *
+ * In addition, the subtype are accessible via:
+ *
+ *
+ *
+ * @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
+ *
+ * Constants available:
+ *
+ *
+ *
+ *
+ */
+ @NonNullByDefault
+ 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}.
+ *
+ *
+ */
+ @NonNullByDefault
+ 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.
+ *
+ *
+ */
+ 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
+ *
+ *
+ * @see VeluxKLFAPI
+ *
+ * @author Guenther Schreiner - initial contribution.
+ */
+@NonNullByDefault
+public class VeluxProductPosition {
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ // Public definition
+
+ public static final VeluxProductPosition UNKNOWN = new VeluxProductPosition();
+ 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;
+
+ 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 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.
+ *
+ * @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) {
+ 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);
+ }
+ return (int) result;
+ }
+
+}
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
new file mode 100644
index 0000000000000..ea30b7774261b
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductReference.java
@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.velux.internal.things;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.velux.internal.VeluxBindingConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Velux product characteristics: Product Reference.
+ *
+ *
+ *
+ *
+ * @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.
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * @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) {
+ 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/internal/things/VeluxProductState.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductState.java
new file mode 100644
index 0000000000000..9c42ab8da774e
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductState.java
@@ -0,0 +1,91 @@
+/**
+ * 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.things;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Velux product characteristics: Product State.
+ *
+ *
+ *
+ *
+ * @author Guenther Schreiner - initial contribution.
+ */
+@NonNullByDefault
+public class VeluxProductState {
+
+ // Type definitions
+
+ @NonNullByDefault
+ 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/internal/things/VeluxProductType.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductType.java
new file mode 100644
index 0000000000000..de056660b6370
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductType.java
@@ -0,0 +1,127 @@
+/**
+ * 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.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.internal.VeluxBindingConstants;
+
+/**
+ * Velux product characteristics: Product Type.
+ *
+ *
+ *
+ * @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
+ *
+ *
+ * @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
+ *
+ *
+ * @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
+
+ @NonNullByDefault
+ 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());
+ }
+ }
+
+ @NonNullByDefault
+ public static class SceneBridgeIndex {
+
+ 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/internal/things/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/package-info.java
new file mode 100644
index 0000000000000..06702fbaac96a
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/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 for modeling the Velux devices and their properties in a general openHAB (OH1/OH2/OH3)-independent way.
+ *
+ * @author Guenther Schreiner - Initial contribution
+ */
+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
new file mode 100644
index 0000000000000..76cc54d637850
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/Localization.java
@@ -0,0 +1,117 @@
+/**
+ * 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.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 utility class for dealing with localization.
+ *
+ * It provides the following methods:
+ *
+ *
+ *
+ *
+ *