added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.netatmo-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-velux" description="Velux Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.velux/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,123 @@
/**
* 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;
import static org.apache.commons.lang.StringUtils.isNotBlank;
import java.lang.reflect.Field;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/***
* <B>Class for Velux binding which validates the bridge configuration parameters.</B>
*
* <ul>
* <li>{@link #VeluxBinding constructor}</li>
* <li>{@link #checked }</li>
* </ul>
*
* @author Guenther Schreiner - Initial contribution
* @author Joachim Sauer (@Saua) - fix for isBulkRetrievalEnabled, isSequentialEnforced
*/
@NonNullByDefault
public class VeluxBinding extends VeluxBridgeConfiguration {
private final Logger logger = LoggerFactory.getLogger(getClass());
/***
*** Startup methods
***/
/**
* Constructor
*
* initializes the interface towards the Velux bridge. Furthermore, the checked configuration can be retrieved by
* the method {@link #checked checked}.
*
* @param uncheckedConfiguration
* The configuration of type {@link VeluxBridgeConfiguration}
* which shall be checked.
*/
public VeluxBinding(@Nullable VeluxBridgeConfiguration uncheckedConfiguration) {
logger.trace("VeluxBinding(constructor) called.");
if (logger.isTraceEnabled()) {
for (Field field : VeluxBridgeConfiguration.class.getFields()) {
if (!StringUtils.capitalize(field.getName()).contentEquals(field.getName())) {
logger.trace("VeluxBinding(): FYI: a potential configuration string is '{}'.", field.getName());
}
}
}
if (uncheckedConfiguration == null) {
logger.debug("No configuration found, using default values.");
} else {
logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_PROTOCOL);
if (isNotBlank(uncheckedConfiguration.protocol)) {
this.protocol = uncheckedConfiguration.protocol;
}
logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_IPADDRESS);
if (isNotBlank(uncheckedConfiguration.ipAddress)) {
this.ipAddress = uncheckedConfiguration.ipAddress;
}
logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_TCPPORT);
if ((uncheckedConfiguration.tcpPort > 0) && (uncheckedConfiguration.tcpPort <= 65535)) {
this.tcpPort = uncheckedConfiguration.tcpPort;
}
logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_PASSWORD);
if (isNotBlank(uncheckedConfiguration.password)) {
this.password = uncheckedConfiguration.password;
}
logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_TIMEOUT_MSECS);
if ((uncheckedConfiguration.timeoutMsecs > 0) && (uncheckedConfiguration.timeoutMsecs <= 10000)) {
this.timeoutMsecs = uncheckedConfiguration.timeoutMsecs;
}
logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_RETRIES);
if ((uncheckedConfiguration.retries >= 0) && (uncheckedConfiguration.retries <= 10)) {
this.retries = uncheckedConfiguration.retries;
}
logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_REFRESH_MSECS);
if ((uncheckedConfiguration.refreshMSecs > 0) && (uncheckedConfiguration.refreshMSecs <= 10000)) {
this.refreshMSecs = uncheckedConfiguration.refreshMSecs;
}
this.isBulkRetrievalEnabled = uncheckedConfiguration.isBulkRetrievalEnabled;
this.isSequentialEnforced = uncheckedConfiguration.isSequentialEnforced;
this.isProtocolTraceEnabled = uncheckedConfiguration.isProtocolTraceEnabled;
}
logger.trace("VeluxBinding(constructor) done.");
}
/**
* Access method returning a validated configuration.
*
* @return bridgeConfiguration of type {@link VeluxBridgeConfiguration
* VeluxBridgeConfiguration}.
*/
public VeluxBridgeConfiguration checked() {
logger.trace("checked() called.");
logger.debug("{}Config[{}={},{}={},{}={},{}={},{}={},{}={},{}={},{}={},{}={},{}={}]",
VeluxBindingConstants.BINDING_ID, VeluxBridgeConfiguration.BRIDGE_PROTOCOL, protocol,
VeluxBridgeConfiguration.BRIDGE_IPADDRESS, this.ipAddress, VeluxBridgeConfiguration.BRIDGE_TCPPORT,
tcpPort, VeluxBridgeConfiguration.BRIDGE_PASSWORD, password.replaceAll(".", "*"),
VeluxBridgeConfiguration.BRIDGE_TIMEOUT_MSECS, timeoutMsecs, VeluxBridgeConfiguration.BRIDGE_RETRIES,
retries, VeluxBridgeConfiguration.BRIDGE_REFRESH_MSECS, refreshMSecs,
VeluxBridgeConfiguration.BRIDGE_IS_BULK_RETRIEVAL_ENABLED, isBulkRetrievalEnabled,
VeluxBridgeConfiguration.BRIDGE_IS_SEQUENTIAL_ENFORCED, isSequentialEnforced,
VeluxBridgeConfiguration.BRIDGE_PROTOCOL_TRACE_ENABLED, isProtocolTraceEnabled);
logger.trace("checked() done.");
return this;
}
}

View File

@@ -0,0 +1,89 @@
/**
* 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;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This represents the configuration of a openHAB item that is binded to a Velux
* KLF200 Gateway. It contains the following information:
*
* <ul>
* <li><B>bindingItemType</B>
* <P>
* Accessible via
* {@link org.openhab.binding.velux.internal.VeluxBindingConfig#getBindingItemType
* getBindingItemType} as representation of the Velux device is filed in the Velux bridge.</li>
* <li><B>bindingConfig</B>
* <P>
* 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).</li>
* </ul>
*
* @author Guenther Schreiner - Initial contribution
*/
@NonNullByDefault
class VeluxBindingConfig {
private final Logger logger = LoggerFactory.getLogger(VeluxBindingConfig.class);
/**
* The binding type of the velux item described by type {@link org.openhab.binding.velux.internal.VeluxItemType
* VeluxItemType}.
*/
private VeluxItemType bindingItemType;
/**
* Device-specific binding configuration as declared in the binding configuration (possibly adapted by
* preprocessing).
*/
private String bindingConfig;
/**
* Constructor of the VeluxBindingConfig.
*
* @param bindingItemType
* The Velux item type {@link org.openhab.binding.velux.internal.VeluxItemType
* VeluxItemType}
* which the Velux device is filed in the Velux bridge.
* @param bindingConfig
* The optional configuration type of the Velux binding.
*/
public VeluxBindingConfig(VeluxItemType bindingItemType, String bindingConfig) {
logger.trace("VeluxBindingConfig(constructor:{},{}) called.", bindingItemType, bindingConfig);
this.bindingItemType = bindingItemType;
this.bindingConfig = bindingConfig;
}
/**
* @return <b>bindingTypeItem</b> of type {@link org.openhab.binding.velux.internal.VeluxItemType
* VeluxItemType}.
*/
public VeluxItemType getBindingItemType() {
return this.bindingItemType;
}
/**
* @return <b>bindingConfig</b> of type {@link String} that
* has been declared in the binding configuration,
* possibly adapted by preprocessing.
*/
public String getBindingConfig() {
return this.bindingConfig;
}
}

View File

@@ -0,0 +1,148 @@
/**
* 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;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link VeluxBindingConstants} class defines common constants, which are
* used across the whole binding.
* <P>
* 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}.
* </P>
* This class contains the Thing identifications:
* <UL>
* <LI>{@link #THING_VELUX_BRIDGE} for the bridge itself,</LI>
* <LI>{@link #THING_VELUX_SCENE} for the scenes summarizing a set of actuator states,</LI>
* <LI>{@link #THING_VELUX_ACTUATOR} for the general actuators identified on the bridge,</LI>
* <LI>{@link #THING_VELUX_ROLLERSHUTTER} for the rollershutters identified on the bridge,</LI>
* <LI>{@link #THING_VELUX_WINDOW} for the windows identified on the bridge.</LI>
* <LI>{@link #THING_VELUX_VSHUTTER} for the virtual shutters defined in the configuration</LI>
* </UL>
*
* @author Guenther Schreiner - Initial contribution
*/
@NonNullByDefault
public class VeluxBindingConstants {
/** Basic binding identification. */
public static final String BINDING_ID = "velux";
// Id of support bridge
/**
* The Thing identification of the binding.
*/
private static final String THING_VELUX_BINDING = "binding";
/**
* The Thing identification of the <B>Velux</B> bridge.
*/
private static final String THING_VELUX_BRIDGE = "klf200";
/**
* The Thing identification of a scene defined on the <B>Velux</B> bridge.
*/
private static final String THING_VELUX_SCENE = "scene";
/**
* The Thing identification of a generic actuator defined on the <B>Velux</B> bridge.
*/
private static final String THING_VELUX_ACTUATOR = "actuator";
/**
* The Thing identification of a rollershutter defined on the <B>Velux</B> bridge.
*/
private static final String THING_VELUX_ROLLERSHUTTER = "rollershutter";
/**
* The Thing identification of a window defined on the <B>Velux</B> bridge.
*/
private static final String THING_VELUX_WINDOW = "window";
/**
* The Thing identification of a virtual shutter defined on the <B>Velux</B> bridge.
*/
private static final String THING_VELUX_VSHUTTER = "vshutter";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_BINDING = new ThingTypeUID(BINDING_ID, THING_VELUX_BINDING);
// List of all Bridge Type UIDs
public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, THING_VELUX_BRIDGE);
// List of all Thing Type UIDs beyond the bridge(s)
public static final ThingTypeUID THING_TYPE_VELUX_SCENE = new ThingTypeUID(BINDING_ID, THING_VELUX_SCENE);
public static final ThingTypeUID THING_TYPE_VELUX_ACTUATOR = new ThingTypeUID(BINDING_ID, THING_VELUX_ACTUATOR);
public static final ThingTypeUID THING_TYPE_VELUX_ROLLERSHUTTER = new ThingTypeUID(BINDING_ID,
THING_VELUX_ROLLERSHUTTER);
public static final ThingTypeUID THING_TYPE_VELUX_WINDOW = new ThingTypeUID(BINDING_ID, THING_VELUX_WINDOW);
public static final ThingTypeUID THING_TYPE_VELUX_VSHUTTER = new ThingTypeUID(BINDING_ID, THING_VELUX_VSHUTTER);
// Definitions of different set of Things
public static final Set<ThingTypeUID> SUPPORTED_THINGS_BINDING = new HashSet<>(Arrays.asList(THING_TYPE_BINDING));
public static final Set<ThingTypeUID> SUPPORTED_THINGS_BRIDGE = new HashSet<>(Arrays.asList(THING_TYPE_BRIDGE));
public static final Set<ThingTypeUID> SUPPORTED_THINGS_ITEMS = new HashSet<>(
Arrays.asList(THING_TYPE_VELUX_SCENE, THING_TYPE_VELUX_ACTUATOR, THING_TYPE_VELUX_ROLLERSHUTTER,
THING_TYPE_VELUX_WINDOW, THING_TYPE_VELUX_VSHUTTER));
// *** List of all Channel ids ***
// List of all binding channel ids
/** Channel identifier describing the current Binding State. */
public static final String CHANNEL_BINDING_INFORMATION = "information";
// List of all bridge channel ids
/** Channel/Property identifier describing the current Bridge State. */
public static final String CHANNEL_BRIDGE_STATUS = "status";
public static final String CHANNEL_BRIDGE_RELOAD = "reload";
public static final String CHANNEL_BRIDGE_DOWNTIME = "downtime";
public static final String CHANNEL_BRIDGE_DO_DETECTION = "doDetection";
public static final String PROPERTY_BRIDGE_TIMESTAMP_SUCCESS = "connectionSuccess";
public static final String PROPERTY_BRIDGE_TIMESTAMP_ATTEMPT = "connectionAttempt";
public static final String PROPERTY_BRIDGE_FIRMWARE = "firmware";
public static final String PROPERTY_BRIDGE_IPADDRESS = "ipAddress";
public static final String PROPERTY_BRIDGE_SUBNETMASK = "subnetMask";
public static final String PROPERTY_BRIDGE_DEFAULTGW = "defaultGW";
public static final String PROPERTY_BRIDGE_DHCP = "DHCP";
public static final String PROPERTY_BRIDGE_WLANSSID = "WLANSSID";
public static final String PROPERTY_BRIDGE_WLANPASSWORD = "WLANPassword";
public static final String PROPERTY_BRIDGE_CHECK = "check";
public static final String PROPERTY_BRIDGE_PRODUCTS = "products";
public static final String PROPERTY_BRIDGE_SCENES = "scenes";
// List of all scene channel ids
public static final String CHANNEL_SCENE_ACTION = "action";
public static final String CHANNEL_SCENE_SILENTMODE = "silentMode";
// List of all actuator channel ids
public static final String CHANNEL_ACTUATOR_POSITION = "position";
public static final String CHANNEL_ACTUATOR_STATE = "state";
public static final String CHANNEL_ACTUATOR_SILENTMODE = "silentMode";
public static final String CHANNEL_ACTUATOR_LIMIT_MINIMUM = "limitMinimum";
public static final String CHANNEL_ACTUATOR_LIMIT_MAXIMUM = "limitMaximum";
// List of all virtual shutter channel ids
public static final String CHANNEL_VSHUTTER_POSITION = "vposition";
// Helper definitions
public static final String BINDING_VALUES_SEPARATOR = ",";
public static final String OUTPUT_VALUE_SEPARATOR = ",";
public static final String UNKNOWN = "unknown";
// Critical issues to be reported will use the following message
public static final String LOGGING_CONTACT = "Please report to maintainer: ";
}

View File

@@ -0,0 +1,60 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.velux.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link VeluxBindingProperties} class defines common constants, which are
* used within the property definitions.
*
* This class contains the property identifications:
* <UL>
* <LI>{@link #PROPERTY_BINDING_BUNDLEVERSION} for identification of the binding,</LI>
* <LI>{@link #PROPERTY_BINDING_NOOFBRIDGES} for number of bridges,</LI>
* <LI>{@link #PROPERTY_BINDING_NOOFTHINGS} for number of things,</LI>
* </UL>
* <UL>
* <LI>{@link #PROPERTY_SCENE_NAME} for defining the name of a scene,</LI>
* <LI>{@link #PROPERTY_SCENE_VELOCITY} for defining the velocity of a scene,</LI>
* </UL>
* <UL>
* <LI>{@link #CONFIG_ACTUATOR_SERIALNUMBER} for defining the serial number of an actuator, a rollershutter and a
* window,</LI>
* <LI>{@link #PROPERTY_ACTUATOR_NAME} for defining the name of an actuator, a rollershutter and a window,</LI>
* <LI>{@link #PROPERTY_ACTUATOR_INVERTED} for modifying the value of a Channel,</LI>
* </UL>
* <UL>
* <LI>{@link #PROPERTY_VSHUTTER_SCENELEVELS} for defining a virtual shutter.</LI>
* <LI>{@link #PROPERTY_VSHUTTER_CURRENTLEVEL} for defining a virtual shutter.</LI>
* </UL>
*
* @author Guenther Schreiner - Initial contribution
*/
@NonNullByDefault
public class VeluxBindingProperties {
public static final String PROPERTY_BINDING_BUNDLEVERSION = "bundleVersion";
public static final String PROPERTY_BINDING_NOOFBRIDGES = "numberOfBridges";
public static final String PROPERTY_BINDING_NOOFTHINGS = "numberOfThings";
public static final String PROPERTY_SCENE_NAME = "sceneName";
public static final String PROPERTY_SCENE_VELOCITY = "velocity";
public static final String CONFIG_ACTUATOR_SERIALNUMBER = "serial";
public static final String PROPERTY_ACTUATOR_NAME = "name";
public static final String PROPERTY_ACTUATOR_INVERTED = "inverted";
public static final String PROPERTY_VSHUTTER_SCENELEVELS = "sceneLevels";
public static final String PROPERTY_VSHUTTER_CURRENTLEVEL = "currentLevel";
}

View File

@@ -0,0 +1,498 @@
/**
* 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;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.items.GenericItem;
import org.openhab.core.library.items.NumberItem;
import org.openhab.core.library.items.RollershutterItem;
import org.openhab.core.library.items.StringItem;
import org.openhab.core.library.items.SwitchItem;
import org.openhab.core.thing.ThingTypeUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Enumeration of Types of a Velux item.
* <br>
* Provides information about:
* <ul>
* <li>associated thing identified by String</li>
* <li>defined channel identified by String</li>
* <li>{@link #getItemClass} item class,</li>
* <li>{@link #isReadable} about a read possibility,</li>
* <li>{@link #isWritable} about a write possibility,</li>
* <li>{@link #isExecutable} about an execute possibility,</li>
* <li>{@link #isToBeRefreshed} about necessarily to be refreshed,</li>
* <li>{@link #isToBeRefreshedNow} about necessarily to be refreshed at this time,</li>
* <li>{@link #isChannel} as indication of being handled as Channel of a thing,</li>
* <li>{@link #isProperty} as indication of being handled as property of a thing.</li>
* </ul>
*
* In addition there are helper methods providing information about:
*
* <ul>
* <li>{@link #getIdentifier} returning the common identifier string,</li>
* <li>{@link #getByThingAndChannel} to retrieve an enum instance selected by Thing
* and Channel identifier,</li>
* <li>{@link #getPropertyEntriesByThing} to retrieve any Thing identifiers as array of
* String,</li>
* </ul>
* <p>
* 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:
* <ul>
* <li>
* Unique identification by:
* <ul>
* <li>Thing name as string,</li>
* <li>Channel name as string,</li>
* </ul>
* </li>
* <li>Channel type as OpenHAB type,</li>
* <li>ability flag whether this item is to be read,</li>
* <li>ability flag whether this item is able to be modified,</li>
* <li>ability flag whether this item is to be used as execution trigger.</li>
* </ul>
*
* @author Guenther Schreiner - Initial contribution
*
*/
@NonNullByDefault
public enum VeluxItemType {
// @formatter:off
UNKNOWN(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.UNKNOWN, TypeFlavor.UNUSABLE),
//
BINDING_INFORMATION(VeluxBindingConstants.THING_TYPE_BINDING, VeluxBindingConstants.CHANNEL_BINDING_INFORMATION, TypeFlavor.READONLY_VOLATILE_STRING),
//
BRIDGE_STATUS(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_STATUS, TypeFlavor.READONLY_VOLATILE_STRING),
BRIDGE_DOWNTIME(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_DOWNTIME, TypeFlavor.READONLY_VOLATILE_NUMBER),
BRIDGE_RELOAD(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_RELOAD, TypeFlavor.INITIATOR),
BRIDGE_DO_DETECTION(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_DO_DETECTION, TypeFlavor.INITIATOR),
BRIDGE_FIRMWARE(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_FIRMWARE, TypeFlavor.PROPERTY),
BRIDGE_IPADDRESS(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_IPADDRESS, TypeFlavor.PROPERTY),
BRIDGE_SUBNETMASK(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_SUBNETMASK, TypeFlavor.PROPERTY),
BRIDGE_DEFAULTGW(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_DEFAULTGW, TypeFlavor.PROPERTY),
BRIDGE_DHCP(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_DHCP, TypeFlavor.PROPERTY),
BRIDGE_WLANSSID(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_WLANSSID, TypeFlavor.PROPERTY),
BRIDGE_WLANPASSWORD(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_WLANPASSWORD, TypeFlavor.PROPERTY),
BRIDGE_PRODUCTS(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_PRODUCTS, TypeFlavor.PROPERTY),
BRIDGE_SCENES(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_SCENES, TypeFlavor.PROPERTY),
BRIDGE_CHECK(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_CHECK, TypeFlavor.PROPERTY),
//
ACTUATOR_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION, TypeFlavor.MANIPULATOR_SHUTTER),
ACTUATOR_STATE(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_STATE, TypeFlavor.MANIPULATOR_SWITCH),
ACTUATOR_LIMIT_MINIMUM(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MINIMUM,TypeFlavor.MANIPULATOR_SHUTTER),
ACTUATOR_LIMIT_MAXIMUM(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MAXIMUM,TypeFlavor.MANIPULATOR_SHUTTER),
//
ROLLERSHUTTER_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_ROLLERSHUTTER,VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION, TypeFlavor.MANIPULATOR_SHUTTER),
ROLLERSHUTTER_LIMIT_MINIMUM(VeluxBindingConstants.THING_TYPE_VELUX_ROLLERSHUTTER,VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MINIMUM,TypeFlavor.MANIPULATOR_SHUTTER),
ROLLERSHUTTER_LIMIT_MAXIMUM(VeluxBindingConstants.THING_TYPE_VELUX_ROLLERSHUTTER,VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MAXIMUM,TypeFlavor.MANIPULATOR_SHUTTER),
//
WINDOW_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_WINDOW, VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION, TypeFlavor.MANIPULATOR_SHUTTER),
WINDOW_LIMIT_MINIMUM(VeluxBindingConstants.THING_TYPE_VELUX_WINDOW, VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MINIMUM,TypeFlavor.MANIPULATOR_SHUTTER),
WINDOW_LIMIT_MAXIMUM(VeluxBindingConstants.THING_TYPE_VELUX_WINDOW, VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MAXIMUM,TypeFlavor.MANIPULATOR_SHUTTER),
//
SCENE_ACTION(VeluxBindingConstants.THING_TYPE_VELUX_SCENE, VeluxBindingConstants.CHANNEL_SCENE_ACTION, TypeFlavor.INITIATOR),
SCENE_SILENTMODE(VeluxBindingConstants.THING_TYPE_VELUX_SCENE, VeluxBindingConstants.CHANNEL_SCENE_SILENTMODE, TypeFlavor.WRITEONLY_VOLATILE_SWITCH),
//
VSHUTTER_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_VSHUTTER, VeluxBindingConstants.CHANNEL_VSHUTTER_POSITION, TypeFlavor.MANIPULATOR_SHUTTER),
;
// @formatter:on
private enum TypeFlavor {
/**
* Used to present read-only non-volatile configuration parameters as StringItem.
*/
READONLY_STATIC_STRING,
/**
* Used to present read-only non-volatile configuration parameters as SwitchItem.
*/
READONLY_STATIC_SWITCH,
/**
* Used to present volatile configuration parameters as StringItem.
*/
READONLY_VOLATILE_STRING,
/**
* Used to present volatile configuration parameters as NumberItem.
*/
READONLY_VOLATILE_NUMBER,
/**
* Used to present volatile configuration parameters as NumberItem.
*/
WRITEONLY_VOLATILE_SWITCH,
/**
* Used to present volatile configuration parameters as SwitchItem.
*/
READWRITE_VOLATILE_SWITCH,
/**
* Used to initiate an action.
*/
INITIATOR,
/**
* Used to manipulate an actuator.
*/
MANIPULATOR_SHUTTER,
/**
* Used to manipulate an actuator.
*/
MANIPULATOR_SWITCH,
/**
* Used to present read-only non-volatile configuration parameter (being handled as property of aThing).
*/
PROPERTY,
/**
* Used to define an UNUSABLE entry.
*/
UNUSABLE,
}
/*
* ***************************
* ***** Private Objects *****
*/
private ThingTypeUID thingIdentifier;
private String channelIdentifier;
private Class<? extends GenericItem> itemClass;
private boolean itemIsReadable;
private boolean itemIsWritable;
private boolean itemIsExecutable;
private boolean itemIsToBeRefreshed;
private int itemsRefreshDivider;
private boolean itemIsChannel;
private boolean itemIsProperty;
private static final Logger LOGGER = LoggerFactory.getLogger(VeluxItemType.class);
private static final int REFRESH_CYCLE_FIRST_TIME = 0;
private static final int REFRESH_ONCE_A_DAY = 8640;
private static final int REFRESH_EACH_HOUR = 360;
private static final int REFRESH_EACH_MINUTE = 6;
private static final int REFRESH_EVERY_CYCLE = 1;
/*
* ************************
* ***** Constructors *****
*/
VeluxItemType(ThingTypeUID thingIdentifier, String channelIdentifier, TypeFlavor typeFlavor) {
this.thingIdentifier = thingIdentifier;
this.channelIdentifier = channelIdentifier;
this.itemIsChannel = true;
this.itemIsProperty = false;
switch (typeFlavor) {
case READONLY_STATIC_STRING:
this.itemClass = StringItem.class;
this.itemIsReadable = true;
this.itemIsWritable = false;
this.itemIsExecutable = false;
this.itemIsToBeRefreshed = true;
this.itemsRefreshDivider = REFRESH_ONCE_A_DAY;
break;
case READONLY_STATIC_SWITCH:
this.itemClass = SwitchItem.class;
this.itemIsReadable = true;
this.itemIsWritable = false;
this.itemIsExecutable = false;
this.itemIsToBeRefreshed = true;
this.itemsRefreshDivider = REFRESH_ONCE_A_DAY;
break;
case READONLY_VOLATILE_STRING:
this.itemClass = StringItem.class;
this.itemIsReadable = true;
this.itemIsWritable = false;
this.itemIsExecutable = false;
this.itemIsToBeRefreshed = true;
this.itemsRefreshDivider = REFRESH_EACH_MINUTE;
break;
case READONLY_VOLATILE_NUMBER:
this.itemClass = NumberItem.class;
this.itemIsReadable = true;
this.itemIsWritable = false;
this.itemIsExecutable = false;
this.itemIsToBeRefreshed = true;
this.itemsRefreshDivider = REFRESH_EVERY_CYCLE;
break;
case WRITEONLY_VOLATILE_SWITCH:
this.itemClass = SwitchItem.class;
this.itemIsReadable = false;
this.itemIsWritable = true;
this.itemIsExecutable = false;
this.itemIsToBeRefreshed = false;
this.itemsRefreshDivider = REFRESH_EACH_MINUTE;
break;
case READWRITE_VOLATILE_SWITCH:
this.itemClass = SwitchItem.class;
this.itemIsReadable = true;
this.itemIsWritable = true;
this.itemIsExecutable = false;
this.itemIsToBeRefreshed = true;
this.itemsRefreshDivider = REFRESH_EVERY_CYCLE;
break;
case INITIATOR:
this.itemClass = SwitchItem.class;
this.itemIsReadable = false;
this.itemIsWritable = false;
this.itemIsExecutable = true;
this.itemIsToBeRefreshed = false;
this.itemsRefreshDivider = 1;
break;
case MANIPULATOR_SHUTTER:
this.itemClass = RollershutterItem.class;
this.itemIsReadable = true;
this.itemIsWritable = true;
this.itemIsExecutable = false;
this.itemIsToBeRefreshed = true;
this.itemsRefreshDivider = REFRESH_EACH_MINUTE;
break;
case MANIPULATOR_SWITCH:
this.itemClass = SwitchItem.class;
this.itemIsReadable = true;
this.itemIsWritable = true;
this.itemIsExecutable = false;
this.itemIsToBeRefreshed = true;
this.itemsRefreshDivider = REFRESH_EACH_MINUTE;
break;
case PROPERTY:
this.itemClass = StringItem.class;
this.itemIsReadable = true;
this.itemIsWritable = false;
this.itemIsExecutable = false;
this.itemIsToBeRefreshed = true;
this.itemsRefreshDivider = REFRESH_EACH_HOUR;
this.itemIsChannel = false;
this.itemIsProperty = true;
break;
case UNUSABLE:
default:
this.itemClass = StringItem.class;
this.itemIsReadable = false;
this.itemIsWritable = false;
this.itemIsExecutable = false;
this.itemIsToBeRefreshed = false;
this.itemsRefreshDivider = REFRESH_ONCE_A_DAY;
this.itemIsChannel = false;
}
}
/*
* ********************************
* ***** Class access methods *****
*/
@Override
public String toString() {
return this.thingIdentifier + "/" + this.channelIdentifier;
}
/**
* {@link VeluxItemType} access method to query Identifier on this type of item.
*
* @return <b>thingIdentifier</b> of type String describing the value of the enum {@link VeluxItemType}
* return
*/
public ThingTypeUID getThingTypeUID() {
return this.thingIdentifier;
}
/**
* {@link VeluxItemType} access method to query common (channel/property) identifier on this type of item.
*
* @return <b>channelIdentifier</b> of type String describing the value of the enum {@link VeluxItemType}.
*/
public String getIdentifier() {
return this.channelIdentifier;
}
/**
* {@link VeluxItemType} access method to query the appropriate type of item.
*
* @return <b>itemClass</b> of type Item describing the possible type of this item.
*/
public Class<? extends GenericItem> getItemClass() {
return this.itemClass;
}
/**
* {@link VeluxItemType} access method to query Read possibility on this type of item.
*
* @return <b>itemIsReadable</b> of type boolean describing the ability to perform a write operation.
*/
public boolean isReadable() {
return this.itemIsReadable;
}
/**
* {@link VeluxItemType} access method to query Write possibility on this type of item.
*
* @return <b>itemIsWritable</b> of type boolean describing the ability to perform a write operation.
*/
public boolean isWritable() {
return this.itemIsWritable;
}
/**
* {@link VeluxItemType} access method to query Execute possibility on this type of item.
*
* @return <b>isExecute</b> of type boolean describing the ability to perform a write operation.
*/
public boolean isExecutable() {
return this.itemIsExecutable;
}
/**
* {@link VeluxItemType} access method to query the need of refresh on this type of item.
*
* @return <b>isExecute</b> of type boolean describing the ability to perform a write operation.
*/
public boolean isToBeRefreshed() {
return this.itemIsToBeRefreshed;
}
/**
* {@link VeluxItemType} access method to query the refreshMSecs interval on this type of item.
*
* @return <b>refreshDivider</b> of type int describing the factor.
*/
public int getRefreshDivider() {
return this.itemsRefreshDivider;
}
/**
* {@link VeluxItemType} access method to query the type of this item.
*
* @return <b>isChannel</b> of type boolean describing the need to be handled as channel.
*/
public boolean isChannel() {
return this.itemIsChannel;
}
/**
* {@link VeluxItemType} access method to query the type of this item.
*
* @return <b>itemIsProperty</b> of type boolean describing the need to be handled as property.
*/
public boolean isProperty() {
return this.itemIsProperty;
}
/**
* {@link VeluxItemType} access method to find an enum by itemTypeName.
*
* @param itemTypeName as name of requested Thing of type String.
*
* @return <b>veluxItemType</b> of type VeluxItemType describing the appropriate enum.
*/
public VeluxItemType getByString(String itemTypeName) {
try {
return VeluxItemType.valueOf(itemTypeName);
} catch (IllegalArgumentException e) {
return UNKNOWN;
}
}
/**
* {@link VeluxItemType} access method to find an enum by name.
*
* @param thingIdentifier as name of requested Thing of type String.
* @param channelIdentifier as name of requested Channel of type String.
*
* @return <b>veluxItemType</b> of type VeluxItemType describing the appropriate enum.
*/
public static VeluxItemType getByThingAndChannel(ThingTypeUID thingIdentifier, String channelIdentifier) {
for (VeluxItemType v : VeluxItemType.values()) {
if (thingIdentifier.equals(v.thingIdentifier) && channelIdentifier.equals(v.channelIdentifier)) {
LOGGER.trace("getByThingAndChannel({},{}) returns enum {}.", thingIdentifier, channelIdentifier, v);
return v;
}
}
LOGGER.trace("getByThingAndChannel({},{}) returns enum UNKNOWN.", thingIdentifier, channelIdentifier);
return UNKNOWN;
}
/**
* {@link VeluxItemType} access method to find similar enum entries by thingIdentifier.
*
* @param thingIdentifier as name of requested Thing of type String.
*
* @return <b>listOfveluxItemType</b> of type List of VeluxItemType containing all similar enum entries.
*/
public static List<VeluxItemType> getPropertyEntriesByThing(ThingTypeUID thingIdentifier) {
List<VeluxItemType> list = new ArrayList<>();
for (VeluxItemType v : VeluxItemType.values()) {
if (thingIdentifier.equals(v.thingIdentifier) && v.itemIsProperty) {
list.add(v);
}
}
LOGGER.trace("getPropertyEntriesByThing({}) returns {}.", thingIdentifier, list);
return list;
}
/**
* Helper function: Calculate modulo.
*
* @param a as dividend.
* @param b as divisor.
*
* @return <b>true</b> if zero is remainder after division.
*/
private static boolean isModulo(int a, int b) {
return (a % b) == 0;
}
/**
* {@link VeluxItemType} access method to determine the necessity of being refreshed
* within the current refresh cycle.
*
* @param refreshCycleCounter as identification of the refresh round.
* @param thingIdentifier as name of requested Thing.
* @param channelIdentifier as name of requested Channel.
*
* @return <b>boolean</b> value which expresses the need.
*/
public static boolean isToBeRefreshedNow(int refreshCycleCounter, ThingTypeUID thingIdentifier,
String channelIdentifier) {
VeluxItemType itemType = getByThingAndChannel(thingIdentifier, channelIdentifier);
if (itemType == VeluxItemType.UNKNOWN) {
LOGGER.warn("isToBeRefreshedNow({},{},{}): returning false, as item is not found.", refreshCycleCounter,
thingIdentifier, channelIdentifier);
return false;
}
if (((refreshCycleCounter == REFRESH_CYCLE_FIRST_TIME) && (itemType.isReadable()))
|| (itemType.isToBeRefreshed())) {
if ((refreshCycleCounter == REFRESH_CYCLE_FIRST_TIME)
|| (isModulo(refreshCycleCounter, itemType.getRefreshDivider()))) {
LOGGER.trace("isToBeRefreshedNow(): returning true, as item is to be refreshed, now.");
return true;
} else {
LOGGER.trace("isToBeRefreshedNow(): returning false, as refresh cycle has not yet come for this item.");
}
} else {
LOGGER.trace("isToBeRefreshedNow(): returning false, as item is not refreshable.");
}
return false;
}
}

View File

@@ -0,0 +1,209 @@
/**
* 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;
import java.util.Comparator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This represents the configuration of a openHAB item that is binded to a Velux
* KLF200 Gateway. It contains the following information:
*
* <ul>
* <li><B>bindingItemType</B>
* <P>
* accessable via
* {@link org.openhab.binding.velux.internal.VeluxRSBindingConfig#getBindingItemType
* getBindingItemType} as representation of the Velux device is filed in the Velux bridge.</li>
* <li><B>bindingConfig</B>
* <P>
* 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).</li>
* </ul>
*
* @author Guenther Schreiner - Initial contribution
*/
@NonNullByDefault
public class VeluxRSBindingConfig extends VeluxBindingConfig {
private final Logger logger = LoggerFactory.getLogger(VeluxRSBindingConfig.class);
/**
* The ascending sorted list of generic Objects indexed by an Integer
*/
private SortedMap<Integer, String> mapAscending = new TreeMap<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});
/**
* The descending sorted list of generic Objects indexed by an Integer
*/
private SortedMap<Integer, String> mapDescending = new TreeMap<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
/**
* The sorted list of generic Objects indexed by an Integer
*/
private Integer rollershutterLevel = 0;
private void veluxRollershutterBindingParser(final String channelValue) {
logger.debug("VeluxRollershutterBindingParser({}) called.", channelValue);
String[] channelValueParts = channelValue.trim().split(VeluxBindingConstants.BINDING_VALUES_SEPARATOR);
if ((channelValueParts.length % 2) != 0) {
throw new IllegalArgumentException(
"Velux Rollershutter binding must contain an even number of configuration parts separated by '"
+ VeluxBindingConstants.BINDING_VALUES_SEPARATOR + "' (ignoring binding '" + channelValue
+ "').");
}
for (int idx = 0; idx < channelValueParts.length; idx++) {
logger.trace("VeluxRollershutterBindingParser() processing {}.", channelValueParts[idx]);
int degree;
try {
degree = Integer.parseInt(channelValueParts[idx]);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(
"Velux Rollershutter binding must contain an even number of configuration parts separated by '"
+ VeluxBindingConstants.BINDING_VALUES_SEPARATOR
+ "' each consisting of a shutter level followed by a scene name (ignoring binding '"
+ channelValue + "').");
}
idx++;
mapAscending.put(degree, channelValueParts[idx]);
mapDescending.put(degree, channelValueParts[idx]);
}
for (Map.Entry<Integer, String> nextEntry : mapAscending.entrySet()) {
logger.trace("VeluxRollershutterBindingParser({},{}) processed.", nextEntry.getKey(), nextEntry.getValue());
}
}
/**
* Constructor of the VeluxBindingConfig.
*
* @param bindingItemType
* The Velux item type {@link org.openhab.binding.velux.internal.VeluxItemType
* VeluxItemType} which the Velux device is filed in the Velux bridge.
* @param channelValue
* The optional configuration type of the Velux binding.
* @param rollershutterLevel of type Integer with current position.
*/
public VeluxRSBindingConfig(VeluxItemType bindingItemType, String channelValue, Integer rollershutterLevel) {
super(bindingItemType, channelValue);
logger.trace("VeluxRSBindingConfig(constructor:{},{},{}) called.", bindingItemType, channelValue,
rollershutterLevel);
this.rollershutterLevel = rollershutterLevel;
veluxRollershutterBindingParser(channelValue);
}
/**
* Constructor of the VeluxBindingConfig.
*
* @param bindingItemType
* The Velux item type {@link org.openhab.binding.velux.internal.VeluxItemType
* VeluxItemType} which the Velux device is filed in the Velux bridge.
* @param channelValue
* The optional configuration type of the Velux binding.
*/
public VeluxRSBindingConfig(VeluxItemType bindingItemType, String channelValue) {
super(bindingItemType, channelValue);
logger.trace("VeluxRSBindingConfig(constructor:{},{}) called.", bindingItemType, channelValue);
veluxRollershutterBindingParser(channelValue);
}
/**
* Returns the next shutter level for an DOWN command w/ adjusting the actual position.
*
* @return <b>rollershutterLevel</b> of type Integer with next position after DOWN command.
*/
public Integer getNextAscendingLevel() {
logger.trace("getNextAscendingLevel() called.");
for (Map.Entry<Integer, String> nextEntry : mapAscending.entrySet()) {
if (nextEntry.getKey() > this.rollershutterLevel) {
this.rollershutterLevel = nextEntry.getKey();
break;
}
}
logger.trace("getNextAscendingLevel() returning {}.", this.rollershutterLevel);
return this.rollershutterLevel;
}
/**
* Returns the next shutter level for an UP command w/ adjusting the actual position.
*
* @return <b>rollershutterLevel</b> of type Integer with next position after UP command.
*/
public Integer getNextDescendingLevel() {
logger.trace("getNextDescendingLevel() called.");
for (Map.Entry<Integer, String> nextEntry : mapDescending.entrySet()) {
if (nextEntry.getKey() < this.rollershutterLevel) {
this.rollershutterLevel = nextEntry.getKey();
break;
}
}
logger.trace("getNextDescendingLevel() returning {}.", this.rollershutterLevel);
return this.rollershutterLevel;
}
/**
* Returns the current shutter level w/o adjusting the actual positioning.
*
* @return <b>rollershutterLevel</b> of type Integer with current position.
*
*/
public Integer getLevel() {
logger.trace("getLevel() returning {}.", this.rollershutterLevel);
return this.rollershutterLevel;
}
/**
* Returns the scene name of the current shutter level w/o adjusting the actual positioning.
*
* @return <B>sceneName</B>
* A String describing the next scene.
*/
public String getSceneName() {
return getSceneName(this.rollershutterLevel);
}
/**
* Returns the scene name w/o adjusting the actual positioning.
*
* @param level
* The shutter level is be queried.
* @return <B>sceneName</B>
* A String describing the next scene.
*/
public String getSceneName(Integer level) {
logger.trace("getSceneName({}) called.", level);
logger.trace("getSceneName() returning {}.", mapDescending.get(level));
return mapDescending.get(level);
}
}

View File

@@ -0,0 +1,283 @@
/**
* 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 java.util.Collections;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.velux.internal.bridge.common.BridgeAPI;
import org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol;
import org.openhab.binding.velux.internal.bridge.common.Login;
import org.openhab.binding.velux.internal.bridge.common.Logout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 2nd Level I/O interface towards the <B>Velux</B> bridge.
* It provides methods for pre- and post-communication
* as well as a common method for the real communication.
* The following class access methods exist:
* <UL>
* <LI>{@link VeluxBridge#bridgeLogin} for pre-communication,</LI>
* <LI>{@link VeluxBridge#bridgeLogout} for post-communication,</LI>
* <LI>{@link VeluxBridge#bridgeCommunicate} as method for the common communication.</LI>
* </UL>
* <P>
* 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.
* <P>
* For protocol-specific implementations this value has to be adapted along the inheritance i.e.
* with the protocol-specific class values.
*/
public Set<String> supportedProtocols = Collections.emptySet();
/** BridgeCommunicationProtocol authentication token for Velux Bridge. */
protected String authenticationToken = emptyAuthenticationToken;
/**
* Handler to access global bridge instance methods
*
*/
protected VeluxBridgeInstance bridgeInstance;
/*
* ************************
* ***** Constructors *****
*/
/**
* Constructor.
* <P>
* 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.
* <P>
* 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.
* <P>
* 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 <b>Velux</b> 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 <b>Velux</b> 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 <b>Velux</b> 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 <b>Velux</b> 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.
* <P>
* If possible, it should be overwritten by protocol specific implementation.
* </P>
*
* @return timestamp (default zero).
*/
public long lastCommunication() {
logger.trace("lastCommunication() returns zero.");
return 0L;
}
/**
* Returns the timestamp in milliseconds since Unix epoch
* of last successful communication.
* <P>
* If possible, it should be overwritten by protocol specific implementation.
* </P>
*
* @return timestamp (default zero).
*/
public long lastSuccessfulCommunication() {
logger.trace("lastSuccessfulCommunication() returns zero.");
return 0L;
}
/**
* Provides information about the base-level communication method and
* any kind of available gateway interaction.
* <P>
* 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 <b>Velux</b> veluxBridge
* based on the protocol-specific implementations with common parameters
* passed as arguments (see below) and provided by VeluxBridgeConfiguration.
* <P>
* 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 <b>success</b> of type boolean which signals the success of the communication.
*/
protected abstract boolean bridgeDirectCommunicate(BridgeCommunicationProtocol communication,
boolean useAuthentication);
}

View File

@@ -0,0 +1,167 @@
/**
* 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 <B>Velux</B> bridge.
* <P>
* It provides the methods:
* <UL>
* <LI>{@link #getProducts} for retrieval of information from the bridge.
* <LI>{@link #getChannel} for accessing the retrieved information.
* <LI>{@link #autoRefresh} for retrieval of information for supporting the update the corresponding openHAB items.
* </UL>
*
* @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:
* <ul>
* <li>isRetrieved (boolean),
* <li>existingProducts ({@link VeluxExistingProducts}).
* </ul>
*/
@NonNullByDefault
public class Channel {
public VeluxExistingProducts existingProducts = new VeluxExistingProducts();
}
private Channel channel;
// Constructor methods
/**
* Constructor.
* <P>
* 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);
}
}

View File

@@ -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 <B>Velux</B> bridge.
* <P>
* It therefore provides a method:
* <UL>
* <LI>{@link VeluxBridgeDetectProducts#detectProducts} for starting the detection.
* </UL>
*
* @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 <b>success</b>
* 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;
}
}

View File

@@ -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 <B>Velux</B> bridge.
* <P>
* It therefore provides a method
* <UL>
* <LI>{@link #initiate} for starting the detection.
* </UL>
*
* @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;
}
}

View File

@@ -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;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.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 <B>Velux</B> bridge.
* <P>
* It therefore provides a method
* <UL>
* <LI>{@link #retrieve} for starting the detection.
* <LI>{@link #getChannel} for accessing the retrieved information.
* </UL>
*
* @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:
* <ul>
* <li>{@link #isRetrieved} describing the retrieval state,
* <li>{@link #gwState} containing the brief gateway state,
* <li>{@link #gwStateDescription} containing the verbose gateway state.
* </ul>
*/
@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.
* <P>
* 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 <b>channel</b> 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;
}
}

View File

@@ -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.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 <B>Velux</B> bridge.
* <P>
* It provides the following methods:
* <UL>
* <LI>{@link #retrieve} for retrieval of information.
* <LI>{@link #getChannel} for accessing the retrieved information.
* </UL>
*
* @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:
* <ul>
* <li>isRetrieved (boolean flag),
* <li>firmwareVersion (human readable String).
* </ul>
*/
@NonNullByDefault
public class Channel {
public boolean isRetrieved = false;
public StringType firmwareVersion = new StringType(VeluxBindingConstants.UNKNOWN);
}
private Channel channel;
// Constructor methods
/**
* Constructor.
* <P>
* 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 <b>channel</b> 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;
}
}

View File

@@ -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.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 <B>House Status Monitor</B> on the <B>Velux</B> bridge.
* <P>
* The HSM is responsible for continuous updates towards the communication initiator
* about any changes of actuator states.
* <P>
* 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;
}
}

View File

@@ -0,0 +1,103 @@
/**
* 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 <B>Velux</B> bridge.
* <P>
* It therefore provides the methods
* <UL>
* <LI>{@link VeluxBridgeGetLimitation#getMinimumLimitation} for querying the lower limitation of an actuator,</LI>
* <LI>{@link VeluxBridgeGetLimitation#getMaximumLimitation} for querying the high limitation of an actuator.</LI>
* </UL>
*
* 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;
}
}

View File

@@ -0,0 +1,68 @@
/**
* 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.
* <P>
* <B>Configuration</B>
* </P>
* <UL>
* <LI>{@link org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration VeluxBridgeConfiguration}
* for specification by a specific Velux bridge,</LI>
* </UL>
*
* <P>
* <B>Status</B>
* </P>
* Two methods for bridge-internal configuration retrieval:
* <UL>
* <LI>{@link #existingProducts}
* for retrieving scene information,</LI>
* <LI>{@link #existingScenes}
* for retrieving product information.</LI>
* </UL>
*
* @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 <B>null</B> 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 <B>null</B> in case of any error.
*/
public VeluxExistingScenes existingScenes();
}

View File

@@ -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.openhab.core.library.types.OnOffType;
import org.openhab.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 <B>Velux</B> bridge.
* <P>
* It provides the following methods:
* <UL>
* <LI>{@link #retrieve} for retrieval of information.
* <LI>{@link #getChannel} for accessing the retrieved information.
* </UL>
*
* @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:
* <ul>
* <li>isRetrieved (boolean flag),
* <li>ipAddress,
* <li>subnetMask,
* <li>defaultGW and
* <li>enabledDHCP.
* </ul>
*/
@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.
* <P>
* 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 <b>channel</b> 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;
}
}

View File

@@ -0,0 +1,58 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.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.
* <P>
* Communication
* </P>
* <UL>
* <LI>{@link VeluxBridgeProvider#bridgeCommunicate} for generic communication,</LI>
* <LI>{@link VeluxBridgeProvider#bridgeAPI} for access to all interaction/communication methods.</LI>
* </UL>
*
* @author Guenther Schreiner - Initial contribution
*/
@NonNullByDefault
public interface VeluxBridgeProvider {
/**
* Initializes a client/server communication towards <b>Velux</b> 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 <b>BridgeAPI</b>
* containing all API methods.
*/
public @Nullable BridgeAPI bridgeAPI();
}

View File

@@ -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.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 <B>Velux</B> bridge.
* <P>
* 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;
}
}

View File

@@ -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.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 <B>Velux</B> bridge.
* <P>
* 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;
}
}
}

View File

@@ -0,0 +1,129 @@
/**
* 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 <B>Velux</B> bridge.
* <P>
* It provides the following methods:
* <UL>
* <LI>{@link #getScenes} for retrieval of information.</LI>
* <LI>{@link #getChannel} for accessing the retrieved information.</LI>
* <LI>{@link #autoRefresh} for retrieval of information in case of an empty list of actuators.</LI>
* </UL>
*
* @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:
* <ul>
* <li>existingScenes ({@link VeluxExistingScenes}).
* </ul>
*/
@NonNullByDefault
public class Channel {
public VeluxExistingScenes existingScenes = new VeluxExistingScenes();
}
private Channel channel;
// Constructor methods
/**
* Constructor.
* <P>
* 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);
}
}

View File

@@ -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.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 <B>House Status Monitor</B> on the <B>Velux</B> bridge.
* <P>
* The HSM is responsible for continuous updates towards the communication initiator
* about any changes of actuator states.
* <P>
* 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;
}
}

View File

@@ -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;
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 <B>Velux</B> bridge.
* <P>
* It therefore provides the methods
* <UL>
* <LI>{@link VeluxBridgeSetLimitation#setMinimumLimitation} for modifying the lower limitation of an actuator,</LI>
* <LI>{@link VeluxBridgeSetLimitation#setMaximumLimitation} for modifying the high limitation of an actuator.</LI>
* </UL>
* 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;
}
}

View File

@@ -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 <B>Velux</B> bridge.
* <P>
* It therefore provides a method
* <UL>
* <LI>{@link VeluxBridgeSetSceneVelocity#setSilentMode} for modifying the behaviour of a scene.
* </UL>
* 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;
}
}

View File

@@ -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;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.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 <B>Velux</B> bridge.
* <P>
* It provides the following methods:
* <UL>
* <LI>{@link #retrieve} for retrieval of information.
* <LI>{@link #getChannel} for accessing the retrieved information.
* </UL>
* <P>
*
* @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:
* <ul>
* <li>isRetrieved,
* <li>wlanSSID,
* <li>wlanPassword.
* </ul>
*/
@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.
* <P>
* 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 <b>channel</b> - 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;
}
}

View File

@@ -0,0 +1,107 @@
/**
* 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 <B>Velux</B> bridge.
* <P>
* It provides the one-and-only protocol specific 1st-level communication class.
* Additionally it provides all methods for different gateway interactions.
* <P>
* The following class access methods exist:
* <UL>
* <LI>{@link #getDeviceStatus} for retrieving the bridge state (i.e. IDLE, BUSY, a.s.o),</LI>
* <LI>{@link #getFirmware} for retrieving the firmware version of the bridge,</LI>
* <LI>{@link #getHouseStatus} for retrieving the information about device state changes recognized by the
* bridge,</LI>
* <LI>{@link #getLANConfig} for retrieving the complete LAN information of the bridge,</LI>
* <LI>{@link #getProduct} for retrieving the any information about a device behind the bridge,</LI>
* <LI>{@link #getProductLimitation} for retrieving the limitation information about a device behind the
* bridge,</LI>
* <LI>{@link #getProducts} for retrieving the any information for all devices behind the bridge,</LI>
* <LI>{@link #getScenes} for retrieving the any information for all scenes defined on the bridge,</LI>
* <LI>{@link #getWLANConfig} for retrieving the complete WLAN information of the bridge,</LI>
* <LI>{@link #login} for establishing a trusted connectivity by authentication,</LI>
* <LI>{@link #logout} for tearing down the trusted connectivity by deauthentication,</LI>
* <LI>{@link #runProductCommand} for manipulation of a device behind the bridge (i.e. instructing to
* modify a position),</LI>
* <LI>{@link #runProductDiscovery} for activation of learning mode of the bridge to discovery new
* products,</LI>
* <LI>{@link #runProductIdentification} for human-oriented identification a device behind the bridge (i.e.
* by winking or switching on-and-off),</LI>
* <LI>{@link #runProductSearch} for searching for lost products on the bridge,</LI>
* <LI>{@link #runScene} for manipulation of a set of devices behind the bridge which are tied together as scene,</LI>
* <LI>{@link #setHouseStatusMonitor} for activation or deactivation of the house monitoring mode to be informed about
* device state changes recognized by the bridge,</LI>
* <LI>{@link #setSceneVelocity} for changes the velocity of a scene defined on the bridge (i.e. silent or
* fast mode).</LI>
* </UL>
* <P>
* Message semantic: Retrieval of Bridge configuration and information of devices beyond the bridge.
* <P>
*
* 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();
}

View File

@@ -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;
/**
* Protocol independent bridge communication supported by the Velux bridge.
* <P>
* Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class
* itself.
* <P>
* As 2nd level interface it defines the methods to help in sending a query and
* processing the received answer.
* <P>
* Methods in this interface for the appropriate interaction:
* <UL>
* <LI>{@link name} to return the name of the interaction for human interface.</LI>
* <LI>{@link isCommunicationSuccessful} to signal the success of the interaction (only available
* after storing the response).</LI>
* </UL>
*
* @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();
}

View File

@@ -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;
/**
* <B>Common bridge communication message scheme supported by the </B><I>Velux</I><B> bridge.</B>
* <P>
* Message semantic will be defined by the implementations according to the different comm paths.
* <P>
* In addition to the common methods defined by {@link BridgeCommunicationProtocol}
* each protocol-specific implementation has to provide the following methods:
* <UL>
* <LI>{@link #getState} for retrieval of information.
* </UL>
*
* @see BridgeCommunicationProtocol
*
* @author Guenther Schreiner - Initial contribution.
*/
@NonNullByDefault
public abstract class GetDeviceStatus implements BridgeCommunicationProtocol {
/**
* <B>Parameter of the device state.</B>
*
* @return <b>thisState</b> as VeluxGwState describing the current status of the bridge.
*/
public abstract VeluxGwState getState();
}

View File

@@ -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.VeluxGwFirmware;
/**
* <B>Common bridge communication message scheme supported by the </B><I>Velux</I><B> bridge.</B>
* <P>
* Message semantic will be defined by the implementations according to the different comm paths.
* <P>
* In addition to the common methods defined by {@link BridgeCommunicationProtocol}
* each protocol-specific implementation has to provide the following methods:
* <UL>
* <LI>{@link #getFirmware} for retrieving the Velux firmware information.
* </UL>
*
* @see BridgeCommunicationProtocol
*
* @author Guenther Schreiner - Initial contribution.
*/
@NonNullByDefault
public abstract class GetFirmware implements BridgeCommunicationProtocol {
/**
* <B>Retrieval of firmware version string</B>
*
* @return <b>firmware</b> as VeluxGwFirmware describing the current software of the bridge.
*/
public abstract VeluxGwFirmware getFirmware();
}

View File

@@ -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;
/**
* <B>Common bridge communication message scheme supported by the </B><I>Velux</I><B> bridge.</B>
* <P>
* Message semantic will be defined by the implementations according to the different comm paths.
* <P>
* 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 {
}

View File

@@ -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.VeluxGwLAN;
/**
* <B>Common bridge communication message scheme supported by the </B><I>Velux</I><B> bridge.</B>
* <P>
* Message semantic will be defined by the implementations according to the different comm paths.
* <P>
* In addition to the common methods defined by {@link BridgeCommunicationProtocol}
* each protocol-specific implementation has to provide the following methods:
* <UL>
* <LI>{@link #getLANConfig} for retrieving the Velux LAN configuration information.
* </UL>
*
* @see BridgeCommunicationProtocol
*
* @author Guenther Schreiner - Initial contribution.
*/
@NonNullByDefault
public abstract class GetLANConfig implements BridgeCommunicationProtocol {
/**
* <B>Retrieval of the parameters of the LAN configuration.</B>
*
* @return <b>lanConfig</b> as VeluxGwLAN describing the current status of the bridge.
*/
public abstract VeluxGwLAN getLANConfig();
}

View File

@@ -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.bridge.common;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.velux.internal.things.VeluxProduct;
/**
* <B>Common bridge communication message scheme supported by the </B><I>Velux</I><B> bridge.</B>
* <P>
* Message semantic will be defined by the implementations according to the different comm paths.
* <P>
* In addition to the common methods defined by {@link BridgeCommunicationProtocol}
* each protocol-specific implementation has to provide the following methods:
* <UL>
* <LI>{@link #setProductId} for defining the intended product.
* <LI>{@link #getProduct} for accessing the retrieved information.
* </UL>
*
* @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);
/**
* <B>Retrieval of information about the selected product</B>
*
* @return <b>veluxProduct</b> as VeluxProduct.
*/
public abstract VeluxProduct getProduct();
}

View File

@@ -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.bridge.common;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* <B>Common bridge communication message scheme supported by the </B><I>Velux</I><B> bridge.</B>
* <P>
* Message semantic will be defined by the implementations according to the different comm paths.
* <P>
* In addition to the common methods defined by {@link BridgeCommunicationProtocol}
* each protocol-specific implementation has to provide the following methods:
* <UL>
* <LI>{@link #setActuatorIdAndLimitationType} for defining the intended actuator and the query type.
* <LI>{@link #getLimitation} for accessing the retrieved information.
* </UL>
*
* @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);
/**
* <B>Retrieval of information about the selected product</B>
*
* @return <b>limitation</b> as int.
*/
public abstract int getLimitation();
}

View File

@@ -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.VeluxProduct;
/**
* <B>Common bridge communication message scheme supported by the </B><I>Velux</I><B> bridge.</B>
* <P>
* Message semantic will be defined by the implementations according to the different comm paths.
* <P>
* In addition to the common methods defined by {@link BridgeCommunicationProtocol}
* each protocol-specific implementation has to provide the following methods:
* <UL>
* <LI>{@link #getProducts} for retrieving the Velux products/actuators information.
* </UL>
*
* @see BridgeCommunicationProtocol
*
* @author Guenther Schreiner - Initial contribution.
*
*/
@NonNullByDefault
public abstract class GetProducts implements BridgeCommunicationProtocol {
/**
* <B>Retrieval of information about all products</B>
*
* @return <b>arrayOfVeluxProducts</b> as Array of VeluxProduct.
*/
public abstract VeluxProduct[] getProducts();
}

View File

@@ -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.VeluxScene;
/**
* <B>Common bridge communication message scheme supported by the </B><I>Velux</I><B> bridge.</B>
* <P>
* Message semantic will be defined by the implementations according to the different comm paths.
* <P>
* In addition to the common methods defined by {@link BridgeCommunicationProtocol}
* each protocol-specific implementation has to provide the following methods:
* <UL>
* <LI>{@link #getScenes} for retrieving the Velux scenes information.
* </UL>
*
* @see BridgeCommunicationProtocol
*
* @author Guenther Schreiner - Initial contribution.
*/
@NonNullByDefault
public abstract class GetScenes implements BridgeCommunicationProtocol {
/**
* <B>Retrieval of information about all defined scenes</B>
*
* @return <b>arrayOfScenes</b> as Array of VeluxScene describing the current scene configuration of the bridge.
*/
public abstract VeluxScene[] getScenes();
}

View File

@@ -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;
/**
* <B>Common bridge communication message scheme supported by the </B><I>Velux</I><B> bridge.</B>
* <P>
* Message semantic will be defined by the implementations according to the different comm paths.
* <P>
* In addition to the common methods defined by {@link BridgeCommunicationProtocol}
* each protocol-specific implementation has to provide the following methods:
* <UL>
* <LI>{@link #getWLANConfig} for retrieving the Velux WLAN configuration information.
* </UL>
*
* @see BridgeCommunicationProtocol
*
* @author Guenther Schreiner - Initial contribution.
*/
@NonNullByDefault
public abstract class GetWLANConfig implements BridgeCommunicationProtocol {
/**
* <B>Retrieval of the parameters of the wireless LAN configuration.</B>
*
* @return <b>wlanConfig</b> as VeluxGwWLAN describing the current status of the bridge.
*/
public abstract VeluxGwWLAN getWLANConfig();
}

View File

@@ -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;
/**
* Specific bridge communication message supported by the Velux bridge.
* <P>
* Message semantic: Communication to authenticate itself, resulting in a return of current bridge state.
* <P>
* In addition to the common methods defined by {@link BridgeCommunicationProtocol}
* each protocol-specific implementation has to provide the following methods:
* <UL>
* <LI>{@link #setPassword} for defining the intended authentication value.
* </UL>
*
* @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 <b>authentication token</b> as String which can be used for next operations.
*/
public String getAuthToken() {
return "";
}
}

View File

@@ -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.
* <P>
* Message semantic: Communication to authenticate itself, resulting in a return of current bridge state.
* <P>
* Note: even before the deauthentication, an authentication is intended.
* <P>
* 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 {
}

View File

@@ -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;
/**
* <B>Common bridge communication message scheme supported by the </B><I>Velux</I><B> bridge.</B>
* <P>
* Message semantic will be defined by the implementations according to the different comm paths.
* <P>
* In addition to the common methods defined by {@link BridgeCommunicationProtocol}
* each protocol-specific implementation has to provide the following methods:
* <UL>
* <LI>{@link #setNodeAndMainParameter} for defining the intended node and the main parameter value.
* </UL>
*
* @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);
}

View File

@@ -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;
/**
* <B>Common bridge communication message scheme supported by the </B><I>Velux</I><B> bridge.</B>
* <P>
* Message semantic will be defined by the implementations according to the different comm paths.
* <P>
* 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 {
}

View File

@@ -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;
/**
* <B>Common bridge communication message scheme supported by the </B><I>Velux</I><B> bridge.</B>
* <P>
* Message semantic will be defined by the implementations according to the different comm paths.
* <P>
* In addition to the common methods defined by {@link BridgeCommunicationProtocol}
* each protocol-specific implementation has to provide the following methods:
* <UL>
* <LI>{@link #setProductId} for defining the intended node.
* </UL>
*
* @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;
}
}

View File

@@ -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;
/**
* <B>Common bridge communication message scheme supported by the </B><I>Velux</I><B> bridge.</B>
* <P>
* Message semantic will be defined by the implementations according to the different comm paths.
* <P>
* 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 {
}

View File

@@ -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;
/**
* <B>Common bridge communication message scheme supported by the </B><I>Velux</I><B> bridge.</B>
* <P>
* Message semantic will be defined by the implementations according to the different comm paths.
* <P>
* In addition to the common methods defined by {@link BridgeCommunicationProtocol}
* each protocol-specific implementation has to provide the following methods:
* <UL>
* <LI>{@link #setSceneId} for defining the intended scene.
* </UL>
*
* @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;
}
}

View File

@@ -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;
/**
* <B>Common bridge communication message scheme supported by the </B><I>Velux</I><B> bridge.</B>
* <P>
* Message semantic will be defined by the implementations according to the different comm paths.
* <P>
* In addition to the common methods defined by {@link BridgeCommunicationProtocol}
* each protocol-specific implementation has to provide the following methods:
* <UL>
* <LI>{@link #serviceActivation} for defining the intended parameter value.
* </UL>
*
* @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);
}

View File

@@ -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;
/**
* <B>Common bridge communication message scheme supported by the </B><I>Velux</I><B> bridge.</B>
* <P>
* Message semantic will be defined by the implementations according to the different comm paths.
* <P>
* In addition to the common methods defined by {@link BridgeCommunicationProtocol}
* each protocol-specific implementation has to provide the following methods:
* <UL>
* <LI>{@link #setActuatorIdAndMinimumLimitation} for defining the intended actuator and the minimum limitation
* value,</LI>
* <LI>{@link #setActuatorIdAndMaximumLimitation} for defining the intended actuator and the maximum limitation
* value.</LI>
* </UL>
*
* @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);
}

View File

@@ -0,0 +1,36 @@
/**
* 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;
/**
* <B>Common bridge communication message scheme supported by the </B><I>Velux</I><B> bridge.</B>
* <P>
* Message semantic will be defined by the implementations according to the different comm paths.
* <P>
* In addition to the common methods defined by {@link BridgeCommunicationProtocol}
* each protocol-specific implementation has to provide the following methods:
* <UL>
* <LI>{@link #setMode} for retrieval of information.
* </UL>
*
* @see BridgeCommunicationProtocol
*
* @author Guenther Schreiner - Initial contribution.
*/
@NonNullByDefault
public abstract class SetSceneVelocity implements BridgeCommunicationProtocol {
public abstract SetSceneVelocity setMode(int id, boolean silent);
}

View File

@@ -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;

View File

@@ -0,0 +1,169 @@
/**
* 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.
* <P>
* Message semantic: Retrieval of current bridge state.
* <P>
*
* 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:
*
* <pre>
* {"action":"getDeviceStatus","params":{}}
* </pre>
*
* 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<String, String> params;
public Request() {
this.action = "getDeviceStatus";
this.params = new HashMap<>();
}
}
/**
* Bridge I/O Response message used by {@link JsonVeluxBridge} for deserializing with including component access
* methods
* <P>
* Expected JSON (sample):
*
* <pre>
* {
* "token":"RHIKGlJyZhidI/JSK0a2RQ==",
* "result":true,
* "deviceStatus":"discovering", or "IDLE"
* "data":{},
* "errors":[]
* }
* </pre>
*/
@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<Response> getClassOfResponse() {
return Response.class;
}
@Override
public void setResponse(Object thisResponse) {
response = (Response) thisResponse;
}
@Override
public boolean isCommunicationSuccessful() {
return response.result;
}
@Override
public String getDeviceStatus() {
return response.deviceStatus;
}
@Override
public String[] getErrors() {
return response.errors;
}
/*
* Methods in addition to interface {@link BridgeCommunicationProtocol}.
*/
@Override
public VeluxGwState getState() {
String deviceStatus = this.getDeviceStatus();
byte stateValue = (byte) VeluxGatewayState.GW_S_GWM.getStateValue();
byte subStateValue;
if ("discovering".equals(deviceStatus)) {
subStateValue = (byte) VeluxGatewaySubState.GW_SS_P1.getStateValue();
} else if ("IDLE".equals(deviceStatus)) {
subStateValue = (byte) VeluxGatewaySubState.GW_SS_IDLE.getStateValue();
} else {
subStateValue = (byte) VeluxGatewaySubState.GW_SS_P2.getStateValue();
}
return new VeluxGwState(stateValue, subStateValue);
}
}

View File

@@ -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.json;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.velux.internal.VeluxBindingConstants;
import org.openhab.binding.velux.internal.bridge.common.GetFirmware;
import org.openhab.binding.velux.internal.things.VeluxGwFirmware;
/**
* Specific bridge communication message supported by the Velux bridge.
* <P>
* Message semantic: Retrieval of Bridge configuration.
* <P>
*
* 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.
* <P>
* Resulting JSON:
*
* <pre>
* {"action":"getFirmware","params":{}}
* </pre>
*/
@NonNullByDefault
private static class Request {
@SuppressWarnings("unused")
private String action;
@SuppressWarnings("unused")
private Map<String, String> params;
public Request() {
this.action = "getFirmware";
this.params = new HashMap<>();
}
}
/**
* Bridge Communication Structure containing the version of the firmware.
* <P>
* 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
* <P>
* Expected JSON (sample):
*
* <pre>
* {
* "token":"RHIKGlJyZhidI/JSK0a2RQ==",
* "result":true,
* "deviceStatus":"IDLE",
* "data":{"version":"0.1.1.0.41.0"},
* "errors":[]
* }
* </pre>
*/
@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<Response> getClassOfResponse() {
return Response.class;
}
@Override
public void setResponse(Object thisResponse) {
response = (Response) thisResponse;
}
@Override
public boolean isCommunicationSuccessful() {
return response.result;
}
@Override
public String getDeviceStatus() {
return response.deviceStatus;
}
@Override
public String[] getErrors() {
return response.errors;
}
/**
* Methods in addition to interface {@link JsonBridgeCommunicationProtocol}.
*/
@Override
public VeluxGwFirmware getFirmware() {
VeluxGwFirmware gwFirmware = new VeluxGwFirmware(response.data.version);
return gwFirmware;
}
}

View File

@@ -0,0 +1,189 @@
/**
* 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.openhab.binding.velux.internal.VeluxBindingConstants;
import org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol;
import org.openhab.binding.velux.internal.bridge.common.GetLANConfig;
import org.openhab.binding.velux.internal.things.VeluxGwLAN;
/**
* Specific bridge communication message supported by the Velux bridge.
* <P>
* Message semantic: Retrieval of LAN configuration.
* <P>
*
* 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.
* <P>
* Resulting JSON:
*
* <pre>
* {"action":"get","params":{}}
* </pre>
*/
@NonNullByDefault
private static class Request {
@SuppressWarnings("unused")
private String action;
@SuppressWarnings("unused")
private Map<String, String> params;
public Request() {
this.action = "get";
this.params = new HashMap<>();
}
}
/**
* Bridge Communication Structure containing the network parameters.
* <P>
* Used within structure {@link JCgetLANConfig} to describe the network connectivity of the Bridge.
*
* <pre>
* {"ipAddress":"192.168.45.9","subnetMask":"255.255.255.0","defaultGateway":"192.168.45.129","dhcp":false}
* </pre>
*/
@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
* <P>
* Expected JSON (sample):
*
* <pre>
* {
* "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":[]
* }
* </pre>
*/
@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<Response> getClassOfResponse() {
return Response.class;
}
@Override
public void setResponse(Object response) {
this.response = (Response) response;
}
@Override
public boolean isCommunicationSuccessful() {
return response.getResult();
}
@Override
public String getDeviceStatus() {
return response.getDeviceStatus();
}
@Override
public String[] getErrors() {
return response.getErrors();
}
/**
* Methods in addition to interface {@link BridgeCommunicationProtocol}.
*/
@Override
public VeluxGwLAN getLANConfig() {
VeluxGwLAN gwLAN = new VeluxGwLAN(response.data.ipAddress, response.data.subnetMask,
response.data.defaultGateway, response.data.dhcp);
return gwLAN;
}
}

View File

@@ -0,0 +1,196 @@
/**
* 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.openhab.binding.velux.internal.VeluxBindingConstants;
import org.openhab.binding.velux.internal.bridge.common.GetProducts;
import org.openhab.binding.velux.internal.things.VeluxProduct;
import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex;
import org.openhab.binding.velux.internal.things.VeluxProductName;
import org.openhab.binding.velux.internal.things.VeluxProductType;
/**
* Specific bridge communication message supported by the Velux bridge.
* <P>
* Message semantic: Retrieval of products.
* <P>
*
* 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
*
* <PRE>
* "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"
* ]
* </PRE>
*/
@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.
* <P>
* Resulting JSON:
*
* <pre>
* {"action":"get","params":{}}
* </pre>
*/
@NonNullByDefault
private static class Request {
@SuppressWarnings("unused")
private String action;
@SuppressWarnings("unused")
private Map<String, String> params;
public Request() {
this.action = "get";
this.params = new HashMap<>();
}
}
/**
* Bridge I/O Response message used by {@link org.openhab.binding.velux.internal.bridge.VeluxBridge VeluxBridge} for
* deserialization with including component access methods
* <P>
* Expected JSON (sample):
*
* <pre>
* {
* "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": []
* }
* </pre>
*/
@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<Response> getClassOfResponse() {
return Response.class;
}
@Override
public void setResponse(Object thisResponse) {
response = (Response) thisResponse;
}
@Override
public boolean isCommunicationSuccessful() {
return response.result;
}
@Override
public String getDeviceStatus() {
return response.deviceStatus;
}
@Override
public String[] getErrors() {
return response.errors;
}
/**
* Methods in addition to interface {@link JsonBridgeCommunicationProtocol}.
*/
@Override
public VeluxProduct[] getProducts() {
VeluxProduct[] products = new VeluxProduct[response.data.length];
for (int productIdx = 0; productIdx < response.data.length; productIdx++) {
products[productIdx] = new VeluxProduct(new VeluxProductName(response.data[productIdx].name),
VeluxProductType.get(response.data[productIdx].typeId),
new ProductBridgeIndex(response.data[productIdx].id));
}
return products;
}
}

View File

@@ -0,0 +1,229 @@
/**
* 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.openhab.binding.velux.internal.VeluxBindingConstants;
import org.openhab.binding.velux.internal.bridge.common.GetScenes;
import org.openhab.binding.velux.internal.things.VeluxProductName;
import org.openhab.binding.velux.internal.things.VeluxProductReference;
import org.openhab.binding.velux.internal.things.VeluxProductState;
import org.openhab.binding.velux.internal.things.VeluxScene;
/**
* Specific bridge communication message supported by the Velux bridge.
* <P>
* Message semantic: Retrieval of scene configurations.
* <P>
*
* 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.
* <P>
* Therefore it includes the typeId and name identifying the product, as well as actuator and status.
* <P>
* Used within structure {@link BCscene} to describe the final states of the products belonging to this scene.
*
* <PRE>
* "typeId": 2,
* "name": "Rolladen Schlafzimmer",
* "actuator": 0,
* "status": 0
* </PRE>
*/
@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.
* <P>
* Therefore it includes the name and id identifying the scene, a flag about silence-mode, as well as the different
* states.
* <P>
* These states are defined by an array of {@link BCproductState} as part of this structure.
*
* <PRE>
* {
* "name": "V_DG_Shutter_West_100",
* "id": 0,
* "silent": true,
* "bCproductStates": [
* {
* "typeId": 2,
* "name": "Rolladen Schlafzimmer",
* "actuator": 0,
* "status": 100
* }
* ]
* },
* </PRE>
*/
@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.
* <P>
* Resulting JSON:
*
* <pre>
* {"action":"get","params":{}}
* </pre>
*/
@NonNullByDefault
private static class Request {
@SuppressWarnings("unused")
private String action;
@SuppressWarnings("unused")
private Map<String, String> params;
public Request() {
this.action = "get";
this.params = new HashMap<>();
}
}
/**
* Bridge Communication Structure describing a response to be received from the Velux Bridge.
*
* <PRE>
* {
* "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": []
* }
* </PRE>
*/
@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<Response> getClassOfResponse() {
return Response.class;
}
@Override
public void setResponse(Object thisResponse) {
response = (Response) thisResponse;
}
@Override
public boolean isCommunicationSuccessful() {
return response.result;
}
@Override
public String getDeviceStatus() {
return response.deviceStatus;
}
@Override
public String[] getErrors() {
return response.errors;
}
/**
* Methods in addition to interface {@link JsonBridgeCommunicationProtocol}.
*/
@Override
public VeluxScene[] getScenes() {
VeluxScene[] scenes = new VeluxScene[response.data.length];
for (int sceneIdx = 0; sceneIdx < response.data.length; sceneIdx++) {
VeluxProductState[] productStates = new VeluxProductState[response.data[sceneIdx].products.length];
for (int productIdx = 0; productIdx < response.data[sceneIdx].products.length; productIdx++) {
productStates[productIdx] = new VeluxProductState(
new VeluxProductReference(
new VeluxProductName(response.data[sceneIdx].products[productIdx].name),
response.data[sceneIdx].products[productIdx].typeId),
response.data[sceneIdx].products[productIdx].actuator,
response.data[sceneIdx].products[productIdx].status);
}
scenes[sceneIdx] = new VeluxScene(response.data[sceneIdx].name, response.data[sceneIdx].id,
response.data[sceneIdx].silent, productStates);
}
return scenes;
}
}

View File

@@ -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.json;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.velux.internal.VeluxBindingConstants;
import org.openhab.binding.velux.internal.bridge.common.GetWLANConfig;
import org.openhab.binding.velux.internal.things.VeluxGwWLAN;
/**
* Specific bridge communication message supported by the Velux bridge.
* <P>
* Message semantic: Retrieval of WLAN configuration.
* <P>
*
* 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.
* <P>
* Resulting JSON:
*
* <pre>
* {"action":"wifi","params":{}}
* </pre>
*/
@NonNullByDefault
private static class Request {
@SuppressWarnings("unused")
private String action;
@SuppressWarnings("unused")
private Map<String, String> params;
public Request() {
this.action = "wifi";
this.params = new HashMap<>();
}
}
/**
* Bridge Communication Structure containing the version of the firmware.
* <P>
* Used within structure {@link JCgetWLANConfig} to describe the network connectivity of the Bridge.
*
* <PRE>
* {"password":"Esf56mxqFY","name":"VELUX_KLF_847C"}
* </PRE>
*/
@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
* <P>
* Expected JSON (sample):
*
* <pre>
* {
* "token":"RHIKGlJyZhidI/JSK0a2RQ==",
* "result":true,
* "deviceStatus":"IDLE",
* "data":{"password":"Esf56mxqFY","name":"VELUX_KLF_847C"},
* "errors":[]
* }
* </pre>
*/
@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<Response> getClassOfResponse() {
return Response.class;
}
@Override
public void setResponse(Object response) {
this.response = (Response) response;
}
@Override
public boolean isCommunicationSuccessful() {
return response.getResult();
}
@Override
public String getDeviceStatus() {
return response.deviceStatus;
}
@Override
public String[] getErrors() {
return response.errors;
}
/**
* Methods in addition to interface {@link JsonBridgeCommunicationProtocol}.
*/
@Override
public VeluxGwWLAN getWLANConfig() {
VeluxGwWLAN gwWLAN = new VeluxGwWLAN(response.data.name, response.data.password);
return gwWLAN;
}
}

View File

@@ -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 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.Login;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Specific bridge communication message supported by the Velux bridge.
* <P>
* Message semantic: Communication to authenticate itself, resulting in a return of current bridge state.
* <P>
*
* 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.
* <P>
* Resulting JSON:
*
* <pre>
* {"action":"login","params":{"password":"PASSWORD"}}
* </pre>
*/
@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
* <P>
* <B>Expected JSON (sample):</B>
*
* <pre>
* '{"token": "PHPnfLda71xfGlxoYEOTGQ==", "result": true, "deviceStatus": "IDLE", "data": {}, "errors": [] }'
* </pre>
*/
@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<Response> getClassOfResponse() {
return Response.class;
}
@Override
public void setResponse(Object thisResponse) {
response = (Response) thisResponse;
}
@Override
public boolean isCommunicationSuccessful() {
return response.getResult();
}
@Override
public String getDeviceStatus() {
return response.deviceStatus;
}
@Override
public String[] getErrors() {
return response.errors;
}
/*
* Methods in addition to interface {@link BridgeCommunicationProtocol}.
*/
@Override
public void setPassword(String thisPassword) {
logger.trace("setPassword() called.");
request.params.password = thisPassword;
}
@Override
public String getAuthToken() {
return response.getToken();
}
}

View File

@@ -0,0 +1,136 @@
/**
* 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.Logout;
/**
* Specific bridge communication message supported by the Velux bridge.
* <P>
* Message semantic: Communication to deauthenticate itself, resulting in a return of current bridge state.
* <P>
*
* 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.
* <P>
* Resulting JSON:
*
* <pre>
* {"action":"logout","params":{}}
* </pre>
*/
@NonNullByDefault
private static class Request {
@SuppressWarnings("unused")
private String action;
@SuppressWarnings("unused")
private Map<String, String> params;
public Request() {
this.action = "logout";
this.params = new HashMap<>();
}
}
/**
* Bridge I/O Response message used by {@link JsonVeluxBridge} for deserializing with including component access
* methods
* <P>
* Expected JSON (sample):
*
* <pre>
* '{"token": "PHPnfLda71xfGlxoYEOTGQ==", "result": true, "deviceStatus": "IDLE", "data": {}, "errors": [] }'
* </pre>
*/
@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<Response> getClassOfResponse() {
return Response.class;
}
@Override
public void setResponse(Object thisResponse) {
response = (Response) thisResponse;
}
@Override
public boolean isCommunicationSuccessful() {
return response.getResult();
}
@Override
public String getDeviceStatus() {
return response.deviceStatus;
}
@Override
public String[] getErrors() {
return response.errors;
}
}

View File

@@ -0,0 +1,158 @@
/**
* 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.RunProductDiscovery;
/**
* Specific bridge communication message supported by the Velux bridge.
* <P>
* Message semantic:Action to start discovery of products, i.e. Velux devices.
* <P>
*
* 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:
*
* <pre>
* {"action":"discover","params":{}}
* </pre>
*
* 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<String, String> params;
public Request() {
this.action = "discover";
this.params = new HashMap<>();
}
}
/**
* Bridge I/O Response message used by {@link JsonVeluxBridge} for deserializing with including component access
* methods.
*
* Expected JSON (sample):
*
* <pre>
* {
* "token":"RHIKGlJyZhidI/JSK0a2RQ==",
* "result":true,
* "deviceStatus":"discovering",
* "data":{},
* "errors":[]
* }
* </pre>
*/
@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<Response> getClassOfResponse() {
return Response.class;
}
@Override
public void setResponse(Object thisResponse) {
response = (Response) thisResponse;
}
@Override
public boolean isCommunicationSuccessful() {
return response.getResult();
}
@Override
public String getDeviceStatus() {
return response.getDeviceStatus();
}
@Override
public String[] getErrors() {
return response.getErrors();
}
}

View File

@@ -0,0 +1,176 @@
/**
* 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.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.velux.internal.VeluxBindingConstants;
import org.openhab.binding.velux.internal.bridge.common.RunProductIdentification;
/**
* Specific bridge communication message supported by the Velux bridge.
* <P>
* Message semantic: Trigger action to identify a product, resulting in a return of current bridge state.
* <P>
*
* 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.
* <P>
* Resulting JSON (sample):
*
* <pre>
* {"action":"identify","params":{"id":23,"time":254}}
* </pre>
*/
@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
* <P>
* Expected JSON (sample):
*
* <pre>
* {
* "token": "NkR/AA5xXj7iL6NiIW8keA==",
* "result": false,
* "deviceStatus": "IDLE",
* "data": {},
* "errors": [ 104 ]
* }
* </pre>
*/
@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<Response> getClassOfResponse() {
return Response.class;
}
@Override
public void setResponse(Object response) {
this.response = (Response) response;
}
@Override
public boolean isCommunicationSuccessful() {
return response.getResult();
}
@Override
public String getDeviceStatus() {
return response.getDeviceStatus();
}
@Override
public String[] getErrors() {
return response.getErrors();
}
/*
* Methods in addition to interface {@link BridgeCommunicationProtocol}.
*/
@Override
public JCrunProductIdentification setProductId(int id) {
JCrunProductIdentification.productId = id;
return this;
}
}

View File

@@ -0,0 +1,148 @@
/**
* 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.openhab.binding.velux.internal.VeluxBindingConstants;
import org.openhab.binding.velux.internal.bridge.common.RunProductSearch;
/**
* Specific bridge communication message supported by the Velux bridge.
* <P>
* Message semantic: query for lost nodes, resulting in a return of current bridge state.
* <P>
* Implementing the abstract class {@link RunProductSearch}.
* <P>
* 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:
*
* <pre>
* {"action":"checkLostNodes","params":{}}
* </pre>
*
* 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<String, String> params;
public Request() {
this.action = "checkLostNodes";
this.params = new HashMap<>();
}
}
/**
* 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):
*
* <pre>
* {
* "token":"RHIKGlJyZhidI/JSK0a2RQ==",
* "result":true,
* "deviceStatus":"IDLE",
* "data":[],
* "errors":[]
* }
* </pre>
*/
@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<Response> getClassOfResponse() {
return Response.class;
}
@Override
public void setResponse(Object thisResponse) {
response = (Response) thisResponse;
}
@Override
public boolean isCommunicationSuccessful() {
return response.result;
}
@Override
public String getDeviceStatus() {
return response.deviceStatus;
}
@Override
public String[] getErrors() {
return response.errors;
}
}

View File

@@ -0,0 +1,156 @@
/**
* 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.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.velux.internal.VeluxBindingConstants;
import org.openhab.binding.velux.internal.bridge.common.RunScene;
/**
* Specific bridge communication message supported by the Velux bridge.
* <P>
* Message semantic: Trigger activation of a specific scene, resulting in a return of current bridge state.
* <P>
*
* 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.
* <P>
* Resulting JSON (sample):
*
* <pre>
* {"action":"run","params":{"id":9}}
* </pre>
*/
@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
* <P>
* Expected JSON (sample):
*
* <pre>
* {
* "token":"RHIKGlJyZhidI/JSK0a2RQ==",
* "result":true,
* "deviceStatus":"IDLE",
* "data":{},
* "errors":[]
* }
* </pre>
*/
@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<Response> getClassOfResponse() {
return Response.class;
}
@Override
public void setResponse(Object response) {
this.response = (Response) response;
}
@Override
public boolean isCommunicationSuccessful() {
return response.getResult();
}
@Override
public String getDeviceStatus() {
return response.deviceStatus;
}
@Override
public String[] getErrors() {
return response.errors;
}
/*
* Methods in addition to interface {@link BridgeCommunicationProtocol}.
*/
@Override
public JCrunScene setSceneId(int id) {
request.params.id = id;
return this;
}
}

View File

@@ -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.json;
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.SetSceneVelocity;
/**
* Specific bridge communication message supported by the Velux bridge.
* <P>
* Message semantic: setting of scene silent mode, resulting in a return of current bridge state.
* <P>
*
* 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.
* <P>
* Resulting JSON (sample):
*
* <pre>
* {"action":"setSilentMode","params":{"id":9,"silent":false}}}
* </pre>
*/
@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
* <P>
* Expected JSON (sample):
*
* <pre>
* {
* "token":"RHIKGlJyZhidI/JSK0a2RQ==",
* "result":true,
* "deviceStatus":"IDLE",
* "data":{},
* "errors":[]
* }
* </pre>
*/
@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<Response> getClassOfResponse() {
return Response.class;
}
@Override
public void setResponse(Object response) {
this.response = (Response) response;
}
@Override
public boolean isCommunicationSuccessful() {
return response.getResult();
}
@Override
public String getDeviceStatus() {
return response.deviceStatus;
}
@Override
public String[] getErrors() {
return response.errors;
}
/*
* Methods in addition to interface {@link BridgeCommunicationProtocol}.
*/
@Override
public JCsetSceneVelocity setMode(int id, boolean silent) {
JCsetSceneVelocity.productId = id;
JCsetSceneVelocity.silentMode = silent;
return this;
}
}

View File

@@ -0,0 +1,208 @@
/**
* 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.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;
/**
* JSON-based 3rd Level I/O interface towards the <B>Velux</B> bridge.
* <P>
* It provides the one-and-only protocol specific 1st-level communication class.
* Additionally it provides all methods for different gateway interactions.
* <P>
* The following class access methods exist:
* <UL>
* <LI>{@link JsonBridgeAPI#getDeviceStatus} for retrieving the bridge state (i.e. IDLE, BUSY, a.s.o),</LI>
* <LI>{@link JsonBridgeAPI#getFirmware} for retrieving the firmware version of the bridge,</LI>
* <LI>{@link JsonBridgeAPI#getHouseStatus} for retrieving the information about device state changes recognized by the
* bridge,</LI>
* <LI>{@link JsonBridgeAPI#getLANConfig} for retrieving the complete LAN information of the bridge,</LI>
* <LI>{@link JsonBridgeAPI#getProduct} for retrieving the any information about a device behind the bridge,</LI>
* <LI>{@link JsonBridgeAPI#getProductLimitation} for retrieving the limitation information about a device behind the
* bridge,</LI>
* <LI>{@link JsonBridgeAPI#getProducts} for retrieving the any information for all devices behind the bridge,</LI>
* <LI>{@link JsonBridgeAPI#getScenes} for retrieving the any information for all scenes defined on the bridge,</LI>
* <LI>{@link JsonBridgeAPI#getWLANConfig} for retrieving the complete WLAN information of the bridge,</LI>
* <LI>{@link JsonBridgeAPI#login} for establishing a trusted connectivity by authentication,</LI>
* <LI>{@link JsonBridgeAPI#logout} for tearing down the trusted connectivity by deauthentication,</LI>
* <LI>{@link JsonBridgeAPI#runProductCommand} for manipulation of a device behind the bridge (i.e. instructing to
* modify a position),</LI>
* <LI>{@link JsonBridgeAPI#runProductDiscovery} for activation of learning mode of the bridge to discovery new
* products,</LI>
* <LI>{@link JsonBridgeAPI#runProductIdentification} for human-oriented identification a device behind the bridge (i.e.
* by winking or switching on-and-off),</LI>
* <LI>{@link JsonBridgeAPI#runProductSearch} for searching for lost products on the bridge,</LI>
* <LI>{@link JsonBridgeAPI#runScene} for manipulation of a set of devices behind the bridge which are tied together as
* scene,</LI>
* <LI>{@link JsonBridgeAPI#setHouseStatusMonitor} for activation or deactivation of the house monitoring mode to be
* informed about device state changes recognized by the bridge,</LI>
* <LI>{@link JsonBridgeAPI#setSceneVelocity} for changes the velocity of a scene defined on the bridge (i.e. silent or
* fast mode).</LI>
* </UL>
* <P>
* 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.
* <P>
* 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;
}
}

View File

@@ -0,0 +1,92 @@
/**
* 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;
/**
* <B>Common JSON-based bridge communication message scheme supported by the </B><I>Velux</I><B> bridge.</B>
* <P>
* This bridge communication is an extension of the common
* {@link org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol BridgeCommunicationProtocol}.
* <P>
* 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}.
* <P>
* The implementations will define the information which to send query and receive answer
* through the
* {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider}.
* <P>
* (Methods in this interface for the appropriate interaction:
* <UL>
* <LI>{@link #getURL} to return the URL suffix for accessing the specific service access point.</LI>
* <LI>{@link #getObjectOfRequest} to return the request object for further JSON serialization.</LI>
* <LI>{@link #getClassOfResponse} to retrieve the class of the object of response message for further JSON
* deserialization.</LI>
* <LI>{@link #setResponse} for storing the response according to the desired class after JSON deserialization.</LI>
* <LI>{@link #getDeviceStatus} to retrieve the current device status.</LI>
* <LI>{@link #getErrors} to retrieve the current error status.</LI>
* </UL>
*
*
* @author Guenther Schreiner - Initial contribution.
*/
@NonNullByDefault
interface JsonBridgeCommunicationProtocol extends BridgeCommunicationProtocol {
/**
* Returning the URL suffix for accessing the specific service access point.
*
* @return <b>sapURL</b>
* as String which is to be combined with the bridge address.
*/
String getURL();
/**
* Returning the request object for further JSON serialization.
*
* @return <b>ObjectOfRequest</b>
* is an Object.
*/
Object getObjectOfRequest();
/**
* Returning the class of the object of response message for further JSON deserialization.
*
* @return <b>ClassOfResponseObject</b>
* 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 <b>deviceStatus</b> as String describing the current status of the bridge.
*/
String getDeviceStatus();
/**
* Returning the communication status included within the response message.
*
* @return <b>errors</b> as String[] describing the status of the operation according to the request in depth.
*/
String[] getErrors();
}

View File

@@ -0,0 +1,321 @@
/**
* 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.openhab.core.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 <B>Velux</B> bridge.
* <P>
* It provides methods for pre- and postcommunication
* as well as a common method for the real communication.
* <P>
* The following class access methods exist:
* <UL>
* <LI>{@link VeluxBridge#bridgeLogin} for pre-communication,</LI>
* <LI>{@link VeluxBridge#bridgeLogout} for post-communication,</LI>
* <LI>{@link VeluxBridge#bridgeCommunicate} as method for the common communication.</LI>
* </UL>
* <P>
* 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.
* <P>
* Inherits the initialization of the binding-wide instance for dealing for common informations and
* initializes the Velux bridge connectivity settings.
*
* @param bridgeInstance refers to the binding-wide instance for dealing for common informations.
*/
public JsonVeluxBridge(VeluxBridgeInstance bridgeInstance) {
super(bridgeInstance);
logger.trace("JsonVeluxBridge(constructor) called.");
bridgeAPI = new JsonBridgeAPI(bridgeInstance);
supportedProtocols = new TreeSet<>();
supportedProtocols.add("http");
supportedProtocols.add("https");
logger.trace("JsonVeluxBridge(constructor) done.");
}
/**
* Provides information about the base-level communication method and
* any kind of available gateway interactions.
* <P>
* <B>Note:</B> the implementation within this class {@link JsonVeluxBridge} as inherited from {@link VeluxBridge}
* will return the protocol-specific class implementations.
* <P>
* The information will be initialized by the corresponding API class {@link JsonBridgeAPI}.
*
* @return <b>bridgeAPI</b> of type {@link BridgeAPI} contains all possible methods.
*/
@Override
public BridgeAPI bridgeAPI() {
logger.trace("bridgeAPI() called.");
return bridgeAPI;
}
/**
* <B>Method as implementation of abstract superclass method.</B>
* <P>
* Initializes a client/server communication towards <b>Velux</b> 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 <b>success</b> 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 <b>Velux</b> 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 <b>response</b> 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 <b>Velux</b> bridge.
*
* @param <T> This describes the request type parameter.
* @param <U> This describes the response type parameter.
* @param url as String describing the Service Access Point location i.e. http://localhost/api .
* @param authentication as String providing the Authentication token to be passed with the request header.
* @param request as Object representing the structure of the message request body to be converted into
* JSON.
* @param classOfResponse as Class representing the expected structure of the message response body to be converted
* from JSON.
* @return <b>response</b> of type Object containing all resulting informations, i.e. device status, errors a.s.o.
* Will
* return
* <B>null</B> in case of communication or decoding error.
* @throws java.io.IOException in case of continuous communication I/O failures.
* @throws JsonSyntaxException in case of unusual communication failures.
*/
private <T, U> T io(String url, String authentication, U request, Class<T> classOfResponse)
throws JsonSyntaxException, IOException {
/** Local handles */
int retryCount = 0;
lastCommunicationInMSecs = System.currentTimeMillis();
do {
try {
Gson gson = new Gson();
String jsonRequest = gson.toJson(request);
logger.trace("io() to {} using request {}.", url, jsonRequest);
Properties headerItems = new Properties();
if (authentication.length() > 0) {
headerItems.setProperty("Authorization", String.format("Bearer %s", authentication));
}
InputStream content = IOUtils.toInputStream(jsonRequest, StandardCharsets.UTF_8.name());
String jsonResponse = HttpUtil.executeUrl("PUT", url, headerItems, content, "application/json",
this.bridgeInstance.veluxBridgeConfiguration().timeoutMsecs);
if (jsonResponse == null) {
throw new IOException("transport error");
}
logger.trace("io(): wait time {} msecs.", this.bridgeInstance.veluxBridgeConfiguration().timeoutMsecs);
// Give the bridge some time to breathe
try {
Thread.sleep(this.bridgeInstance.veluxBridgeConfiguration().timeoutMsecs);
} catch (InterruptedException ie) {
logger.trace("io() wait interrupted.");
}
logger.trace("io() got response {}.", jsonResponse.replaceAll("\\p{C}", "."));
jsonResponse = jsonResponse.replaceAll("^.+,\n", "");
logger.trace("io() cleaned response {}.", jsonResponse);
T response = gson.fromJson(jsonResponse, classOfResponse);
lastCommunicationInMSecs = lastSuccessfulCommunicationInMSecs = System.currentTimeMillis();
return response;
} catch (IOException ioe) {
logger.trace("io(): Exception occurred during I/O: {}.", ioe.getMessage());
// Error Retries with Exponential Backoff
long waitTime = ((long) Math.pow(2, retryCount)
* this.bridgeInstance.veluxBridgeConfiguration().timeoutMsecs);
logger.trace("io(): wait time {} msecs.", waitTime);
try {
Thread.sleep(waitTime);
} catch (InterruptedException ie) {
logger.trace("io() wait interrupted.");
}
} catch (JsonSyntaxException jse) {
logger.info("io(): Exception occurred on deserialization: {}, aborting.", jse.getMessage());
throw jse;
}
} while (retryCount++ < this.bridgeInstance.veluxBridgeConfiguration().retries);
throw new IOException(String.format("io(): socket I/O failed (%d times).",
this.bridgeInstance.veluxBridgeConfiguration().retries));
}
/**
* Initializes an authenticated communication with the {@link JsonVeluxBridge <b>Velux</b> bridge}.
*
* @param <T> This describes the request type parameter.
* @param <U> This describes the response type parameter.
* @param url as String describing the Service Access Point location i.e. http://localhost/api .
* @param authentication as String providing the Authentication token to be passed with the request header.
* @param request as Object representing the structure of the message request body to be converted into
* JSON.
* @param classOfResponse as Class representing the expected structure of the message response body to be converted
* from JSON.
* @return <b>response</b> of type T containing all resulting informations, i.e. device status, errors a.s.o. Will
* return
* <B>null</B> in case of communication or decoding error.
* @throws java.io.IOException in case of continuous communication I/O failures.
* @throws JsonSyntaxException in case of unusual communication failures.
*/
private <T, U> T ioAuthenticated(String url, String authentication, U request, Class<T> classOfResponse)
throws JsonSyntaxException, IOException {
return io(url, authentication, request, classOfResponse);
}
/**
* Initializes an unauthenticated communication with the {@link JsonVeluxBridge <b>Velux</b> bridge}.
*
* @param <T> This describes the request type parameter.
* @param <U> This describes the response type parameter.
* @param url as String describing the Service Access Point location i.e. http://localhost/api .
* @param request as Object representing the structure of the message request body to be converted into
* JSON.
* @param classOfResponse as Class representing the expected structure of the message response body to be converted
* from JSON.
* @return <b>response</b> of type Object containing all resulting informations, i.e. device status, errors a.s.o.
* Will
* return
* <B>null</B> in case of communication or decoding error.
* @throws java.io.IOException in case of continuous communication I/O failures.
* @throws JsonSyntaxException in case of unusual communication failures.
*/
private <T, U> T ioUnauthenticated(String url, U request, Class<T> classOfResponse)
throws JsonSyntaxException, IOException {
return io(url, "", request, classOfResponse);
}
}

View File

@@ -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
*/
/**
* JSON-protocol specific implementations of the interactions to IO-homecontrolled devices.
*
* @author Guenther Schreiner - Initial contribution
*/
package org.openhab.binding.velux.internal.bridge.json;

View File

@@ -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 for Generic protocol-independent interactions to IO-homecontrolled devices via the Velux gateway.
* This layer is responsible for the transformation of openHAB "objects" into Velux "object" and vice versa.
*
* @author Guenther Schreiner - Initial contribution
*/
package org.openhab.binding.velux.internal.bridge;

View File

@@ -0,0 +1,151 @@
/**
* 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.GetDeviceStatus;
import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response;
import org.openhab.binding.velux.internal.bridge.slip.utils.Packet;
import org.openhab.binding.velux.internal.things.VeluxGwState;
import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command;
import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Protocol specific bridge communication supported by the Velux bridge:
* <B>Get Bridge Device Status</B>
* <P>
* Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class
* itself.
* <P>
* 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}.
* <P>
* Methods in addition to the mentioned interface:
* <UL>
* <LI>{@link #getState} to retrieve the Velux gateway status.</LI>
* </UL>
*
* @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;
}
}

View File

@@ -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;
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:
* <B>Get Firmware Version</B>
* <P>
* Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class
* itself.
* <P>
* 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}.
* <P>
* Methods in addition to the mentioned interface:
* <UL>
* <LI>{@link #getFirmware} to retrieve the Velux firmware version.</LI>
* </UL>
*
* @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);
}
}

View File

@@ -0,0 +1,183 @@
/**
* 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:
* <B>Retrieve House Status</B>
* <P>
* Common Message semantic: Communication from the bridge and storing returned information within the class itself.
* <P>
* 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}.
* <P>
* Methods in addition to the mentioned interface:
* <UL>
* <LI>{@link #getNtfNodeID} to retrieve the node identifier which has been changed.</LI>
* <LI>{@link #getNtfState} to retrieve the state of the node which has been changed.</LI>
* <LI>{@link #getNtfCurrentPosition} to retrieve the actual position of this node.</LI>
* <LI>{@link #getNtfTarget} to retrieve the target position of this node.</LI>
* </UL>
* <P>
* 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 <b>ntfNodeID</b> returns the Actuator Id as int.
*/
public int getNtfNodeID() {
return ntfNodeID;
}
/**
* @return <b>ntfState</b> returns the state of the Actuator as int.
*/
public int getNtfState() {
return ntfState;
}
/**
* @return <b>ntfCurrentPosition</b> returns the current position of the Actuator as int.
*/
public int getNtfCurrentPosition() {
return ntfCurrentPosition;
}
/**
* @return <b>ntfTarget</b> returns the target position of the Actuator as int.
*/
public int getNtfTarget() {
return ntfTarget;
}
}

View File

@@ -0,0 +1,152 @@
/**
* 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:
* <B>Retrieve LAN configuration</B>
* <P>
* Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class
* itself.
* <P>
* 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}.
* <P>
* Methods in addition to the mentioned interface:
* <UL>
* <LI>{@link #getLANConfig} to retrieve the current LAN configuration.</LI>
* </UL>
*
* @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;
}
}

View File

@@ -0,0 +1,299 @@
/**
* 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:
* <B>Retrieve Product Limitations</B>
* <P>
* Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class
* itself.
* <P>
* 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}.
* <P>
* Methods in addition to the mentioned interface:
* <UL>
* <LI>{@link #setActuatorIdAndLimitationType(int,boolean)} to define the one specific product.</LI>
* </UL>
*
* @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;
}
}

View File

@@ -0,0 +1,267 @@
/**
* 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:
* <B>Retrieve Product</B>
* <P>
* Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class
* itself.
* <P>
* 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}.
* <P>
* Methods in addition to the mentioned interface:
* <UL>
* <LI>{@link #setProductId(int)} to define the one specific product.</LI>
* <LI>{@link #getProduct} to retrieve one specific product.</LI>
* </UL>
*
* @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;
}
}

View File

@@ -0,0 +1,275 @@
/**
* 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:
* <B>Retrieve Products</B>
* <P>
* Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class
* itself.
* <P>
* 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}.
* <P>
* Methods in addition to the mentioned interface:
* <UL>
* <LI>{@link #getProducts()} to retrieve the currently registered products.</LI>
* </UL>
*
* @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];
}
}
}

View File

@@ -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.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.VeluxKLFAPI.Command;
import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber;
import org.openhab.binding.velux.internal.things.VeluxProductState;
import org.openhab.binding.velux.internal.things.VeluxScene;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Protocol specific bridge communication supported by the Velux bridge:
* <B>Retrieve Scenes</B>
* <P>
* Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class
* itself.
* <P>
* 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}.
* <P>
* Methods in addition to the mentioned interface:
* <UL>
* <LI>{@link #getScenes()} to retrieve the set of current scenes.</LI>
* </UL>
*
* @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;
}
/**
* ===========================================================
* <P>
* Public Methods required for abstract class {@link GetScenes}.
*/
@Override
public VeluxScene[] getScenes() {
logger.trace("getScenes(): returning {} scenes.", scenes.length);
return scenes;
}
}

View File

@@ -0,0 +1,123 @@
/**
* 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:
* <B>Retrieve WLAN configuration</B>
* <P>
* Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class
* itself.
* <P>
* 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}.
* <P>
* Methods in addition to the mentioned interface:
* <UL>
* <LI>{@link #getWLANConfig} to retrieve the current WLAN configuration.</LI>
* </UL>
*
* @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);
}
}

View File

@@ -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.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:
* <B>Authenticate / login</B>
* <P>
* Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class
* itself.
* <P>
* 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}.
* <P>
* Methods in addition to the mentioned interface:
* <UL>
* <LI>{@link #setPassword(String)} to define the authentication reqPassword to be used.</LI>
* <LI>{@link #getAuthToken()} to retrieve the authentication reqPassword.</LI>
* </UL>
*
* @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;
}
}

View File

@@ -0,0 +1,101 @@
/**
* 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:
* <B>Retrieve LAN configuration</B>
* <P>
* Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class
* itself.
* <P>
* 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}.
* <P>
* 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;
}
}

View File

@@ -0,0 +1,312 @@
/**
* 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:
* <B>Send Command to Actuator</B>
* <P>
* Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class
* itself.
* <P>
* 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}.
* <P>
* Methods in addition to the mentioned interface:
* <UL>
* <LI>{@link #setNodeAndMainParameter} to define the node and intended parameter value.</LI>
* </UL>
*
* @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;
}
}

View File

@@ -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.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:
* <B>Ask the Bridge to detect (new) products/actuators</B>
* <P>
* Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class
* itself.
* <P>
* 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}.
* <P>
* 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;
}
}

View File

@@ -0,0 +1,203 @@
/**
* 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:
* <B>Identify Product</B>
* <P>
* Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class
* itself.
* <P>
* 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}.
* <P>
* Methods in addition to the mentioned interface:
* <UL>
* <LI>{@link #setProductId(int)} to define the intended product.</LI>
* </UL>
*
* @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.
* <P>
* 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.
* <P>
* Passes the intended Actuator Identifier towards this class for building the request.
*
* @param actuatorId as type int describing the scene to be processed.
* @return <b>this</b> of type {@link SCrunProductIdentification} as class itself.
*/
@Override
public SCrunProductIdentification setProductId(int actuatorId) {
logger.trace("setProductId({}) called.", actuatorId);
this.reqIndexValue0 = actuatorId;
return this;
}
}

View File

@@ -0,0 +1,112 @@
/**
* 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:
* <B>Check for lost Nodes</B>
* <P>
* Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class
* itself.
* <P>
* Implementing the protocol-independent class {@link RunProductSearch}.
* <P>
* 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}.
* <P>
* Methods in addition to the mentioned interface:
* <UL>
* <LI>{@link SCrunProductSearch#getState} to retrieve the Velux gateway status.</LI>
* </UL>
*
* @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;
}
}

View File

@@ -0,0 +1,290 @@
/**
* 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:
* <B>Run Scene</B>
* <P>
* Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class
* itself.
* <P>
* 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}.
* <P>
* Methods in addition to the mentioned interface:
* <UL>
* <LI>{@link #setSceneId} to define the scene to be executed.</LI>
* </UL>
*
* @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.
* <P>
* 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;
}
}

View File

@@ -0,0 +1,144 @@
/**
* 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:
* <B>Modify HouseStatusMonitor</B>
* <P>
* Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class
* itself.
* <P>
* 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}.
* <P>
* Methods in addition to the mentioned interface:
* <UL>
* <LI>{@link #serviceActivation} to define the new service activation state.</LI>
* </UL>
*
* @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;
}
}

View File

@@ -0,0 +1,319 @@
/**
* 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:
* <B>Modify Product Limitations</B>
* <P>
* Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class
* itself.
* <P>
* 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}.
* <P>
* Methods in addition to the mentioned interface:
* <UL>
* <LI>{@link #setActuatorIdAndMinimumLimitation} to set the lower limitation of one specific product.</LI>
* <LI>{@link #setActuatorIdAndMaximumLimitation} to set the higher limitation of one specific product.</LI>
* <LI>{@link #setActuatorIdAndResetLimitation} to reset any limitation of one specific product.</LI>
* </UL>
*
* @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;
}
}

View File

@@ -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.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.VeluxKLFAPI.Command;
import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber;
import org.openhab.binding.velux.internal.things.VeluxProductVelocity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Protocol specific bridge communication supported by the Velux bridge:
* <B>Modify Velocity of an Actuator</B>
* <P>
* Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class
* itself.
* <P>
* 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}.
* <P>
* Methods in addition to the mentioned interface:
* <UL>
* <LI>{@link #setMode} to define the new silence mode of the intended actuator.</LI>
* </UL>
*
* @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.
* <P>
* 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 <b>this</b> 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;
}
}

View File

@@ -0,0 +1,213 @@
/**
* 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 <B>Velux</B> bridge.
* <P>
* It provides the one-and-only protocol specific 1st-level communication class.
* Additionally it provides all methods for different gateway interactions.
* <P>
* The following class access methods exist:
* <UL>
* <LI>{@link SlipBridgeAPI#getDeviceStatus} for retrieving the bridge state (i.e. IDLE, BUSY, a.s.o),</LI>
* <LI>{@link SlipBridgeAPI#getFirmware} for retrieving the firmware version of the bridge,</LI>
* <LI>{@link SlipBridgeAPI#getHouseStatus} for retrieving the information about device state changes recognized by the
* bridge,</LI>
* <LI>{@link SlipBridgeAPI#getLANConfig} for retrieving the complete LAN information of the bridge,</LI>
* <LI>{@link SlipBridgeAPI#getProduct} for retrieving the any information about a device behind the bridge,</LI>
* <LI>{@link SlipBridgeAPI#getProductLimitation} for retrieving the limitation information about a device behind the
* bridge,</LI>
* <LI>{@link SlipBridgeAPI#getProducts} for retrieving the any information for all devices behind the bridge,</LI>
* <LI>{@link SlipBridgeAPI#getScenes} for retrieving the any information for all scenes defined on the bridge,</LI>
* <LI>{@link SlipBridgeAPI#getWLANConfig} for retrieving the complete WLAN information of the bridge,</LI>
* <LI>{@link SlipBridgeAPI#login} for establishing a trusted connectivity by authentication,</LI>
* <LI>{@link SlipBridgeAPI#logout} for tearing down the trusted connectivity by deauthentication,</LI>
* <LI>{@link SlipBridgeAPI#runProductCommand} for manipulation of a device behind the bridge (i.e. instructing to
* modify a position),</LI>
* <LI>{@link SlipBridgeAPI#runProductDiscovery} for activation of learning mode of the bridge to discovery new
* products,</LI>
* <LI>{@link SlipBridgeAPI#runProductIdentification} for human-oriented identification a device behind the bridge (i.e.
* by winking or switching on-and-off),</LI>
* <LI>{@link SlipBridgeAPI#runProductSearch} for searching for lost products on the bridge,</LI>
* <LI>{@link SlipBridgeAPI#runScene} for manipulation of a set of devices behind the bridge which are tied together as
* scene,</LI>
* <LI>{@link SlipBridgeAPI#setHouseStatusMonitor} for activation or deactivation of the house monitoring mode to be
* informed about device state changes recognized by the bridge,</LI>
* <LI>{@link SlipBridgeAPI#setSceneVelocity} for changes the velocity of a scene defined on the bridge (i.e. silent or
* fast mode).</LI>
* </UL>
* <P>
* 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.
* <P>
* 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;
}
}

View File

@@ -0,0 +1,84 @@
/**
* 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:
* <B>Authentication</B>
* <P>
* Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class
* itself.
* <P>
* As 2nd level interface it defines the methods to help in sending a query and
* processing the received answer.
* <P>
* (Additional) Methods in this interface for the appropriate interaction:
* <UL>
* <LI>{@link getRequestCommand} to return the intended command to be sent.</LI>
* <LI>{@link getRequestDataAsArrayOfBytes} to return the intended data part to be sent.</LI>
* <LI>{@link setResponse} to store the response already separated into response command and data part.</LI>
* <LI>{@link isCommunicationFinished} to signal the completeness of the interaction (only available
* after storing the response).</LI>
* </UL>
* <P>
* 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 <b>commandNumber</b>
* is of type {@link CommandNumber}.
*/
public CommandNumber getRequestCommand();
/**
* Returning the data part of the request object for further SLIP serialization.
*
* @return <b>dataAsArrayOfBytes</b>
* 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 <b>isFinished</b>
* is a boolean signaling the end of this transaction.
*/
public boolean isCommunicationFinished();
}

View File

@@ -0,0 +1,384 @@
/**
* 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 <B>Velux</B> bridge.
* <P>
* It provides methods for pre- and postcommunication
* as well as a common method for the real communication.
* <P>
* In addition to the generic {@link VeluxBridge} methods, i.e.
* <UL>
* <LI>{@link VeluxBridge#bridgeLogin} for pre-communication,</LI>
* <LI>{@link VeluxBridge#bridgeLogout} for post-communication,</LI>
* <LI>{@link VeluxBridge#bridgeCommunicate} as method for the common communication,</LI>
* <LI>{@link VeluxBridge#bridgeAPI} as interfacing method to all interaction prototypes,</LI>
* </UL>
* the following class access methods provides the protocol-specific implementation:
* <UL>
* <LI>{@link #bridgeDirectCommunicate} for SLIP-based communication.</LI>
* <LI>{@link #bridgeAPI} for return all defined protocol-specific implementations which are provided by
* {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeAPI SlipBridgeAPI}.</LI>
* </UL>
*
* @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.
* <P>
* 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.
* <P>
* 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.
* <P>
* Inherits the initialization of the binding-wide instance for dealing for common informations and
* initializes the Velux bridge connection settings.
*
* @param bridgeInstance refers to the binding-wide instance for dealing for common informations.
*/
public SlipVeluxBridge(VeluxBridgeInstance bridgeInstance) {
super(bridgeInstance);
logger.trace("SlipVeluxBridge(constructor) called.");
bridgeAPI = new SlipBridgeAPI(bridgeInstance);
supportedProtocols = new TreeSet<>();
supportedProtocols.add("slip");
logger.trace("SlipVeluxBridge(constructor) done.");
}
/**
* Destructor.
* <P>
* 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.
* <P>
* <B>Note:</B> the implementation within this class {@link SlipVeluxBridge} as inherited from {@link VeluxBridge}
* will return the protocol-specific class implementations.
* <P>
* The information will be initialized by the corresponding API class {@link SlipBridgeAPI}.
*
* @return <b>bridgeAPI</b> of type {@link BridgeAPI} contains all possible methods.
*/
@Override
public BridgeAPI bridgeAPI() {
logger.trace("bridgeAPI() called.");
return bridgeAPI;
}
/**
* <B>Method as implementation of abstract superclass method.</B>
* <P>
* Initializes a client/server communication towards <b>Velux</b> 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 <b>success</b> 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 <b>Velux</b> 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 <b>success</b> 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;
}
}

View File

@@ -0,0 +1,240 @@
/**
* 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 <B>Velux</B> bridge.
* It provides methods for a pure client/server communication.
* <P>
* The following class access methods exist:
* <UL>
* <LI>{@link Connection#io} for a complete pair of request and response messages,</LI>
* <LI>{@link Connection#isAlive} to check the presence of a connection,</LI>
* <LI>{@link Connection#isMessageAvailable} to check the presence of an incoming message,</LI>
* <LI>{@link Connection#lastSuccessfulCommunication} returns the timestamp of the last successful communication,</LI>
* <LI>{@link Connection#lastCommunication} returns the timestamp of the last communication,</LI>
* <LI>{@link Connection#resetConnection} for resetting the current connection.</LI>
* </UL>
*
* @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 <B>SlipVeluxBridg</B>.
*
* @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 <b>response</b> 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.");
}
}

View File

@@ -0,0 +1,135 @@
/**
* 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.
* <P>
* 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.
* <p>
* 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 <code>len</code> bytes of data from the contained
* input stream into an array of bytes. An attempt is made to read
* as many as <code>len</code> bytes, but a smaller number may be read,
* possibly zero. The number of bytes actually read is returned as an
* integer.
*
* <p>
* This method blocks until input data is available, end of file is
* detected, or an exception is thrown <B>until</B> the given timeout.
*
* <p>
* If <code>len</code> is zero, then no bytes are read and
* <code>0</code> 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 <code>-1</code> is returned; otherwise, at least one
* byte is read and stored into <code>b</code>.
*
* <p>
* The first byte read is stored into element <code>b[off]</code>, the
* next one into <code>b[off+1]</code>, and so on. The number of bytes read
* is, at most, equal to <code>len</code>. Let <i>k</i> be the number of
* bytes actually read; these bytes will be stored in elements
* <code>b[off]</code> through <code>b[off+</code><i>k</i><code>-1]</code>,
* leaving elements <code>b[off+</code><i>k</i><code>]</code> through
* <code>b[off+len-1]</code> unaffected.
*
* <p>
* In every case, elements <code>b[0]</code> through
* <code>b[off]</code> and elements <code>b[off+len]</code> through
* <code>b[b.length-1]</code> are unaffected.
*
* @param b the buffer into which the data is read.
* @param off the start offset in the destination array <code>b</code>
* @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
* <code>-1</code> if there is no more data because the end
* of the stream has been reached.
* @exception NullPointerException If <code>b</code> is <code>null</code>.
* @exception IndexOutOfBoundsException If <code>off</code> is negative,
* <code>len</code> is negative, or <code>len</code> is greater than
* <code>b.length - off</code>
* @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<Integer> readTask = new Callable<Integer>() {
@Override
public Integer call() throws IOException {
return in.read(b, off, len);
}
};
try {
Future<Integer> future = executor.submit(readTask);
return future.get(timeoutMSecs, TimeUnit.MILLISECONDS);
} catch (RejectedExecutionException e) {
throw new IOException("executor failed", e);
} catch (ExecutionException e) {
throw new IOException("execution failed", e);
} catch (InterruptedException e) {
throw new IOException("read interrupted", e);
} catch (TimeoutException e) {
throw new IOException("read timeout", e);
}
}
}

View File

@@ -0,0 +1,257 @@
/**
* 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.DataOutputStream;
import java.io.IOException;
import java.net.ConnectException;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.velux.internal.VeluxBindingConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Transport layer supported by the Velux bridge.
* <P>
* SLIP-based 2nd Level I/O interface towards the <B>Velux</B> bridge.
* <P>
* It provides methods for pre- and post-communication
* as well as a common method for the real communication.
* <UL>
* <LI>{@link SSLconnection#SSLconnection} for establishing the connection,</LI>
* <LI>{@link SSLconnection#send} for sending a message to the bridge,</LI>
* <LI>{@link SSLconnection#available} for observation whether there are bytes available,</LI>
* <LI>{@link SSLconnection#receive} for receiving a message from the bridge,</LI>
* <LI>{@link SSLconnection#close} for tearing down the connection.</LI>
* <LI>{@link SSLconnection#setTimeout} for adapting communication parameters.</LI>
* </UL>
*
* @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 <B>Velux</B>.
*/
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 <b>ready</b> 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 <b>true</b> 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 <b>packet</b> 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;
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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.
*
* <P>
* Methods available:
* <P>
* Static methods are:
* <UL>
* <LI>{@link #introLogging} for logging used at the beginning of the response handling.</LI>
* <LI>{@link #errorLogging} for error logging during processing.</LI>
* <LI>{@link #outroLogging} logging used at the end of the response handling.</LI>
* <LI>{@link #isLengthValid} for validation of length of the data part of the message.</LI>
* <LI>{@link #check4matchingNodeID} for validation of node identifier.</LI>
* <LI>{@link #check4matchingSessionID} for validation of session identifier.</LI>
* </UL>
*
* @author Guenther Schreiner - Initial contribution.
*/
@NonNullByDefault
public class KLF200Response {
/**
* Provides user-oriented logging in two log levels for monitoring KLF behavior.
* <P>
* 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.
* <P>
* 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.
* <P>
* 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.
* <P>
* 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 <b>isLengthValid</b> 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.
* <P>
* 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 <b>check4matchingAnyID</b> 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 <b>success</b> 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 <b>success</b> 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);
}
}

View File

@@ -0,0 +1,279 @@
/**
* 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.
*
* <P>
* Methods available:
* <UL>
* <LI>{@link #Packet(byte[])} creates a Packet object based on the array of bytes.</LI>
* <LI>{@link #length()} returns the number of bytes contained within this Packet.</LI>
* <LI>{@link #toByteArray} returns the complete Packet as array of bytes.</LI>
* <LI>{@link #getByteArray} returns the a range of bytes as array of bytes.</LI>
* <LI>{@link #toString} returns the complete packet as human-readable String.</LI>
* <LI>{@link #getOneByteValue} returns the value of one byte as int.</LI>
* <LI>{@link #setOneByteValue} sets the value of one byte within the Packet.</LI>
* <LI>{@link #getTwoByteValue} returns the value of two bytes as int.</LI>
* <LI>{@link #setTwoByteValue} sets the value of two bytes within the Packet.</LI>
* <LI>{@link #getFourByteValue(int)} returns the value of four bytes as int.</LI>
* <LI>{@link #setFourByteValue} sets the value of four bytes within the Packet.</LI>
* <LI>{@link #getString} returns the value of a range of bytes as String.</LI>
* <LI>{@link #setString} sets the value of a range of bytes within the Packet.</LI>
* </UL>
* Static methods are:
* <UL>
* <LI>{@link #shortToString} converts a byte into a human-readable hex-String.</LI>
* <LI>{@link #byteArrayToInt} converts a range of four bytes into an int.</LI>
* <LI>{@link #intToIPAddressString} converts an int into a String.</LI>
* </UL>
*
* @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 <b>packetLength</b>
* 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 <b>packet</b>
* 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 <b>packet</b> 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 <b>packetString</b> 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 <b>packetString</b> 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 <b>value</b> 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.
* <P>
* Note: Big-endian LSB-0 encoding.
* </P>
*
* @param position Position (n) within the packet.
* @return <b>value</b> of type int.
*/
public int getTwoByteValue(int position) {
return 0x00 << 24 | 0x00 << 16 | (data[position] & 0xff) << 8 | (data[position + 1] & 0xff);
}
/**
* Modifies the value of the bytes at the positions (n) and (n+1) by setting it to the value passed as int.
* <P>
* Note: Big-endian LSB-0 encoding.
* </P>
*
* @param position Position (n) within the packet.
* @param value of type int.
*/
public void setTwoByteValue(int position, int value) {
data[position] = (byte) ((value >>> 8) & 0xFF);
data[position + 1] = (byte) (value & 0xFF);
}
/**
* Returns the value of the bytes at the positions (n)th to (n+3) as int value for convenience.
* <P>
* Note: Big-endian LSB-0 encoding.
* </P>
*
* @param position Position (n) within the packet.
* @return <b>value</b> of type int.
*/
public int getFourByteValue(int position) {
return data[position] << 24 | (data[position + 1] & 0xff) << 16 | (data[position + 2] & 0xff) << 8
| (data[position + 3] & 0xff);
}
/**
* Modifies the value of the bytes at the positions (n) to (n+3) by setting it to the value passed as int.
* <P>
* Note: Big-endian LSB-0 encoding.
* </P>
*
* @param position Position (n) within the packet.
* @param value of type int.
*/
public void setFourByteValue(int position, int value) {
data[position] = (byte) ((value >>> 24) & 0xFF);
data[position + 1] = (byte) ((value >>> 16) & 0xFF);
data[position + 2] = (byte) ((value >>> 8) & 0xFF);
data[position + 3] = (byte) (value & 0xFF);
}
/**
* Returns the char string converted byte-by-byte starting at the position (n) up to (n+length+1).
* <P>
* Note: Any trailing null char will be eliminated.
* </P>
*
* @param position Position (n) within the packet.
* @param length Length of the intended slice as int.
* @return <b>value</b> of type String.
*/
public String getString(int position, int length) {
return new String(Arrays.copyOfRange(data, position, position + length - 1)).replace("\0", "");
}
/**
* Modifies the value of the bytes starting at the position (n) by setting it to the character values passed as
* String.
* <P>
* Note: The trailing null char will not be stored.
* </P>
*
* @param position Position (n) within the packet.
* @param text of type String.
*/
public void setString(int position, String text) {
System.arraycopy(text, 0, data, 0, text.length());
}
/*
* ===========================================================
* Conversion Methods
*/
/**
* Returns the hex char string representing the byte.
*
* @param oneByte of type byte to be converted.
* @return <b>hexByteString</b> of type String.
*/
static String shortToString(int oneByte) {
return String.format("%02X", oneByte);
}
static int byteArrayToInt(byte[] data) {
return data[0] << 24 | (data[1] & 0xff) << 16 | (data[2] & 0xff) << 8 | (data[3] & 0xff);
}
/**
* Returns the dotted string representing an IP address.
*
* @param ipAddress of type int to be converted.
* @return <b>ipAddressString</b> of type String.
*/
public static String intToIPAddressString(int ipAddress) {
return String.format("%d.%d.%d.%d", ((ipAddress >>> 24) & 0xFF), ((ipAddress >>> 16) & 0xFF),
((ipAddress >>> 8) & 0xFF), (ipAddress & 0xFF));
}
}

View File

@@ -0,0 +1,169 @@
/**
* 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.nio.ByteBuffer;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Transport layer supported by the Velux bridge.
* <P>
* Module semantic: encoding and decoding of frames according to RFC 1055.
* <P>
* 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}.
* <P>
* Methods available:
* <UL>
* <LI>{@link #SlipEncoding(short command, byte[] data) } builds a message based on command and data.</LI>
* <LI>{@link #SlipEncoding(byte[] thisPacket) } splits a message into command and data.</LI>
* <LI>{@link #isValid} returns the number of bytes contained within this Packet.</LI>
* <LI>{@link #getCommand} returns the Command part of the message.</LI>
* <LI>{@link #getData} returns the data part of the message.</LI>
* <LI>{@link #toMessage} returns the complete message.</LI>
* <LI>{@link #toString} returns the message in a human-readable way.</LI>
* </UL>
*
* @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 <b>encodingValid</b>
* of type boolean as status of the encoding or decoding.
*/
public boolean isValid() {
return encodingValid;
}
/**
* Extracts the command.
*
* @return <b>command</b>
* 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 <b>data</b>
* 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 <b>message</b>
* of type Array-of-byte.
*/
public byte[] toMessage() {
return message;
}
@Override
public String toString() {
return new Packet(message).toString();
}
}

View File

@@ -0,0 +1,125 @@
/**
* 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.
* <P>
* Methods available:
* <UL>
* <LI>{@link #encode(byte[] payload) } converts a given payload into transfer byte encoding.</LI>
* <LI>{@link #decode(byte[] packet) } converts a given transfer byte encoding into a payload.</LI>
* </UL>
*
* @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 <b>packet</b>
* 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 <b>payload</b>
* 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;
}
}

View File

@@ -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;

View File

@@ -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.
* <p>
* It contains the factory default values as well.
* <ul>
* <li>{@link VeluxBridgeConfiguration#protocol protocol} protocol type
* (one of http or https or slip),</li>
* <li>{@link VeluxBridgeConfiguration#ipAddress ipAddress} bridge IP address,</li>
* <li>{@link VeluxBridgeConfiguration#tcpPort tcpPort} bridge TCP port,</li>
* <li>{@link VeluxBridgeConfiguration#password password} bridge password,</li>
* <li>{@link VeluxBridgeConfiguration#timeoutMsecs timeoutMsecs} communication timeout in milliseconds,</li>
* <li>{@link VeluxBridgeConfiguration#retries retries} number of retries (with exponential backoff algorithm),</li>
* <li>{@link VeluxBridgeConfiguration#refreshMSecs refreshMSecs} refreshMSecs interval for retrieval of bridge
* information.</li>
* <li>{@link VeluxBridgeConfiguration#isBulkRetrievalEnabled isBulkRetrievalEnabled} flag to use bulk product</LI>
* <li>{@link VeluxBridgeConfiguration#isSequentialEnforced isSequentialEnforced} flag to enforce sequential control on
* actuators.</LI>
* <li>{@link VeluxBridgeConfiguration#isProtocolTraceEnabled isProtocolTraceEnabled} flag to enable protocol logging
* (via loglevel INFO).</li>
* </ul>
* <p>
*
* @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;
}

View File

@@ -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.openhab.core.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 <B>Velux</B> device.
* <p>
* It contains the factory default values as well.
* <p>
* There are three parts. Information for:
* <UL>
* <LI>{@link #sceneName} Name of a scene,</LI>
* <LI>{@link #serial} Unique identification of type actuator, rollershutter and window,</LI>
* <LI>{@link #name} Alternate Unique identification of type actuator, rollershutter and window,</LI>
* <LI>{@link #inverted} Value inversion for an actuator, rollershutter or window,</LI>
* <LI>{@link #velocity} Speed value for a scene,</LI>
* <LI>{@link #sceneLevels} Virtual shutter definition as a set of scenes imitating a shutter,</LI>
* <LI>{@link #currentLevel} Initial state of Virtual shutter definition.</LI>
* </UL>
*
* @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.
* <P>
* <B>Configuration for the channel scene:</B>
* </P>
* <UL>
* <LI>{@link #sceneName} for identification of a set of settings, so called scene.</LI>
* </UL>
*/
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).
* <P>
* <B>Configuration for the channels actuator, rollershutter and window:</B>
* </P>
* <UL>
* <LI>{@link #serial} for identification of a io-homecontrol device,</LI>
* <LI>{@link #name} for alternate identification of a io-homecontrol device,</LI>
* <LI>{@link #inverted} for modified value behavior.</LI>
* <LI>{@link #velocity} for modified action speed.</LI>
* </UL>
*/
public String serial = VeluxProductSerialNo.UNKNOWN;
/**
* {@link #name} of type {@link String}, identifying a io-homecontrol device by its registration name especially
* for <B>somfy</B> as they do not provide a valid serial number.
* <P>
* Part of the {@link #serial Configuration for the channels actuator, rollershutter and window}.
* </P>
*/
String name = VeluxBindingConstants.UNKNOWN;
/**
* {@link #inverted} of type {@link Boolean}, inverts each Channel value. This means 0% will be handled as 100%,
* and vice versa, 100% will be handled as 0%.
* <P>
* Part of the {@link #serial Configuration for the channels actuator, rollershutter and window}.
* </P>
*/
Boolean inverted = false;
/**
* {@link #velocity} of type {@link String}, describes the intended speed of action.
* Possible values are defined within VeluxProductVelocity.
* <P>
* Part of the {@link #serial Configuration for the channels actuator, rollershutter and window}.
* </P>
*/
String velocity = VeluxBindingConstants.UNKNOWN;
/**
* {@link #sceneLevels} of type {@link String}, identifying a number of Velux scenes which act together as a virtual
* shutter. Each scene is defined to a corresponding shutter level.
* <P>
* <B>Configuration for the channel virtualshutter:</B>
* </P>
* <UL>
* <LI>{@link #sceneLevels} for identification of a set of settings, so called scene.</LI>
* </UL>
* <P>
* Additionally it contains an internal variable for keeping the actual virtual shutter level.
* </P>
* <UL>
* <LI>{@link #currentLevel} for identification of a set of settings, so called scene.</LI>
* </UL>
*/
String sceneLevels = VeluxBindingConstants.UNKNOWN;
/**
* {@link #currentLevel} of type {@link int}, which represents the current shutter level.
* <P>
* Private part of the {@link #sceneLevels Configuration for the channel virtualshutter}.
* </P>
*/
int currentLevel = 0;
}

View File

@@ -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
*/
/**
* Class with default openHAB configuration definitions.
*
* @author Guenther Schreiner - Initial contribution
*/
package org.openhab.binding.velux.internal.config;

Some files were not shown because too many files have changed in this diff Show More