diff --git a/bundles/org.openhab.binding.insteon/src/main/java/org/openhab/binding/insteon/internal/device/BaseDevice.java b/bundles/org.openhab.binding.insteon/src/main/java/org/openhab/binding/insteon/internal/device/BaseDevice.java index 6f68c0ffab7..50efa7ef00d 100644 --- a/bundles/org.openhab.binding.insteon/src/main/java/org/openhab/binding/insteon/internal/device/BaseDevice.java +++ b/bundles/org.openhab.binding.insteon/src/main/java/org/openhab/binding/insteon/internal/device/BaseDevice.java @@ -43,6 +43,7 @@ public abstract class BaseDevice<@NonNull T extends DeviceAddress, @NonNull S ex implements Device { private static final int DIRECT_ACK_TIMEOUT = 6000; // in milliseconds private static final int REQUEST_QUEUE_TIMEOUT = 30000; // in milliseconds + private static final int FAILED_REQUEST_THRESHOLD = 5; protected static enum DeviceStatus { INITIALIZED, @@ -63,6 +64,7 @@ public abstract class BaseDevice<@NonNull T extends DeviceAddress, @NonNull S ex private Map requestQueueHash = new HashMap<>(); private @Nullable DeviceFeature featureQueried; private long pollInterval = -1L; // in milliseconds + private volatile int failedRequestCount = 0; private volatile long lastRequestQueued = 0L; private volatile long lastRequestSent = 0L; @@ -145,6 +147,10 @@ public abstract class BaseDevice<@NonNull T extends DeviceAddress, @NonNull S ex } } + public boolean isResponding() { + return failedRequestCount < FAILED_REQUEST_THRESHOLD; + } + public void setModem(@Nullable InsteonModem modem) { this.modem = modem; } @@ -403,10 +409,8 @@ public abstract class BaseDevice<@NonNull T extends DeviceAddress, @NonNull S ex public void handleMessage(Msg msg) { getFeatures().stream().filter(feature -> feature.handleMessage(msg)).findFirst().ifPresent(feature -> { logger.trace("handled reply of direct for {}", feature.getName()); - // mark feature queried as processed and answered - setFeatureQueried(null); - feature.setQueryMessage(null); - feature.setQueryStatus(QueryStatus.QUERY_ANSWERED); + // notify feature queried was answered + featureQueriedAnswered(feature); }); } @@ -527,9 +531,8 @@ public abstract class BaseDevice<@NonNull T extends DeviceAddress, @NonNull S ex return now + 1000L; // retry in 1000 ms } logger.debug("gave up waiting for {} query to be sent to {}", feature.getName(), address); - // reset feature queried as never queried - feature.setQueryMessage(null); - feature.setQueryStatus(QueryStatus.NEVER_QUERIED); + // notify feature queried failed + featureQueriedFailed(feature); break; case QUERY_SENT: case QUERY_ACKED: @@ -541,20 +544,61 @@ public abstract class BaseDevice<@NonNull T extends DeviceAddress, @NonNull S ex return now + 500L; // retry in 500 ms } logger.debug("gave up waiting for {} query reply from {}", feature.getName(), address); - // reset feature queried as never queried - feature.setQueryMessage(null); - feature.setQueryStatus(QueryStatus.NEVER_QUERIED); + // notify feature queried failed + featureQueriedFailed(feature); break; default: logger.debug("unexpected feature {} query status {} for {}", feature.getName(), queryStatus, address); + // reset feature queried + setFeatureQueried(null); } - // reset feature queried otheriwse - setFeatureQueried(null); } return 0L; } + /** + * Notifies that the feature queried was answered + * + * @param feature the feature queried + */ + protected void featureQueriedAnswered(DeviceFeature feature) { + // store current failed request count + int prevCount = failedRequestCount; + // reset failed request count + failedRequestCount = 0; + // mark feature queried as processed and answered + setFeatureQueried(null); + feature.setQueryMessage(null); + feature.setQueryStatus(QueryStatus.QUERY_ANSWERED); + // notify status changed if failed request count was above threshold + if (prevCount >= FAILED_REQUEST_THRESHOLD) { + statusChanged(); + } + } + + /** + * Notifies that the feature queried failed + * + * @param feature the feature queried + */ + protected void featureQueriedFailed(DeviceFeature feature) { + // increase failed request count + failedRequestCount++; + // mark feature queried as processed and never queried + setFeatureQueried(null); + feature.setQueryMessage(null); + feature.setQueryStatus(QueryStatus.NEVER_QUERIED); + // poll feature again if device is responding + if (isResponding()) { + feature.doPoll(0L); + } + // notify status changed if failed request count at threshold + if (failedRequestCount == FAILED_REQUEST_THRESHOLD) { + statusChanged(); + } + } + /** * Notifies that a message request was replied for this device * @@ -564,10 +608,17 @@ public abstract class BaseDevice<@NonNull T extends DeviceAddress, @NonNull S ex public void requestReplied(Msg msg) { DeviceFeature feature = getFeatureQueried(); if (feature != null && feature.isMyReply(msg)) { - // mark feature queried as processed and answered - setFeatureQueried(null); - feature.setQueryMessage(null); - feature.setQueryStatus(QueryStatus.QUERY_ANSWERED); + if (msg.isReplyNack()) { + logger.debug("got a reply nack msg: {}", msg); + // notify feature queried failed + featureQueriedFailed(feature); + } else if (!msg.isInsteon()) { + // notify feature queried was answered + featureQueriedAnswered(feature); + } else { + // mark feature queried as acked + feature.setQueryStatus(QueryStatus.QUERY_ACKED); + } } } @@ -588,6 +639,18 @@ public abstract class BaseDevice<@NonNull T extends DeviceAddress, @NonNull S ex } } + /** + * Notifies that the status has changed for this device + */ + public void statusChanged() { + logger.trace("status for {} has changed", address); + @Nullable + S handler = getHandler(); + if (handler != null) { + handler.updateStatus(); + } + } + /** * Refreshes this device */ diff --git a/bundles/org.openhab.binding.insteon/src/main/java/org/openhab/binding/insteon/internal/device/InsteonDevice.java b/bundles/org.openhab.binding.insteon/src/main/java/org/openhab/binding/insteon/internal/device/InsteonDevice.java index 5c2185f8e31..cdfc7a76076 100644 --- a/bundles/org.openhab.binding.insteon/src/main/java/org/openhab/binding/insteon/internal/device/InsteonDevice.java +++ b/bundles/org.openhab.binding.insteon/src/main/java/org/openhab/binding/insteon/internal/device/InsteonDevice.java @@ -66,7 +66,6 @@ import org.openhab.core.types.UnDefType; public class InsteonDevice extends BaseDevice { private static final int BCAST_STATE_TIMEOUT = 2000; // in milliseconds private static final int DEFAULT_HEARTBEAT_TIMEOUT = 1440; // in minutes - private static final int FAILED_MSG_COUNT_THRESHOLD = 5; private InsteonEngine engine = InsteonEngine.UNKNOWN; private LinkDB linkDB; @@ -76,7 +75,6 @@ public class InsteonDevice extends BaseDevice deferredQueueHash = new HashMap<>(); private Map lastBroadcastReceived = new HashMap<>(); private Map groupState = new HashMap<>(); - private volatile int failedMsgCount = 0; private volatile long lastMsgReceived = 0L; public InsteonDevice() { @@ -145,10 +143,6 @@ public class InsteonDevice extends BaseDevice feature.isMyDirectAckOrNack(msg)).findFirst() .ifPresent(feature -> { logger.debug("got a failure report reply of direct for {}", feature.getName()); - // increase failed message counter - failedMsgCount++; - // mark feature queried as processed and never queried - setFeatureQueried(null); - feature.setQueryMessage(null); - feature.setQueryStatus(QueryStatus.NEVER_QUERIED); - // poll feature again if device is responding - if (isResponding()) { - feature.doPoll(0L); - } + // notify feature queried failed + featureQueriedFailed(feature); }); } else { // update non-status features getFeatures().stream().filter(feature -> !feature.isStatusFeature() && feature.handleMessage(msg)) .findFirst().ifPresent(feature -> { logger.trace("handled reply of direct for {}", feature.getName()); - // reset failed message counter - failedMsgCount = 0; - // mark feature queried as processed and answered - setFeatureQueried(null); - feature.setQueryMessage(null); - feature.setQueryStatus(QueryStatus.QUERY_ANSWERED); + // notify feature queried was answered + featureQueriedAnswered(feature); }); // update all status features (e.g. device last update time) getFeatures().stream().filter(DeviceFeature::isStatusFeature) @@ -485,10 +465,6 @@ public class InsteonDevice extends BaseDevice properties = editProperties(); diff --git a/bundles/org.openhab.binding.insteon/src/main/java/org/openhab/binding/insteon/internal/handler/InsteonThingHandler.java b/bundles/org.openhab.binding.insteon/src/main/java/org/openhab/binding/insteon/internal/handler/InsteonThingHandler.java index 675c32c1fae..6cfd5db1495 100644 --- a/bundles/org.openhab.binding.insteon/src/main/java/org/openhab/binding/insteon/internal/handler/InsteonThingHandler.java +++ b/bundles/org.openhab.binding.insteon/src/main/java/org/openhab/binding/insteon/internal/handler/InsteonThingHandler.java @@ -91,4 +91,9 @@ public interface InsteonThingHandler extends ThingHandler { * Refreshes the thing */ public void refresh(); + + /** + * Updates the thing status + */ + public void updateStatus(); }