[velux] fix bugs with Somfy devices, and switch devices (#9245)

* [velux] add data type to config params in readme
* [velux] eliminate mvn warning
* [velux] fix Somfy bugs: uniqueindex = serial number or name
* [velux] fix inconsistent switch state logic
* [velux] cosmetics on BRIDGE_THING_TYPE_UID

Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
This commit is contained in:
Andrew Fiddian-Green 2020-12-08 17:20:14 +00:00 committed by GitHub
parent d2e5c3e7dd
commit ce5d5ca61d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 129 additions and 97 deletions

View File

@ -82,17 +82,17 @@ These types of Thing only supported in the Velux Bridge in API version two or hi
These types of Thing are configured by means of their serial number in the hub. These types of Thing are configured by means of their serial number in the hub.
In addition there are some optional Configuration Parameters. In addition there are some optional Configuration Parameters.
| Configuration Parameter | Default | Required | Description | | Configuration Parameter | Default | Type | Required | Description |
|-------------------------|------------------------|:--------:|-------------------------------------------------------------------| |-------------------------|---------|---------|:--------:|----------------------------------------------------------------------------------------|
| serial | | Yes | Serial number of the io-homecontrol device in the hub. | | serial | | custom | Yes | Serial number of the device in the hub (custom format 00:00:00:00:00:00:00:00) |
| name | | No | (Optional) name of the io-homecontrol device in the hub. | | name | | text | No | Name of the device in the hub. |
| inverted | false | No | (Optional) the position is inverted (i.e. 0% translates to 100%). | | inverted | false | boolean | No | The `position` and `state` (if available) are inverted (i.e. 0% <-> 100%, OFF <-> ON). |
Notes: Notes:
1. To enable a complete inversion of all parameter values (i.e. for Velux windows), use the property `inverted` or add a trailing star to the eight-byte serial number. For an example, see below at item `Velux DG Window Bathroom`. 1. To enable a complete inversion of all parameter values (i.e. for Velux windows), use the property `inverted` or add a trailing star to the eight-byte serial number. For an example, see below at item `Velux DG Window Bathroom`.
2. Somfy devices do not provide a valid serial number to the Velux KLF200 gateway. The bridge reports a registration of the serial number 00:00:00:00:00:00:00:00. Therefore the binding implements a fallback to allow an item specification with a actuator `name` instead of actuator serial number whenever such an invalid serial number occurs. For an example, see below at item `Velux OG Somfy Shutter`. 2. Somfy devices do not provide a valid serial number to the Velux KLF200 gateway. In this case you should enter the default `serial` number 00:00:00:00:00:00:00:00, and in addition enter the `name` parameter; this is the name that you gave to the actuator when you first registered it in the KLF200 Bridge. For an example, see below at item `Velux OG Somfy Shutter`.
### Thing Configuration for "scene" ### Thing Configuration for "scene"
@ -100,10 +100,10 @@ The Velux Bridge in API version one (firmware version 0.1.1.*) allows activating
So besides the bridge, only one real Thing type exists, namely "scene". So besides the bridge, only one real Thing type exists, namely "scene".
This type of Thing is configured by means of its scene name in the hub. This type of Thing is configured by means of its scene name in the hub.
| Configuration Parameter | Default | Required | Description | | Configuration Parameter | Default | Type | Required | Description |
|-------------------------|------------------------|:--------:|-----------------------------------------------------------------------| |-------------------------|-----------|------|:--------:|-----------------------------------------------------------------------------|
| sceneName | | Yes | Name of the scene in the hub. | | sceneName | | text | Yes | Name of the scene in the hub. |
| velocity | | No | The speed at which the scene will be executed (deafult, silent, fast) | | velocity | 'default' | text | No | The speed at which the scene will be executed ('default', 'silent', 'fast') |
### Thing Configuration for "vshutter" ### Thing Configuration for "vshutter"
@ -112,10 +112,10 @@ So besides the bridge, this binding provides a virtual rollershutter Thing consi
Therefore the respective Item definition contains multiple pairs of rollershutter levels each followed by a scene name. Therefore the respective Item definition contains multiple pairs of rollershutter levels each followed by a scene name.
The virtual shutter Thing must be configured with pairs of level (0..10%) combined with the appropriate scene names (text) as follows. The virtual shutter Thing must be configured with pairs of level (0..10%) combined with the appropriate scene names (text) as follows.
| Configuration Parameter | Default | Required | Description | | Configuration Parameter | Default | Type | Required | Description |
|-------------------------|------------------------|:--------:|-----------------------------------------------------------| |-------------------------|---------|---------|:--------:|-----------------------------------------|
| sceneLevels | | Yes | <Level1>,<Scene1>,<Level2>,<Scene2>,.... | | sceneLevels | | text | Yes | {Level1},{Scene1},{Level2},{Scene2},.. |
| currentLevel | 0 | No | Inverts any device values. | | currentLevel | 0 | integer | No | Inverts any device values (0..100). |
## Supported Channels for Thing Types ## Supported Channels for Thing Types

View File

@ -153,4 +153,6 @@ public class VeluxBindingConstants {
public static final String UNKNOWN_THING_TYPE_ID = "FAILED"; public static final String UNKNOWN_THING_TYPE_ID = "FAILED";
public static final String UNKNOWN_IP_ADDRESS = "xxx.xxx.xxx.xxx"; public static final String UNKNOWN_IP_ADDRESS = "xxx.xxx.xxx.xxx";
public static final String BRIDGE_THING_TYPE_UID = THING_TYPE_BRIDGE.toString();
} }

View File

@ -12,7 +12,7 @@
*/ */
package org.openhab.binding.velux.internal.handler; package org.openhab.binding.velux.internal.handler;
import static org.openhab.binding.velux.internal.VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION; import static org.openhab.binding.velux.internal.VeluxBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
@ -82,6 +82,10 @@ final class ChannelActuatorPosition extends ChannelHandlerTemplate {
LOGGER.trace("handleRefresh(): there are some existing products."); LOGGER.trace("handleRefresh(): there are some existing products.");
} }
Thing2VeluxActuator veluxActuator = thisBridgeHandler.channel2VeluxActuator.get(channelUID); Thing2VeluxActuator veluxActuator = thisBridgeHandler.channel2VeluxActuator.get(channelUID);
if (veluxActuator == null || !veluxActuator.isKnown()) {
LOGGER.warn("handleRefresh(): unknown actuator.");
break;
}
GetProduct bcp = thisBridgeHandler.thisBridge.bridgeAPI().getProduct(); GetProduct bcp = thisBridgeHandler.thisBridge.bridgeAPI().getProduct();
if (bcp == null) { if (bcp == null) {
LOGGER.trace("handleRefresh(): aborting processing as handler is null."); LOGGER.trace("handleRefresh(): aborting processing as handler is null.");
@ -93,10 +97,16 @@ final class ChannelActuatorPosition extends ChannelHandlerTemplate {
VeluxProduct product = bcp.getProduct(); VeluxProduct product = bcp.getProduct();
VeluxProductPosition position = new VeluxProductPosition(product.getDisplayPosition()); VeluxProductPosition position = new VeluxProductPosition(product.getDisplayPosition());
if (position.isValid()) { if (position.isValid()) {
PercentType posPercent = position.getPositionAsPercentType(veluxActuator.isInverted()); if (CHANNEL_ACTUATOR_POSITION.equals(channelId)) {
LOGGER.trace("handleRefresh(): position of actuator is {}%.", posPercent); newState = position.getPositionAsPercentType(veluxActuator.isInverted());
newState = posPercent; LOGGER.trace("handleRefresh(): position of actuator is {}%.", newState);
break; break;
} else if (CHANNEL_ACTUATOR_STATE.equals(channelId)) {
newState = OnOffType.from(
position.getPositionAsPercentType(veluxActuator.isInverted()).intValue() > 50);
LOGGER.trace("handleRefresh(): state of actuator is {}.", newState);
break;
}
} }
LOGGER.trace("handleRefresh(): position of actuator is 'UNDEFINED'."); LOGGER.trace("handleRefresh(): position of actuator is 'UNDEFINED'.");
newState = UnDefType.UNDEF; newState = UnDefType.UNDEF;
@ -124,55 +134,49 @@ final class ChannelActuatorPosition extends ChannelHandlerTemplate {
LOGGER.debug("handleCommand({},{},{},{}) called.", channelUID, channelId, command, thisBridgeHandler); LOGGER.debug("handleCommand({},{},{},{}) called.", channelUID, channelId, command, thisBridgeHandler);
Command newValue = null; Command newValue = null;
do { // just for common exit do { // just for common exit
assert thisBridgeHandler.bridgeParameters.actuators != null : "VeluxBridgeHandler.bridgeParameters.actuators not initialized.";
if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) {
LOGGER.trace("handleCommand(): there are some existing products."); LOGGER.trace("handleCommand(): there are some existing products.");
} }
Thing2VeluxActuator veluxActuator = thisBridgeHandler.channel2VeluxActuator.get(channelUID); Thing2VeluxActuator veluxActuator = thisBridgeHandler.channel2VeluxActuator.get(channelUID);
if (veluxActuator == null || !veluxActuator.isKnown()) {
LOGGER.warn("handleRefresh(): unknown actuator.");
break;
}
VeluxProductPosition targetLevel = VeluxProductPosition.UNKNOWN; VeluxProductPosition targetLevel = VeluxProductPosition.UNKNOWN;
if (channelId.equals(CHANNEL_ACTUATOR_POSITION)) { if (CHANNEL_ACTUATOR_POSITION.equals(channelId)) {
if ((command instanceof UpDownType) && (command == UpDownType.UP)) { if (command instanceof UpDownType) {
LOGGER.trace("handleCommand(): found UP command."); LOGGER.trace("handleCommand(): found UpDownType.{} command.", command);
targetLevel = veluxActuator.isInverted() ? new VeluxProductPosition(PercentType.HUNDRED) targetLevel = UpDownType.UP.equals(command) ^ veluxActuator.isInverted()
: new VeluxProductPosition(PercentType.ZERO); ? new VeluxProductPosition(PercentType.ZERO)
} else if ((command instanceof UpDownType) && (command == UpDownType.DOWN)) {
LOGGER.trace("handleCommand(): found DOWN command.");
targetLevel = veluxActuator.isInverted() ? new VeluxProductPosition(PercentType.ZERO)
: new VeluxProductPosition(PercentType.HUNDRED); : new VeluxProductPosition(PercentType.HUNDRED);
} else if ((command instanceof StopMoveType) && (command == StopMoveType.STOP)) { } else if (command instanceof StopMoveType) {
LOGGER.trace("handleCommand(): found STOP command."); LOGGER.trace("handleCommand(): found StopMoveType.{} command.", command);
targetLevel = new VeluxProductPosition(); targetLevel = StopMoveType.STOP.equals(command) ? new VeluxProductPosition() : targetLevel;
} else if (command instanceof PercentType) { } else if (command instanceof PercentType) {
LOGGER.trace("handleCommand(): found command of type PercentType."); LOGGER.trace("handleCommand(): found PercentType.{} command", command);
PercentType ptCommand = (PercentType) command; PercentType ptCommand = (PercentType) command;
if (veluxActuator.isInverted()) { if (veluxActuator.isInverted()) {
ptCommand = new PercentType(PercentType.HUNDRED.intValue() - ptCommand.intValue()); ptCommand = new PercentType(PercentType.HUNDRED.intValue() - ptCommand.intValue());
} }
LOGGER.trace("handleCommand(): found command to set level to {}.", ptCommand); LOGGER.trace("handleCommand(): found command to set level to {}.", ptCommand);
targetLevel = new VeluxProductPosition(ptCommand); targetLevel = new VeluxProductPosition(ptCommand);
} else {
LOGGER.info("handleCommand({},{}): ignoring command.", channelUID.getAsString(), command);
break;
} }
} else { } else if (CHANNEL_ACTUATOR_STATE.equals(channelId)) {
if ((command instanceof OnOffType) && (command == OnOffType.ON)) { if (command instanceof OnOffType) {
LOGGER.trace("handleCommand(): found ON command."); LOGGER.trace("handleCommand(): found OnOffType.{} command.", command);
targetLevel = veluxActuator.isInverted() ? new VeluxProductPosition(PercentType.HUNDRED) targetLevel = OnOffType.OFF.equals(command) ^ veluxActuator.isInverted()
: new VeluxProductPosition(PercentType.ZERO); ? new VeluxProductPosition(PercentType.ZERO)
} else if ((command instanceof OnOffType) && (command == OnOffType.OFF)) {
LOGGER.trace("handleCommand(): found OFF command.");
targetLevel = veluxActuator.isInverted() ? new VeluxProductPosition(PercentType.ZERO)
: new VeluxProductPosition(PercentType.HUNDRED); : new VeluxProductPosition(PercentType.HUNDRED);
} else {
LOGGER.info("handleCommand({},{}): ignoring command.", channelUID.getAsString(), command);
break;
} }
} }
if (targetLevel == VeluxProductPosition.UNKNOWN) {
LOGGER.info("handleCommand({},{}): ignoring command.", channelUID.getAsString(), command);
break;
}
LOGGER.debug("handleCommand(): sending command with target level {}.", targetLevel); LOGGER.debug("handleCommand(): sending command with target level {}.", targetLevel);
new VeluxBridgeRunProductCommand().sendCommand(thisBridgeHandler.thisBridge, new VeluxBridgeRunProductCommand().sendCommand(thisBridgeHandler.thisBridge,
veluxActuator.getProductBridgeIndex().toInt(), targetLevel); veluxActuator.getProductBridgeIndex().toInt(), targetLevel);
LOGGER.trace("handleCommand(): The new shutter level will be send through the home monitoring events."); LOGGER.trace("handleCommand(): The new shutter level will be send through the home monitoring events.");
if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) {
LOGGER.trace("handleCommand(): position of actuators are updated."); LOGGER.trace("handleCommand(): position of actuators are updated.");
} }

View File

@ -466,7 +466,7 @@ public class VeluxBridgeHandler extends ExtendedBaseBridgeHandler implements Vel
continue; continue;
} }
Thing2VeluxActuator actuator = channel2VeluxActuator.get(channelUID); Thing2VeluxActuator actuator = channel2VeluxActuator.get(channelUID);
if (!actuator.isKnown()) { if (actuator == null || !actuator.isKnown()) {
logger.trace("syncChannelsWithProducts(): channel {} not registered on bridge.", channelUID); logger.trace("syncChannelsWithProducts(): channel {} not registered on bridge.", channelUID);
continue; continue;
} }

View File

@ -13,8 +13,10 @@
package org.openhab.binding.velux.internal.handler.utils; package org.openhab.binding.velux.internal.handler.utils;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.velux.internal.VeluxBindingConstants;
import org.openhab.binding.velux.internal.VeluxBindingProperties; import org.openhab.binding.velux.internal.VeluxBindingProperties;
import org.openhab.binding.velux.internal.handler.VeluxBridgeHandler; import org.openhab.binding.velux.internal.handler.VeluxBridgeHandler;
import org.openhab.binding.velux.internal.things.VeluxExistingProducts;
import org.openhab.binding.velux.internal.things.VeluxProduct; import org.openhab.binding.velux.internal.things.VeluxProduct;
import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex; import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex;
import org.openhab.binding.velux.internal.things.VeluxProductSerialNo; import org.openhab.binding.velux.internal.things.VeluxProductSerialNo;
@ -50,32 +52,60 @@ public class Thing2VeluxActuator {
// Private // Private
private void mapThing2Velux() { private void mapThing2Velux() {
if (!ThingConfiguration.exists(bridgeHandler, channelUID, if (channelUID.toString().startsWith(VeluxBindingConstants.BRIDGE_THING_TYPE_UID)) {
VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER)) { logger.trace("mapThing2Velux(): channel {} is on a Bridge Thing, exiting.", channelUID);
logger.trace("mapThing2Velux(): aborting processing as {} is not set within {}.",
VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER, channelUID);
return; return;
} }
String actuatorSerial = (String) ThingConfiguration.getValue(bridgeHandler, channelUID,
VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER);
logger.trace("mapThing2Velux(): found actuatorSerial={}.", actuatorSerial);
// Handle value inversion // the uniqueIndex is the serial number (if valid)
boolean propertyInverted = false; String uniqueIndex = null;
if (ThingConfiguration.exists(bridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) { boolean invert = false;
propertyInverted = (boolean) ThingConfiguration.getValue(bridgeHandler, channelUID, if (ThingConfiguration.exists(bridgeHandler, channelUID, VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER)) {
VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED); String serial = (String) ThingConfiguration.getValue(bridgeHandler, channelUID,
VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER);
invert = VeluxProductSerialNo.indicatesRevertedValues(serial);
serial = VeluxProductSerialNo.cleaned(serial);
uniqueIndex = ("".equals(serial) || serial.equals(VeluxProductSerialNo.UNKNOWN)) ? null : serial;
} }
isInverted = propertyInverted || VeluxProductSerialNo.indicatesRevertedValues(actuatorSerial); logger.trace("mapThing2Velux(): in {} serialNumber={}.", channelUID, uniqueIndex);
logger.trace("mapThing2Velux(): found isInverted={}.", isInverted);
actuatorSerial = VeluxProductSerialNo.cleaned(actuatorSerial);
if (!bridgeHandler.bridgeParameters.actuators.getChannel().existingProducts.isRegistered(actuatorSerial)) { // if serial number not valid, the uniqueIndex is name (if valid)
logger.warn("mapThing2Velux(): cannot work on unknown actuator with serial {}.", actuatorSerial); if (uniqueIndex == null) {
if (ThingConfiguration.exists(bridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_ACTUATOR_NAME)) {
String name = (String) ThingConfiguration.getValue(bridgeHandler, channelUID,
VeluxBindingProperties.PROPERTY_ACTUATOR_NAME);
uniqueIndex = ("".equals(name) || name.equals(VeluxBindingConstants.UNKNOWN)) ? null : name;
}
logger.trace("mapThing2Velux(): in {} name={}.", channelUID, uniqueIndex);
}
if (uniqueIndex == null) {
logger.warn("mapThing2Velux(): in {} cannot find a uniqueIndex, aborting.", channelUID);
return;
} else {
logger.trace("mapThing2Velux(): in {} uniqueIndex={}, proceeding.", channelUID, uniqueIndex);
}
// handle value inversion
if (!invert) {
if (ThingConfiguration.exists(bridgeHandler, channelUID,
VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) {
invert = (boolean) ThingConfiguration.getValue(bridgeHandler, channelUID,
VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED);
}
}
isInverted = invert;
logger.trace("mapThing2Velux(): in {} isInverted={}.", channelUID, isInverted);
VeluxExistingProducts existing = bridgeHandler.bridgeParameters.actuators.getChannel().existingProducts;
if (!existing.isRegistered(uniqueIndex)) {
logger.warn("mapThing2Velux(): actuator with uniqueIndex={} is not registered", uniqueIndex);
return; return;
} }
logger.trace("mapThing2Velux(): fetching actuator for {}.", actuatorSerial);
thisProduct = bridgeHandler.bridgeParameters.actuators.getChannel().existingProducts.get(actuatorSerial); logger.trace("mapThing2Velux(): fetching actuator for {}.", uniqueIndex);
thisProduct = existing.get(uniqueIndex);
logger.debug("mapThing2Velux(): found actuator {}.", thisProduct); logger.debug("mapThing2Velux(): found actuator {}.", thisProduct);
return; return;
} }

View File

@ -47,7 +47,7 @@ public class VeluxExistingProducts {
// Type definitions, class-internal variables // Type definitions, class-internal variables
private Map<String, VeluxProduct> existingProductsByUniqueIndex; private Map<String, VeluxProduct> existingProductsByUniqueIndex;
private Map<Integer, String> bridgeIndexToSerialNumber; private Map<Integer, String> bridgeIndexToUniqueIndex;
private Map<String, VeluxProduct> modifiedProductsByUniqueIndex; private Map<String, VeluxProduct> modifiedProductsByUniqueIndex;
private int memberCount; private int memberCount;
@ -61,7 +61,7 @@ public class VeluxExistingProducts {
public VeluxExistingProducts() { public VeluxExistingProducts() {
logger.trace("VeluxExistingProducts(constructor) called."); logger.trace("VeluxExistingProducts(constructor) called.");
existingProductsByUniqueIndex = new ConcurrentHashMap<>(); existingProductsByUniqueIndex = new ConcurrentHashMap<>();
bridgeIndexToSerialNumber = new ConcurrentHashMap<>(); bridgeIndexToUniqueIndex = new ConcurrentHashMap<>();
modifiedProductsByUniqueIndex = new ConcurrentHashMap<>(); modifiedProductsByUniqueIndex = new ConcurrentHashMap<>();
memberCount = 0; memberCount = 0;
dirty = true; dirty = true;
@ -70,24 +70,22 @@ public class VeluxExistingProducts {
// Class access methods // Class access methods
public boolean isRegistered(String productUniqueIndexOrSerialNumber) { public boolean isRegistered(String productUniqueIndex) {
logger.trace("isRegistered(String {}) returns {}.", productUniqueIndexOrSerialNumber, boolean result = existingProductsByUniqueIndex.containsKey(productUniqueIndex);
existingProductsByUniqueIndex.containsKey(productUniqueIndexOrSerialNumber) ? "true" : "false"); logger.trace("isRegistered(String {}) returns {}.", productUniqueIndex, result);
return existingProductsByUniqueIndex.containsKey(productUniqueIndexOrSerialNumber); return result;
} }
public boolean isRegistered(VeluxProduct product) { public boolean isRegistered(VeluxProduct product) {
logger.trace("isRegistered(VeluxProduct {}) called.", product.toString()); boolean result = existingProductsByUniqueIndex.containsKey(product.getProductUniqueIndex());
if (product.isV2()) { logger.trace("isRegistered(VeluxProduct {}) returns {}.", product, result);
return isRegistered(product.getSerialNumber()); return result;
}
return isRegistered(product.getProductUniqueIndex());
} }
public boolean isRegistered(ProductBridgeIndex bridgeProductIndex) { public boolean isRegistered(ProductBridgeIndex bridgeProductIndex) {
logger.trace("isRegisteredProductBridgeIndex {}) called.", bridgeProductIndex.toString()); boolean result = bridgeIndexToUniqueIndex.containsKey(bridgeProductIndex.toInt());
String serialNumber = bridgeIndexToSerialNumber.get(bridgeProductIndex.toInt()); logger.trace("isRegistered(ProductBridgeIndex {}) returns {}.", bridgeProductIndex, result);
return serialNumber != null && isRegistered(serialNumber); return result;
} }
public boolean register(VeluxProduct newProduct) { public boolean register(VeluxProduct newProduct) {
@ -97,12 +95,12 @@ public class VeluxExistingProducts {
} }
logger.trace("register() registering new product {}.", newProduct); logger.trace("register() registering new product {}.", newProduct);
String uniqueIndex = newProduct.isV2() ? newProduct.getSerialNumber() : newProduct.getProductUniqueIndex(); String uniqueIndex = newProduct.getProductUniqueIndex();
logger.trace("register() registering by UniqueIndex {}", uniqueIndex); logger.trace("register() registering by UniqueIndex {}", uniqueIndex);
existingProductsByUniqueIndex.put(uniqueIndex, newProduct); existingProductsByUniqueIndex.put(uniqueIndex, newProduct);
logger.trace("register() registering by ProductBridgeIndex {}", newProduct.getBridgeProductIndex().toInt()); logger.trace("register() registering by ProductBridgeIndex {}", newProduct.getBridgeProductIndex().toInt());
bridgeIndexToSerialNumber.put(newProduct.getBridgeProductIndex().toInt(), newProduct.getSerialNumber()); bridgeIndexToUniqueIndex.put(newProduct.getBridgeProductIndex().toInt(), uniqueIndex);
logger.trace("register() registering set of modifications by UniqueIndex {}", uniqueIndex); logger.trace("register() registering set of modifications by UniqueIndex {}", uniqueIndex);
modifiedProductsByUniqueIndex.put(uniqueIndex, newProduct); modifiedProductsByUniqueIndex.put(uniqueIndex, newProduct);
@ -125,8 +123,7 @@ public class VeluxExistingProducts {
dirty |= thisProduct.setCurrentPosition(productPosition); dirty |= thisProduct.setCurrentPosition(productPosition);
dirty |= thisProduct.setTarget(productTarget); dirty |= thisProduct.setTarget(productTarget);
if (dirty) { if (dirty) {
String uniqueIndex = thisProduct.isV2() ? thisProduct.getSerialNumber() String uniqueIndex = thisProduct.getProductUniqueIndex();
: thisProduct.getProductUniqueIndex();
logger.trace("update(): updating by UniqueIndex {}.", uniqueIndex); logger.trace("update(): updating by UniqueIndex {}.", uniqueIndex);
existingProductsByUniqueIndex.replace(uniqueIndex, thisProduct); existingProductsByUniqueIndex.replace(uniqueIndex, thisProduct);
modifiedProductsByUniqueIndex.put(uniqueIndex, thisProduct); modifiedProductsByUniqueIndex.put(uniqueIndex, thisProduct);
@ -141,21 +138,16 @@ public class VeluxExistingProducts {
currentProduct.getCurrentPosition(), currentProduct.getTarget()); currentProduct.getCurrentPosition(), currentProduct.getTarget());
} }
public VeluxProduct get(String productUniqueIndexOrSerialNumber) { public VeluxProduct get(String productUniqueIndex) {
logger.trace("get({}) called.", productUniqueIndexOrSerialNumber); logger.trace("get({}) called.", productUniqueIndex);
if (!isRegistered(productUniqueIndexOrSerialNumber)) { return existingProductsByUniqueIndex.getOrDefault(productUniqueIndex, VeluxProduct.UNKNOWN);
return VeluxProduct.UNKNOWN;
}
return existingProductsByUniqueIndex.getOrDefault(productUniqueIndexOrSerialNumber, VeluxProduct.UNKNOWN);
} }
public VeluxProduct get(ProductBridgeIndex bridgeProductIndex) { public VeluxProduct get(ProductBridgeIndex bridgeProductIndex) {
logger.trace("get({}) called.", bridgeProductIndex); logger.trace("get({}) called.", bridgeProductIndex);
String serialNumber = bridgeIndexToSerialNumber.get(bridgeProductIndex.toInt()); String unique = bridgeIndexToUniqueIndex.get(bridgeProductIndex.toInt());
if (!isRegistered(bridgeProductIndex) || serialNumber == null) { return unique != null ? existingProductsByUniqueIndex.getOrDefault(unique, VeluxProduct.UNKNOWN)
return VeluxProduct.UNKNOWN; : VeluxProduct.UNKNOWN;
}
return existingProductsByUniqueIndex.getOrDefault(serialNumber, VeluxProduct.UNKNOWN);
} }
public VeluxProduct[] values() { public VeluxProduct[] values() {

View File

@ -217,7 +217,10 @@ public class VeluxProduct {
// Class helper methods // Class helper methods
public String getProductUniqueIndex() { public String getProductUniqueIndex() {
return this.name.toString().concat("#").concat(this.typeId.toString()); if (!v2 || serialNumber.startsWith(VeluxProductSerialNo.UNKNOWN)) {
return name.toString();
}
return VeluxProductSerialNo.cleaned(serialNumber);
} }
// Getter and Setter methods // Getter and Setter methods

View File

@ -138,6 +138,7 @@
<label>@text/config.velux.thing.rollershutter.serial.label</label> <label>@text/config.velux.thing.rollershutter.serial.label</label>
<description>@text/config.velux.thing.rollershutter.serial.description</description> <description>@text/config.velux.thing.rollershutter.serial.description</description>
<required>true</required> <required>true</required>
<default>00:00:00:00:00:00:00:00</default>
<advanced>false</advanced> <advanced>false</advanced>
</parameter> </parameter>
<parameter name="name" type="text"> <parameter name="name" type="text">