]> git.basschouten.com Git - openhab-addons.git/commitdiff
[hdpowerview] Add Hub configuration option hardRefreshBatteryLevel (#11260)
authorjlaur <jacob-github@vindvejr.dk>
Sat, 18 Sep 2021 11:55:04 +0000 (13:55 +0200)
committerGitHub <noreply@github.com>
Sat, 18 Sep 2021 11:55:04 +0000 (13:55 +0200)
* 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>
bundles/org.openhab.binding.hdpowerview/README.md
bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java
bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/config/HDPowerViewHubConfiguration.java
bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java
bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java
bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/thing-types.xml
bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java

index 7f043983b74f6a36a146576a45e98d4a1a84a366..d387c46160d5b0f2888302b680e8e458b27d8c2e 100644 (file)
@@ -44,6 +44,7 @@ If in the future, you add additional shades or scenes to your system, the bindin
 | 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
 
@@ -135,6 +136,10 @@ The hub periodically does a _**"hard refresh"**_ in order to overcome this issue
 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:
 
 ```
index 4d40d9927bee22f3d15d4b7fd3600bea817754c3..eaa17cfbfe3acbd045c60f9c163fa4bba8aa26c8 100644 (file)
@@ -234,8 +234,8 @@ public class HDPowerViewWebTargets {
 
     /**
      * 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
@@ -243,13 +243,31 @@ public class HDPowerViewWebTargets {
      * @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
      *
index 0827a682e31e40855df9dd9f6d1e4e9d947d5448..d617bb06cfbeb3917aae692f5f9fb36a904ccf94 100644 (file)
@@ -67,11 +67,13 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler {
     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);
@@ -84,7 +86,7 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler {
     @Override
     public void handleCommand(ChannelUID channelUID, Command command) {
         if (RefreshType.REFRESH.equals(command)) {
-            requestRefreshShades();
+            requestRefreshShadePositions();
             return;
         }
 
@@ -119,7 +121,8 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler {
 
         webTargets = new HDPowerViewWebTargets(httpClient, host);
         refreshInterval = config.refresh;
-        hardRefreshInterval = config.hardRefresh;
+        hardRefreshPositionInterval = config.hardRefresh;
+        hardRefreshBatteryLevelInterval = config.hardRefreshBatteryLevel;
         schedulePoll();
     }
 
@@ -147,14 +150,24 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler {
         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);
         }
     }
 
@@ -165,11 +178,17 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler {
         }
         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() {
@@ -304,13 +323,27 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler {
         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);
index 70bf65bdd75ed05d52a67d9d9553b12f453374b4..42514597bafbf3694ae4acf711f265f045b32344 100644 (file)
@@ -19,6 +19,8 @@ import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*;
 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;
@@ -57,10 +59,16 @@ import org.slf4j.LoggerFactory;
 @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);
@@ -85,7 +93,7 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler {
     @Override
     public void handleCommand(ChannelUID channelUID, Command command) {
         if (RefreshType.REFRESH.equals(command)) {
-            requestRefreshShade();
+            requestRefreshShadePosition();
             return;
         }
 
@@ -138,7 +146,9 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler {
             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);
@@ -171,6 +181,8 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler {
                 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);
@@ -243,7 +255,7 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler {
             }
             int shadeId = getShadeId();
             webTargets.stopShade(shadeId);
-            requestRefreshShade();
+            requestRefreshShadePosition();
         } catch (HubProcessingException | NumberFormatException e) {
             logger.warn("Unexpected error: {}", e.getMessage());
             return;
@@ -254,15 +266,36 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler {
     }
 
     /**
-     * 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) {
@@ -273,7 +306,17 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler {
                 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) {
@@ -287,6 +330,5 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler {
         } catch (HubMaintenanceException e) {
             // exceptions are logged in HDPowerViewWebTargets
         }
-        refreshFuture = null;
     }
 }
index 9794e8368aa5fbb34239744a81276105a29cf30d..c852a49b752995c38c2856b3da08668d4f593ba4 100644 (file)
                                <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>
 
index 8fd7e844c565dc026be8c51cc718395788bd96cd..46240065fbaf712c16fcba769af2e49db07eb936 100644 (file)
@@ -218,7 +218,7 @@ public class HDPowerViewJUnitTests {
             Shade shade = null;
             try {
                 assertNotEquals(0, shadeId);
-                shade = webTargets.refreshShade(shadeId);
+                shade = webTargets.refreshShadePosition(shadeId);
                 assertNotNull(shade);
             } catch (HubProcessingException | HubMaintenanceException e) {
                 fail(e.getMessage());