[hdpowerview] Channels are visible depending on shade capabilities ()

* [hdpowerview] create channels dynamically
* [hdpowerview] revert from channel creation to channel removal
* [hdpowerview] add category

Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
This commit is contained in:
Andrew Fiddian-Green 2022-08-23 21:03:43 +01:00 committed by GitHub
parent bc9cf8e07a
commit 0a41c3f2f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 66 additions and 19 deletions
bundles/org.openhab.binding.hdpowerview
README.md
src/main
java/org/openhab/binding/hdpowerview/internal/handler
resources/OH-INF/thing

@ -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`)

@ -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<String, String> detectedCapabilities = new HashMap<>();
private static final ShadeCapabilitiesDatabase DB = new ShadeCapabilitiesDatabase();
private final Map<String, String> 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<Channel> 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<Channel> 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());
}
}
}

@ -8,6 +8,7 @@
<item-type>Rollershutter</item-type>
<label>Position</label>
<description>The vertical position of the shade</description>
<category>blinds</category>
</channel-type>
<channel-type id="shade-vane">