Skip to content

Commit

Permalink
[hdpowerview] Corrections to shade database and capabilities (#12902)
Browse files Browse the repository at this point in the history
* [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>
  • Loading branch information
andrewfg authored Jun 10, 2022
1 parent 798d59b commit c806c76
Show file tree
Hide file tree
Showing 6 changed files with 254 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ private State getPosition1(Capabilities shadeCapabilities, CoordinateSystem posK
}
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;
Expand All @@ -182,6 +182,10 @@ private State getPosition1(Capabilities shadeCapabilities, CoordinateSystem posK
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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
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;

Expand All @@ -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()));

Expand All @@ -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"),
Expand All @@ -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()));
Expand Down Expand Up @@ -112,6 +116,7 @@ public String toString() {
*/
public static class Type extends Base {
private int capabilities = -1;
private int capabilitiesOverride = -1;

protected Type() {
}
Expand All @@ -130,6 +135,11 @@ protected Type capabilities(int capabilities) {
return this;
}

protected Type capabilitiesOverride(int capabilitiesOverride) {
this.capabilitiesOverride = capabilitiesOverride;
return this;
}

/**
* Get shade types's 'capabilities'.
*
Expand All @@ -138,6 +148,15 @@ protected Type capabilities(int capabilities) {
public int getCapabilities() {
return capabilities;
}

/**
* Get shade's type specific 'capabilities'.
*
* @return 'typeCapabilities'.
*/
public int getCapabilitiesOverride() {
return capabilitiesOverride;
}
}

/**
Expand Down Expand Up @@ -301,13 +320,35 @@ public Type getType(int type) {
}

/**
* 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!";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,7 @@ private void discoverShades(HDPowerViewWebTargets webTargets)
}
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
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;
Expand Down Expand Up @@ -75,6 +76,10 @@ private enum RefreshKind {
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();

Expand Down Expand Up @@ -260,14 +265,13 @@ private void updateCapabilities(ShadeData shade) {
// 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() {
Expand Down Expand Up @@ -304,9 +308,8 @@ private void updateSoftProperties(ShadeData shadeData) {
}

// 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();
Expand Down Expand Up @@ -338,43 +341,42 @@ private void updateFirmwareProperties(ShadeData shadeData) {
}

/**
* 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
* potential need to add support for that type resp. capabilities.
*
* @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);
}
}
}
Expand Down Expand Up @@ -523,7 +525,7 @@ private void doRefreshShade(RefreshKind kind) {
case POSITION:
shadeData = webTargets.refreshShadePosition(shadeId);
updateShadePositions(shadeData);
updateHardProperties(shadeData);
updateDetectedCapabilities(shadeData);
break;
case SURVEY:
Survey survey = webTargets.getShadeSurvey(shadeId);
Expand Down
Loading

0 comments on commit c806c76

Please sign in to comment.