[hdpowerview] Corrections to shade database and capabilities (#12902)

* [hdpowerview] add type 66 shutters to database
* [hdpowerview] shade database updates
* [hdpowerview] shade database additions and corrections
* [hdpowerview] enhance database features
* [hdpowerview] fix capabilities 8, 9 functionality
* [hdpowerview] adjust tests to match new capabilities
* [hdpowerview] correct method visibility
* [hdpowerview] test type 44
* [hdpowerview] remove comment
* [hdpowerview] name change
* [hdpowerview] remove comments attribute
* [hdpowerview] refactor capabilities code
* [hdpowerview] 'hard' properties now hidden
* [hdpowerview] adopt reviewer suggestion
* [hdpowerview] refactor constant names

Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
This commit is contained in:
Andrew Fiddian-Green
2022-06-10 20:10:31 +02:00
committed by GitHub
parent 798d59b2c5
commit c806c76631
6 changed files with 255 additions and 97 deletions

View File

@@ -67,8 +67,6 @@ public class HDPowerViewBindingConstants {
// Shade properties
public static final String PROPERTY_SHADE_TYPE = "type";
public static final String PROPERTY_SHADE_CAPABILITIES = "capabilities";
public static final String PROPERTY_SECONDARY_RAIL_DETECTED = "secondaryRailDetected";
public static final String PROPERTY_TILT_ANYWHERE_DETECTED = "tiltAnywhereDetected";
public static final String PROPERTY_MOTOR_FIRMWARE_VERSION = "motorFirmwareVersion";
public static final List<String> NETBIOS_NAMES = Arrays.asList("PDBU-Hub3.0", "PowerView-Hub");

View File

@@ -158,7 +158,7 @@ public class ShadePosition {
}
return new PercentType((int) Math.round((double) position1 / MAX_SHADE * 100));
}
if (PRIMARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped()) {
if (!SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped()) {
return PercentType.ZERO;
}
break;
@@ -182,6 +182,10 @@ public class ShadePosition {
if (PRIMARY_POSITION.equals(posKind1) && shadeCapabilities.supportsTiltOnClosed()) {
return position1 != 0 ? UnDefType.UNDEF : PercentType.ZERO;
}
if (SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped()
&& shadeCapabilities.supportsTiltOnClosed()) {
return PercentType.HUNDRED;
}
break;
case ERROR_UNKNOWN:

View File

@@ -18,6 +18,7 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,16 +41,17 @@ public class ShadeCapabilitiesDatabase {
*/
private static final Map<Integer, Capabilities> CAPABILITIES_DATABASE = Arrays.asList(
// @formatter:off
new Capabilities(0).primary() .tiltOnClosed() .text("Bottom Up"),
new Capabilities(0).primary() .text("Bottom Up"),
new Capabilities(1).primary() .tiltOnClosed() .text("Bottom Up Tilt 90°"),
new Capabilities(2).primary() .tiltAnywhere().tilt180() .text("Bottom Up Tilt 180°"),
new Capabilities(3).primary() .tiltOnClosed() .text("Vertical"),
new Capabilities(4).primary() .tiltAnywhere().tilt180() .text("Vertical Tilt 180°"),
new Capabilities(3).primary() .tiltAnywhere().tilt180() .text("Vertical Tilt 180°"),
new Capabilities(4).primary() .text("Vertical"),
new Capabilities(5) .tiltAnywhere().tilt180() .text("Tilt Only 180°"),
new Capabilities(6).primaryInverted() .text("Top Down"),
new Capabilities(7).primary() .secondary() .text("Top Down Bottom Up"),
new Capabilities(8).primary() .secondaryOverlapped().text("Dual Overlapped"),
new Capabilities(9).primary() .tiltAnywhere() .secondaryOverlapped().text("Dual Overlapped Tilt 90°"),
// note: for the following capabilities entry the 'tiltOnClosed()' applies to the primary shade
new Capabilities(9).primary() .tiltOnClosed() .secondaryOverlapped().text("Dual Overlapped Tilt 90°"),
// @formatter:on
new Capabilities()).stream().collect(Collectors.toMap(Capabilities::getValue, Function.identity()));
@@ -58,18 +60,20 @@ public class ShadeCapabilitiesDatabase {
*/
private static final Map<Integer, Type> TYPE_DATABASE = Arrays.asList(
// @formatter:off
new Type( 1).capabilities(0).text("Roller / Solar"),
new Type( 4).capabilities(0).text("Roman"),
new Type( 5).capabilities(0).text("Bottom Up"),
new Type( 6).capabilities(0).text("Duette"),
new Type( 7).capabilities(6).text("Top Down"),
new Type( 8).capabilities(7).text("Duette Top Down Bottom Up"),
new Type( 9).capabilities(7).text("Duette DuoLite Top Down Bottom Up"),
new Type(18).capabilities(1).text("Silhouette"),
new Type(18).capabilities(1).text("Pirouette"),
new Type(23).capabilities(1).text("Silhouette"),
new Type(38).capabilities(9).text("Silhouette Duolite"),
new Type(42).capabilities(0).text("M25T Roller Blind"),
new Type(43).capabilities(1).text("Facette"),
new Type(44).capabilities(0).text("Twist"),
// note: the following shade type has the functionality of a capabilities 1 shade
new Type(44).capabilities(0).text("Twist").capabilitiesOverride(1),
new Type(47).capabilities(7).text("Pleated Top Down Bottom Up"),
new Type(49).capabilities(0).text("AC Roller"),
new Type(51).capabilities(2).text("Venetian"),
@@ -79,9 +83,9 @@ public class ShadeCapabilitiesDatabase {
new Type(62).capabilities(2).text("Venetian"),
new Type(65).capabilities(8).text("Vignette Duolite"),
new Type(66).capabilities(5).text("Shutter"),
new Type(69).capabilities(3).text("Curtain Left Stack"),
new Type(70).capabilities(3).text("Curtain Right Stack"),
new Type(71).capabilities(3).text("Curtain Split Stack"),
new Type(69).capabilities(4).text("Curtain Left Stack"),
new Type(70).capabilities(4).text("Curtain Right Stack"),
new Type(71).capabilities(4).text("Curtain Split Stack"),
new Type(79).capabilities(8).text("Duolite Lift"),
// @formatter:on
new Type()).stream().collect(Collectors.toMap(Type::getValue, Function.identity()));
@@ -112,6 +116,7 @@ public class ShadeCapabilitiesDatabase {
*/
public static class Type extends Base {
private int capabilities = -1;
private int capabilitiesOverride = -1;
protected Type() {
}
@@ -130,6 +135,11 @@ public class ShadeCapabilitiesDatabase {
return this;
}
protected Type capabilitiesOverride(int capabilitiesOverride) {
this.capabilitiesOverride = capabilitiesOverride;
return this;
}
/**
* Get shade types's 'capabilities'.
*
@@ -138,6 +148,15 @@ public class ShadeCapabilitiesDatabase {
public int getCapabilities() {
return capabilities;
}
/**
* Get shade's type specific 'capabilities'.
*
* @return 'typeCapabilities'.
*/
public int getCapabilitiesOverride() {
return capabilitiesOverride;
}
}
/**
@@ -301,13 +320,35 @@ public class ShadeCapabilitiesDatabase {
}
/**
* Return a Capabilities class instance that corresponds to the given 'capabilities' parameter.
* Return a Capabilities class instance that corresponds to the given 'capabilitiesId' parameter. If the
* 'capabilitiesId' parameter is for a valid capabilities entry in the database, then that respective Capabilities
* class instance is returned. Otherwise a blank Capabilities class instance is returned.
*
* @param capabilities the shade 'capabilities' parameter.
* @return corresponding instance of Capabilities class.
* @param capabilitiesId the target capabilities Id.
* @return corresponding Capabilities class instance.
*/
public Capabilities getCapabilities(int capabilities) {
return CAPABILITIES_DATABASE.getOrDefault(capabilities, new Capabilities());
public Capabilities getCapabilities(@Nullable Integer capabilitiesId) {
return CAPABILITIES_DATABASE.getOrDefault(capabilitiesId != null ? capabilitiesId.intValue() : -1,
new Capabilities());
}
/**
* Return a Capabilities class instance that corresponds to the given 'typeId' parameter. If the 'typeId' parameter
* is a valid type in the database, and it has a 'capabilitiesOverride' value, then an instance of the respective
* overridden Capabilities class is returned. Otherwise if the 'capabilitiesId' parameter is for a valid
* capabilities entry in the database, then that respective Capabilities class instance is returned. Otherwise a
* blank Capabilities class instance is returned.
*
* @param typeId the target shade type Id (to check if it has a 'capabilitiesOverride' value).
* @param capabilitiesId the target capabilities value (when type Id does not have a 'capabilitiesOverride').
* @return corresponding Capabilities class instance.
*/
public Capabilities getCapabilities(int typeId, @Nullable Integer capabilitiesId) {
int targetCapabilities = TYPE_DATABASE.getOrDefault(typeId, new Type()).getCapabilitiesOverride();
if (targetCapabilities < 0) {
targetCapabilities = capabilitiesId != null ? capabilitiesId.intValue() : -1;
}
return getCapabilities(targetCapabilities);
}
private static final String REQUEST_DEVELOPERS_TO_UPDATE = " => Please request developers to update the database!";

View File

@@ -115,8 +115,7 @@ public class HDPowerViewDeviceDiscoveryService extends AbstractDiscoveryService
}
String id = Integer.toString(shadeData.id);
ThingUID thingUID = new ThingUID(HDPowerViewBindingConstants.THING_TYPE_SHADE, bridgeUid, id);
Integer caps = shadeData.capabilities;
Capabilities capabilities = db.getCapabilities((caps != null) ? caps.intValue() : -1);
Capabilities capabilities = db.getCapabilities(shadeData.capabilities);
DiscoveryResultBuilder builder = DiscoveryResultBuilder.create(thingUID).withLabel(shadeData.getName())
.withBridge(bridgeUid).withProperty(HDPowerViewShadeConfiguration.ID, id)

View File

@@ -15,6 +15,7 @@ package org.openhab.binding.hdpowerview.internal.handler;
import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*;
import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@@ -75,6 +76,10 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler {
private static final String COMMAND_CALIBRATE = "CALIBRATE";
private static final String COMMAND_IDENTIFY = "IDENTIFY";
private static final String DETECTED_SECONDARY_RAIL = "secondaryRailDetected";
private static final String DETECTED_TILT_ANYWHERE = "tiltAnywhereDetected";
private final Map<String, String> detectedCapabilities = new HashMap<>();
private final Logger logger = LoggerFactory.getLogger(HDPowerViewShadeHandler.class);
private final ShadeCapabilitiesDatabase db = new ShadeCapabilitiesDatabase();
@@ -260,14 +265,13 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler {
// Already cached.
return;
}
Integer value = shade.capabilities;
if (value != null) {
int valueAsInt = value.intValue();
logger.debug("Caching capabilities {} for shade {}", valueAsInt, shade.id);
capabilities = db.getCapabilities(valueAsInt);
} else {
logger.debug("Capabilities not included in shade response");
Capabilities capabilities = db.getCapabilities(shade.type, shade.capabilities);
if (capabilities.getValue() < 0) {
logger.debug("Unable to set capabilities for shade {}", shade.id);
return;
}
logger.debug("Caching capabilities {} for shade {}", capabilities.getValue(), shade.id);
this.capabilities = capabilities;
}
private Capabilities getCapabilitiesOrDefault() {
@@ -304,9 +308,8 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler {
}
// update 'capabilities' property
final Integer temp = shadeData.capabilities;
final int capabilitiesVal = temp != null ? temp.intValue() : -1;
Capabilities capabilities = db.getCapabilities(capabilitiesVal);
Capabilities capabilities = db.getCapabilities(shadeData.capabilities);
final int capabilitiesVal = capabilities.getValue();
propKey = HDPowerViewBindingConstants.PROPERTY_SHADE_CAPABILITIES;
propOldVal = properties.getOrDefault(propKey, "");
propNewVal = capabilities.toString();
@@ -338,7 +341,7 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler {
}
/**
* After a hard refresh, update the Thing's properties based on the contents of the provided ShadeData.
* After a hard refresh, update the Thing's detected capabilities based on the contents of the provided ShadeData.
*
* Checks if the secondary support capabilities in the database of known Shade 'types' and 'capabilities' matches
* that implied by the ShadeData and logs any incompatible values, so that developers can be kept updated about the
@@ -346,35 +349,34 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler {
*
* @param shadeData
*/
private void updateHardProperties(ShadeData shadeData) {
private void updateDetectedCapabilities(ShadeData shadeData) {
final ShadePosition positions = shadeData.positions;
if (positions == null) {
return;
}
Capabilities capabilities = getCapabilitiesOrDefault();
final Map<String, String> properties = getThing().getProperties();
// update 'secondary rail detected' property
String propKey = HDPowerViewBindingConstants.PROPERTY_SECONDARY_RAIL_DETECTED;
String propOldVal = properties.getOrDefault(propKey, "");
boolean propNewBool = positions.secondaryRailDetected();
String propNewVal = String.valueOf(propNewBool);
if (!propNewVal.equals(propOldVal)) {
getThing().setProperty(propKey, propNewVal);
if (propNewBool != capabilities.supportsSecondary()) {
db.logPropertyMismatch(propKey, shadeData.type, capabilities.getValue(), propNewBool);
// update 'secondary rail' detected capability
String capsKey = DETECTED_SECONDARY_RAIL;
String capsOldVal = detectedCapabilities.getOrDefault(capsKey, "");
boolean capsNewBool = positions.secondaryRailDetected();
String capsNewVal = String.valueOf(capsNewBool);
if (!capsNewVal.equals(capsOldVal)) {
detectedCapabilities.put(capsKey, capsNewVal);
if (capsNewBool != capabilities.supportsSecondary()) {
db.logPropertyMismatch(capsKey, shadeData.type, capabilities.getValue(), capsNewBool);
}
}
// update 'tilt anywhere detected' property
propKey = HDPowerViewBindingConstants.PROPERTY_TILT_ANYWHERE_DETECTED;
propOldVal = properties.getOrDefault(propKey, "");
propNewBool = positions.tiltAnywhereDetected();
propNewVal = String.valueOf(propNewBool);
if (!propNewVal.equals(propOldVal)) {
getThing().setProperty(propKey, propNewVal);
if (propNewBool != capabilities.supportsTiltAnywhere()) {
db.logPropertyMismatch(propKey, shadeData.type, capabilities.getValue(), propNewBool);
// update 'tilt anywhere' detected capability
capsKey = DETECTED_TILT_ANYWHERE;
capsOldVal = detectedCapabilities.getOrDefault(capsKey, "");
capsNewBool = positions.tiltAnywhereDetected();
capsNewVal = String.valueOf(capsNewBool);
if (!capsNewVal.equals(capsOldVal)) {
detectedCapabilities.put(capsKey, capsNewVal);
if (capsNewBool != capabilities.supportsTiltAnywhere()) {
db.logPropertyMismatch(capsKey, shadeData.type, capabilities.getValue(), capsNewBool);
}
}
}
@@ -523,7 +525,7 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler {
case POSITION:
shadeData = webTargets.refreshShadePosition(shadeId);
updateShadePositions(shadeData);
updateHardProperties(shadeData);
updateDetectedCapabilities(shadeData);
break;
case SURVEY:
Survey survey = webTargets.getShadeSurvey(shadeId);