diff --git a/bundles/org.openhab.binding.hdpowerview/README.md b/bundles/org.openhab.binding.hdpowerview/README.md index 146813ec5..e4ed9a98a 100644 --- a/bundles/org.openhab.binding.hdpowerview/README.md +++ b/bundles/org.openhab.binding.hdpowerview/README.md @@ -91,9 +91,9 @@ All of these channels appear in the binding, but only those which have a physica | Channel | Item Type | Description | |----------------|--------------------------|-------------| -| position | Rollershutter | The vertical position of the shade's rail -- see [next chapter](#Roller-Shutter-Up/Down-Position-vs.-Open/Close-State). Up/Down commands will move the rail completely up or completely down. Percentage commands will move the rail to an intermediate position. Stop commands will halt any current movement of the rail. | -| secondary | Rollershutter | The vertical position of the secondary rail (if any). Its function is similar to the `position` channel above -- but see [next chapter](#Roller-Shutter-Up/Down-Position-vs.-Open/Close-State). | -| vane | Dimmer | The degree of opening of the slats or vanes. Setting this to a non-zero value will first move the shade `position` fully down, since the slats or vanes can only have a defined state if the shade is in its down position -- see [Interdependency between Channel positions](#Interdependency-between-Channel-positions). | +| position | Rollershutter | The vertical position of the shade's rail (if any). -- See [next chapter](#Roller-Shutter-Up/Down-Position-vs.-Open/Close-State). Up/Down commands will move the rail completely up or completely down. Percentage commands will move the rail to an intermediate position. Stop commands will halt any current movement of the rail. | +| secondary | Rollershutter | The vertical position of the secondary rail (if any). Its function is similar to the `position` channel above. -- But see [next chapter](#Roller-Shutter-Up/Down-Position-vs.-Open/Close-State). | +| vane | Dimmer | The degree of opening of the slats or vanes (if any). On some shade types, setting this to a non-zero value might first move the shade `position` fully down, since the slats or vanes can only have a defined state if the shade is in its down position. See [Interdependency between Channel positions](#Interdependency-between-Channel-positions). | | command | String | Send a command to the shade. Valid values are: `CALIBRATE`, `IDENTIFY` | | lowBattery | Switch | Indicates ON when the battery level of the shade is low, as determined by the hub's internal rules. | | batteryLevel | Number | Battery level (10% = low, 50% = medium, 100% = high) @@ -102,7 +102,9 @@ All of these channels appear in the binding, but only those which have a physica | hubRssi | Number:Power | Received Signal Strength Indicator for Hub | | repeaterRssi | Number:Power | Received Signal Strength Indicator for Repeater | -Please note that RSSI values will only be updated upon manual request by a `REFRESH` command (e.g. in a rule). +Notes: +- The channels `position`, `secondary` and `vane` only exist if the shade physically supports such channels. +- The RSSI values will only be updated upon manual request by a `REFRESH` command (e.g. in a rule). ### Channels for Repeaters (Thing type `repeater`) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java index 391cfaf44..fb0d76e64 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java @@ -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.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -50,6 +51,7 @@ import org.openhab.core.library.types.StringType; import org.openhab.core.library.types.UpDownType; import org.openhab.core.library.unit.Units; import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingStatus; @@ -80,10 +82,10 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler { private static final String DETECTED_SECONDARY_RAIL = "secondaryRailDetected"; private static final String DETECTED_TILT_ANYWHERE = "tiltAnywhereDetected"; - private final Map detectedCapabilities = new HashMap<>(); + private static final ShadeCapabilitiesDatabase DB = new ShadeCapabilitiesDatabase(); + private final Map detectedCapabilities = new HashMap<>(); private final Logger logger = LoggerFactory.getLogger(HDPowerViewShadeHandler.class); - private final ShadeCapabilitiesDatabase db = new ShadeCapabilitiesDatabase(); private @Nullable ScheduledFuture refreshPositionFuture = null; private @Nullable ScheduledFuture refreshSignalFuture = null; @@ -107,7 +109,6 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler { "@text/offline.conf-error.invalid-bridge-handler"); return; } - updateStatus(ThingStatus.UNKNOWN); } @@ -265,13 +266,15 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler { // Already cached. return; } - Capabilities capabilities = db.getCapabilities(shade.type, shade.capabilities); + 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; + + updateDynamicChannels(capabilities); } private Capabilities getCapabilitiesOrDefault() { @@ -298,17 +301,17 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler { final int type = shadeData.type; String propKey = HDPowerViewBindingConstants.PROPERTY_SHADE_TYPE; String propOldVal = properties.getOrDefault(propKey, ""); - String propNewVal = db.getType(type).toString(); + String propNewVal = DB.getType(type).toString(); if (!propNewVal.equals(propOldVal)) { propChanged = true; getThing().setProperty(propKey, propNewVal); - if ((type > 0) && !db.isTypeInDatabase(type)) { - db.logTypeNotInDatabase(type); + if ((type > 0) && !DB.isTypeInDatabase(type)) { + DB.logTypeNotInDatabase(type); } } // update 'capabilities' property - Capabilities capabilities = db.getCapabilities(shadeData.capabilities); + Capabilities capabilities = DB.getCapabilities(shadeData.capabilities); final int capabilitiesVal = capabilities.getValue(); propKey = HDPowerViewBindingConstants.PROPERTY_SHADE_CAPABILITIES; propOldVal = properties.getOrDefault(propKey, ""); @@ -316,14 +319,14 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler { if (!propNewVal.equals(propOldVal)) { propChanged = true; getThing().setProperty(propKey, propNewVal); - if ((capabilitiesVal >= 0) && !db.isCapabilitiesInDatabase(capabilitiesVal)) { - db.logCapabilitiesNotInDatabase(type, capabilitiesVal); + if ((capabilitiesVal >= 0) && !DB.isCapabilitiesInDatabase(capabilitiesVal)) { + DB.logCapabilitiesNotInDatabase(type, capabilitiesVal); } } - if (propChanged && db.isCapabilitiesInDatabase(capabilitiesVal) && db.isTypeInDatabase(type) - && (capabilitiesVal != db.getType(type).getCapabilities()) && (shadeData.capabilities != null)) { - db.logCapabilitiesMismatch(type, capabilitiesVal); + if (propChanged && DB.isCapabilitiesInDatabase(capabilitiesVal) && DB.isTypeInDatabase(type) + && (capabilitiesVal != DB.getType(type).getCapabilities()) && (shadeData.capabilities != null)) { + DB.logCapabilitiesMismatch(type, capabilitiesVal); } } @@ -364,7 +367,7 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler { if (!capsNewVal.equals(capsOldVal)) { detectedCapabilities.put(capsKey, capsNewVal); if (capsNewBool != capabilities.supportsSecondary()) { - db.logPropertyMismatch(capsKey, shadeData.type, capabilities.getValue(), capsNewBool); + DB.logPropertyMismatch(capsKey, shadeData.type, capabilities.getValue(), capsNewBool); } } @@ -376,7 +379,7 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler { if (!capsNewVal.equals(capsOldVal)) { detectedCapabilities.put(capsKey, capsNewVal); if (capsNewBool != capabilities.supportsTiltAnywhere()) { - db.logPropertyMismatch(capsKey, shadeData.type, capabilities.getValue(), capsNewBool); + DB.logPropertyMismatch(capsKey, shadeData.type, capabilities.getValue(), capsNewBool); } } } @@ -596,4 +599,45 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler { } } } + + /** + * If the given channel exists in the thing, but is NOT required in the thing, then add it to a list of channels to + * be removed. Or if the channel does NOT exist in the thing, but is required in the thing, then log a warning. + * + * @param removeList the list of channels to be removed from the thing. + * @param channelId the id of the channel to be (eventually) removed. + * @param channelRequired true if the thing requires this channel. + */ + private void removeListProcessChannel(List removeList, String channelId, boolean channelRequired) { + Channel channel = thing.getChannel(channelId); + if (!channelRequired && channel != null) { + removeList.add(channel); + } else if (channelRequired && channel == null) { + logger.warn("Shade {} does not have a '{}' channel => please reinitialize the thing", shadeId, channelId); + } + } + + /** + * Remove previously statically created channels if the shade does not support them. + */ + private void updateDynamicChannels(Capabilities capabilities) { + List removeList = new ArrayList<>(); + + removeListProcessChannel(removeList, CHANNEL_SHADE_POSITION, capabilities.supportsPrimary()); + + removeListProcessChannel(removeList, CHANNEL_SHADE_SECONDARY_POSITION, + capabilities.supportsSecondary() || capabilities.supportsSecondaryOverlapped()); + + removeListProcessChannel(removeList, CHANNEL_SHADE_VANE, + capabilities.supportsTiltAnywhere() || capabilities.supportsTiltOnClosed()); + + if (!removeList.isEmpty()) { + if (logger.isDebugEnabled()) { + StringJoiner joiner = new StringJoiner(", "); + removeList.forEach(c -> joiner.add(c.getUID().getId())); + logger.debug("Removing unsupported channels for {}: {}", shadeId, joiner.toString()); + } + updateThing(editThing().withoutChannels(removeList).build()); + } + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/channels.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/channels.xml index 8ec5e9c5a..914bedbb3 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/channels.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/channels.xml @@ -8,6 +8,7 @@ Rollershutter The vertical position of the shade + blinds