* Add Hub configuration option hardRefreshBatteryLevel for refreshing battery status more frequently.
* Explicitly update battery channels to Undefined when data is missing or invalid.
Fixes #11259
Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
| host | The host name or IP address of the hub on your network. |
| refresh | The number of milli-seconds between fetches of the PowerView hub's shade state (default 60'000 one minute). |
| hardRefresh | The number of minutes between hard refreshes of the PowerView hub's shade state (default 180 three hours). See [Refreshing the PowerView Hub Cache](#Refreshing-the-PowerView-Hub-Cache). |
+| hardRefreshBatteryLevel | The number of hours between hard refreshes of battery levels from the PowerView Hub (or 0 to disable, defaulting to weekly). See [Refreshing the PowerView Hub Cache](#Refreshing-the-PowerView-Hub-Cache). |
### Thing Configuration for PowerView Shades
The time interval between hard refreshes is set in the `hardRefresh` configuration parameter.
To disable periodic hard refreshes, set `hardRefresh` to zero.
+Similarly, the battery level is transient and is only updated automatically by the hub once a week.
+To change this interval, set `hardRefreshBatteryLevel` to number of hours between refreshes.
+To use default hub behavior (weekly updates), set `hardRefreshBatteryLevel` to zero.
+
Note: You can also force the hub to refresh itself by sending a `REFRESH` command in a rule to an item that is connected to a channel in the hub as follows:
```
/**
* Instructs the hub to do a hard refresh (discovery on the hubs RF network) on
- * a specific shade; fetches a JSON package that describes that shade, and wraps
- * it in a Shade class instance
+ * a specific shade's position; fetches a JSON package that describes that shade,
+ * and wraps it in a Shade class instance
*
* @param shadeId id of the shade to be refreshed
* @return Shade class instance
* @throws HubProcessingException if there is any processing error
* @throws HubMaintenanceException if the hub is down for maintenance
*/
- public @Nullable Shade refreshShade(int shadeId)
+ public @Nullable Shade refreshShadePosition(int shadeId)
throws JsonParseException, HubProcessingException, HubMaintenanceException {
String json = invoke(HttpMethod.GET, shades + Integer.toString(shadeId),
Query.of("refresh", Boolean.toString(true)), null);
return gson.fromJson(json, Shade.class);
}
+ /**
+ * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on
+ * a specific shade's battery level; fetches a JSON package that describes that shade,
+ * and wraps it in a Shade class instance
+ *
+ * @param shadeId id of the shade to be refreshed
+ * @return Shade class instance
+ * @throws JsonParseException if there is a JSON parsing error
+ * @throws HubProcessingException if there is any processing error
+ * @throws HubMaintenanceException if the hub is down for maintenance
+ */
+ public @Nullable Shade refreshShadeBatteryLevel(int shadeId)
+ throws JsonParseException, HubProcessingException, HubMaintenanceException {
+ String json = invoke(HttpMethod.GET, shades + Integer.toString(shadeId),
+ Query.of("updateBatteryLevel", Boolean.toString(true)), null);
+ return gson.fromJson(json, Shade.class);
+ }
+
/**
* Tells the hub to stop movement of a specific shade
*
public long refresh;
public long hardRefresh;
+ public long hardRefreshBatteryLevel;
}
private final HttpClient httpClient;
private long refreshInterval;
- private long hardRefreshInterval;
+ private long hardRefreshPositionInterval;
+ private long hardRefreshBatteryLevelInterval;
private @Nullable HDPowerViewWebTargets webTargets;
private @Nullable ScheduledFuture<?> pollFuture;
- private @Nullable ScheduledFuture<?> hardRefreshFuture;
+ private @Nullable ScheduledFuture<?> hardRefreshPositionFuture;
+ private @Nullable ScheduledFuture<?> hardRefreshBatteryLevelFuture;
private final ChannelTypeUID sceneChannelTypeUID = new ChannelTypeUID(HDPowerViewBindingConstants.BINDING_ID,
HDPowerViewBindingConstants.CHANNELTYPE_SCENE_ACTIVATE);
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (RefreshType.REFRESH.equals(command)) {
- requestRefreshShades();
+ requestRefreshShadePositions();
return;
}
webTargets = new HDPowerViewWebTargets(httpClient, host);
refreshInterval = config.refresh;
- hardRefreshInterval = config.hardRefresh;
+ hardRefreshPositionInterval = config.hardRefresh;
+ hardRefreshBatteryLevelInterval = config.hardRefreshBatteryLevel;
schedulePoll();
}
logger.debug("Scheduling poll for 5000ms out, then every {}ms", refreshInterval);
this.pollFuture = scheduler.scheduleWithFixedDelay(this::poll, 5000, refreshInterval, TimeUnit.MILLISECONDS);
- future = this.hardRefreshFuture;
+ future = this.hardRefreshPositionFuture;
if (future != null) {
future.cancel(false);
}
- if (hardRefreshInterval > 0) {
- logger.debug("Scheduling hard refresh every {}minutes", hardRefreshInterval);
- this.hardRefreshFuture = scheduler.scheduleWithFixedDelay(this::requestRefreshShades, 1,
- hardRefreshInterval, TimeUnit.MINUTES);
+ if (hardRefreshPositionInterval > 0) {
+ logger.debug("Scheduling hard position refresh every {} minutes", hardRefreshPositionInterval);
+ this.hardRefreshPositionFuture = scheduler.scheduleWithFixedDelay(this::requestRefreshShadePositions, 1,
+ hardRefreshPositionInterval, TimeUnit.MINUTES);
+ }
+
+ future = this.hardRefreshBatteryLevelFuture;
+ if (future != null) {
+ future.cancel(false);
+ }
+ if (hardRefreshBatteryLevelInterval > 0) {
+ logger.debug("Scheduling hard battery level refresh every {} hours", hardRefreshBatteryLevelInterval);
+ this.hardRefreshBatteryLevelFuture = scheduler.scheduleWithFixedDelay(
+ this::requestRefreshShadeBatteryLevels, 1, hardRefreshBatteryLevelInterval, TimeUnit.HOURS);
}
}
}
this.pollFuture = null;
- future = this.hardRefreshFuture;
+ future = this.hardRefreshPositionFuture;
if (future != null) {
future.cancel(true);
}
- this.hardRefreshFuture = null;
+ this.hardRefreshPositionFuture = null;
+
+ future = this.hardRefreshBatteryLevelFuture;
+ if (future != null) {
+ future.cancel(true);
+ }
+ this.hardRefreshBatteryLevelFuture = null;
}
private synchronized void poll() {
return ret;
}
- private void requestRefreshShades() {
+ private void requestRefreshShadePositions() {
+ Map<Thing, String> thingIdMap = getThingIdMap();
+ for (Entry<Thing, String> item : thingIdMap.entrySet()) {
+ Thing thing = item.getKey();
+ ThingHandler handler = thing.getHandler();
+ if (handler instanceof HDPowerViewShadeHandler) {
+ ((HDPowerViewShadeHandler) handler).requestRefreshShadePosition();
+ } else {
+ String shadeId = item.getValue();
+ logger.debug("Shade '{}' handler not initialized", shadeId);
+ }
+ }
+ }
+
+ private void requestRefreshShadeBatteryLevels() {
Map<Thing, String> thingIdMap = getThingIdMap();
for (Entry<Thing, String> item : thingIdMap.entrySet()) {
Thing thing = item.getKey();
ThingHandler handler = thing.getHandler();
if (handler instanceof HDPowerViewShadeHandler) {
- ((HDPowerViewShadeHandler) handler).requestRefreshShade();
+ ((HDPowerViewShadeHandler) handler).requestRefreshShadeBatteryLevel();
} else {
String shadeId = item.getValue();
logger.debug("Shade '{}' handler not initialized", shadeId);
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
+import javax.ws.rs.NotSupportedException;
+
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets;
@NonNullByDefault
public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler {
+ private enum RefreshKind {
+ POSITION,
+ BATTERY_LEVEL
+ }
+
private final Logger logger = LoggerFactory.getLogger(HDPowerViewShadeHandler.class);
private static final int REFRESH_DELAY_SEC = 10;
- private @Nullable ScheduledFuture<?> refreshFuture = null;
+ private @Nullable ScheduledFuture<?> refreshPositionFuture = null;
+ private @Nullable ScheduledFuture<?> refreshBatteryLevelFuture = null;
public HDPowerViewShadeHandler(Thing thing) {
super(thing);
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (RefreshType.REFRESH.equals(command)) {
- requestRefreshShade();
+ requestRefreshShadePosition();
return;
}
updateStatus(ThingStatus.ONLINE);
updateBindingStates(shadeData.positions);
updateBatteryLevel(shadeData.batteryStatus);
- updateState(CHANNEL_SHADE_BATTERY_VOLTAGE, new QuantityType<>(shadeData.batteryStrength / 10, Units.VOLT));
+ updateState(CHANNEL_SHADE_BATTERY_VOLTAGE,
+ shadeData.batteryStrength > 0 ? new QuantityType<>(shadeData.batteryStrength / 10, Units.VOLT)
+ : UnDefType.UNDEF);
updateState(CHANNEL_SHADE_SIGNAL_STRENGTH, new DecimalType(shadeData.signalStrength));
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
mappedValue = 100;
break;
default: // No status available (0) or invalid
+ updateState(CHANNEL_SHADE_LOW_BATTERY, UnDefType.UNDEF);
+ updateState(CHANNEL_SHADE_BATTERY_LEVEL, UnDefType.UNDEF);
return;
}
updateState(CHANNEL_SHADE_LOW_BATTERY, batteryStatus == 1 ? OnOffType.ON : OnOffType.OFF);
}
int shadeId = getShadeId();
webTargets.stopShade(shadeId);
- requestRefreshShade();
+ requestRefreshShadePosition();
} catch (HubProcessingException | NumberFormatException e) {
logger.warn("Unexpected error: {}", e.getMessage());
return;
}
/**
- * Request that the shade shall undergo a 'hard' refresh
+ * Request that the shade shall undergo a 'hard' refresh for querying its current position
+ */
+ protected synchronized void requestRefreshShadePosition() {
+ if (refreshPositionFuture == null) {
+ refreshPositionFuture = scheduler.schedule(this::doRefreshShadePosition, REFRESH_DELAY_SEC,
+ TimeUnit.SECONDS);
+ }
+ }
+
+ /**
+ * Request that the shade shall undergo a 'hard' refresh for querying its battery level state
*/
- protected synchronized void requestRefreshShade() {
- if (refreshFuture == null) {
- refreshFuture = scheduler.schedule(this::doRefreshShade, REFRESH_DELAY_SEC, TimeUnit.SECONDS);
+ protected synchronized void requestRefreshShadeBatteryLevel() {
+ if (refreshBatteryLevelFuture == null) {
+ refreshBatteryLevelFuture = scheduler.schedule(this::doRefreshShadeBatteryLevel, REFRESH_DELAY_SEC,
+ TimeUnit.SECONDS);
}
}
- private void doRefreshShade() {
+ private void doRefreshShadePosition() {
+ this.doRefreshShade(RefreshKind.POSITION);
+ refreshPositionFuture = null;
+ }
+
+ private void doRefreshShadeBatteryLevel() {
+ this.doRefreshShade(RefreshKind.BATTERY_LEVEL);
+ refreshBatteryLevelFuture = null;
+ }
+
+ private void doRefreshShade(RefreshKind kind) {
try {
HDPowerViewHubHandler bridge;
if ((bridge = getBridgeHandler()) == null) {
throw new HubProcessingException("Web targets not initialized");
}
int shadeId = getShadeId();
- Shade shade = webTargets.refreshShade(shadeId);
+ Shade shade;
+ switch (kind) {
+ case POSITION:
+ shade = webTargets.refreshShadePosition(shadeId);
+ break;
+ case BATTERY_LEVEL:
+ shade = webTargets.refreshShadeBatteryLevel(shadeId);
+ break;
+ default:
+ throw new NotSupportedException("Unsupported refresh kind " + kind.toString());
+ }
if (shade != null) {
ShadeData shadeData = shade.shade;
if (shadeData != null) {
} catch (HubMaintenanceException e) {
// exceptions are logged in HDPowerViewWebTargets
}
- refreshFuture = null;
}
}
<default>60000</default>
</parameter>
<parameter name="hardRefresh" type="integer" required="false">
- <label>Hard Refresh Interval</label>
- <description>The number of minutes between hard refreshes of the PowerView Hub (or 0 to disable)</description>
+ <label>Hard Position Refresh Interval</label>
+ <description>The number of minutes between hard refreshes of positions from the PowerView Hub (or 0 to disable)</description>
<default>180</default>
</parameter>
+ <parameter name="hardRefreshBatteryLevel" type="integer" required="false">
+ <label>Hard Battery Level Refresh Interval</label>
+ <description>The number of hours between hard refreshes of battery levels from the PowerView Hub (or 0 to disable,
+ default is weekly)</description>
+ <advanced>true</advanced>
+ <default>0</default>
+ </parameter>
</config-description>
</bridge-type>
Shade shade = null;
try {
assertNotEquals(0, shadeId);
- shade = webTargets.refreshShade(shadeId);
+ shade = webTargets.refreshShadePosition(shadeId);
assertNotNull(shade);
} catch (HubProcessingException | HubMaintenanceException e) {
fail(e.getMessage());