[insteon] Fix intermittent device offline status (#18893)

Signed-off-by: Jeremy Setton <jeremy.setton@gmail.com>
This commit is contained in:
Jeremy
2025-07-06 10:24:14 +02:00
committed by GitHub
parent c9e376af9f
commit b978f989ca
15 changed files with 170 additions and 81 deletions
+47 -35
View File
@@ -65,41 +65,44 @@ The default poll interval of 300 seconds has been tested and found to be a good
### `hub1`
| Parameter | Default | Required | Description |
| --------------------------- | :-----: | :------: | ---------------------------------------------------------------------- |
| hostname | | Yes | Network address of the hub. |
| port | 9761 | No | Network port of the hub. |
| devicePollIntervalInSeconds | 300 | No | Device poll interval in seconds. |
| deviceDiscoveryEnabled | true | No | Discover Insteon devices found in the hub database but not configured. |
| sceneDiscoveryEnabled | false | No | Discover Insteon scenes found in the hub database but not configured. |
| deviceSyncEnabled | false | No | Synchronize related devices based on their all-link database. |
| Parameter | Default | Required | Description |
| ------------------------------ | :-----: | :------: | ------------------------------------------------------------------------- |
| hostname | | Yes | Network address of the hub. |
| port | 9761 | No | Network port of the hub. |
| devicePollIntervalInSeconds | 300 | No | Device poll interval in seconds. |
| deviceResponseTimeoutInMinutes | 30 | No | Device response timeout in minutes before a device is considered offline. |
| deviceDiscoveryEnabled | true | No | Discover Insteon devices found in the hub database but not configured. |
| sceneDiscoveryEnabled | false | No | Discover Insteon scenes found in the hub database but not configured. |
| deviceSyncEnabled | false | No | Synchronize related devices based on their all-link database. |
>NOTE: Use this bridge to connect to a networked PLM via ser2net.
### `hub2`
| Parameter | Default | Required | Description |
| ----------------------------- | :-----: | :------: | ---------------------------------------------------------------------- |
| hostname | | Yes | Network address of the hub. |
| port | 25105 | No | Network port of the hub. |
| username | | Yes | Username to access the hub. |
| password | | Yes | Password to access the hub. |
| hubPollIntervalInMilliseconds | 1000 | No | Hub poll interval in milliseconds. |
| devicePollIntervalInSeconds | 300 | No | Device poll interval in seconds. |
| deviceDiscoveryEnabled | true | No | Discover Insteon devices found in the hub database but not configured. |
| sceneDiscoveryEnabled | false | No | Discover Insteon scenes found in the hub database but not configured. |
| deviceSyncEnabled | false | No | Synchronize related devices based on their all-link database. |
| Parameter | Default | Required | Description |
| ------------------------------ | :-----: | :------: | ------------------------------------------------------------------------- |
| hostname | | Yes | Network address of the hub. |
| port | 25105 | No | Network port of the hub. |
| username | | Yes | Username to access the hub. |
| password | | Yes | Password to access the hub. |
| hubPollIntervalInMilliseconds | 1000 | No | Hub poll interval in milliseconds. |
| devicePollIntervalInSeconds | 300 | No | Device poll interval in seconds. |
| deviceResponseTimeoutInMinutes | 30 | No | Device response timeout in minutes before a device is considered offline. |
| deviceDiscoveryEnabled | true | No | Discover Insteon devices found in the hub database but not configured. |
| sceneDiscoveryEnabled | false | No | Discover Insteon scenes found in the hub database but not configured. |
| deviceSyncEnabled | false | No | Synchronize related devices based on their all-link database. |
### `plm`
| Parameter | Default | Required | Description |
| --------------------------- | :-----: | :------: | ------------------------------------------------------------------------ |
| serialPort | | Yes | Serial port connected to the modem. Example: `/dev/ttyS0` or `COM1` |
| baudRate | 19200 | No | Serial port baud rate connected to the modem. |
| devicePollIntervalInSeconds | 300 | No | Device poll interval in seconds. |
| deviceDiscoveryEnabled | true | No | Discover Insteon devices found in the modem database but not configured. |
| sceneDiscoveryEnabled | false | No | Discover Insteon scenes found in the modem database but not configured. |
| deviceSyncEnabled | false | No | Synchronize related devices based on their all-link database. |
| Parameter | Default | Required | Description |
| ------------------------------ | :-----: | :------: | ------------------------------------------------------------------------- |
| serialPort | | Yes | Serial port connected to the modem. Example: `/dev/ttyS0` or `COM1` |
| baudRate | 19200 | No | Serial port baud rate connected to the modem. |
| devicePollIntervalInSeconds | 300 | No | Device poll interval in seconds. |
| deviceResponseTimeoutInMinutes | 30 | No | Device response timeout in minutes before a device is considered offline. |
| deviceDiscoveryEnabled | true | No | Discover Insteon devices found in the modem database but not configured. |
| sceneDiscoveryEnabled | false | No | Discover Insteon scenes found in the modem database but not configured. |
| deviceSyncEnabled | false | No | Synchronize related devices based on their all-link database. |
### `device`
@@ -1506,15 +1509,24 @@ It shouldn't be used in most cases except during initial device configuration.
Same goes with commands, the binding will queue up commands requested on these devices and send them during the awake time window.
Only one command per channel is queued, this mean that subsequent requests will overwrite previous ones.
### Heartbeat Timeout Monitor
### Heartbeat Timeout
Sensor devices that supports heartbeat have a timeout monitor.
If no broadcast message is received within a specific interval, the associated thing status will go offline until the binding receives a broadcast message from that device.
The heartbeat interval on most sensor devices is hard coded as 24 hours but some have the ability to change that interval through the `heartbeat-interval` channel.
It is enabled by default on devices that supports that feature and will be disabled on devices that have the ability to turn off their heartbeat through the `heartbeat-on-off` channel.
It is important that the heartbeat group (typically 4) is linked properly to the modem by using the `insteon device addMissingLinks` console command.
Otherwise, if the link is missing, the timeout monitor will be disabled.
If necessary, the heartbeat timeout monitor can be manually reset by disabling and re-enabling the associated device thing.
Sensor devices that support heartbeats have a timeout.
If a broadcast message is not received within a specific interval, the associated thing's status will change to offline.
This status persists until the binding receives a broadcast message from that device.
While most sensor devices have a hardcoded heartbeat interval of 24 hours, some allow modification via the `heartbeat-interval` channel.
This timeout feature is enabled by default on supporting devices and disabled on devices that can have their heartbeat turned off using the `heartbeat-on-off` channel.
Proper linking of the heartbeat group (typically group 4) to the modem is crucial; use the `insteon device addMissingLinks` console command to ensure this.
If the link is missing, the timeout feature will be disabled.
The heartbeat timeout can be manually reset, if necessary, by disabling and then re-enabling the associated device thing.
### Response Timeout
Non-battery powered devices have a response timeout.
If a successful response message is not received within a specific interval, the associated thing's status will change to offline.
While the device is offline, the binding will ignore commands sent to it.
This status persists until a valid response is received.
The response timeout can be increased from 30 minutes (default) up to 6 hours by updating the associated bridge parameter `deviceResponseTimeoutInMinutes`.
## Related Devices
@@ -509,7 +509,7 @@ public class DeviceCommand extends InsteonCommand {
console.println("There are no missing links for device " + device.getAddress() + ".");
} else {
if (!deviceLinks.isEmpty()) {
if (!device.isAwake() || !device.isResponding()) {
if (!device.isAwake() || !device.isOnline()) {
console.println("Scheduling " + deviceLinks.size() + " missing links for device "
+ device.getAddress() + " to be added to its link database the next time it is "
+ (device.isBatteryPowered() ? "awake" : "online") + ".");
@@ -608,7 +608,7 @@ public class DeviceCommand extends InsteonCommand {
+ ".");
} else {
int count = device.getLinkDB().getChanges().size();
if (!device.isAwake() || !device.isResponding() || !getModem().getDB().isComplete()) {
if (!device.isAwake() || !device.isOnline() || !getModem().getDB().isComplete()) {
console.println("Scheduling " + count + " pending changes for device " + device.getAddress()
+ " to be applied to its link database the next time it is "
+ (device.isBatteryPowered() ? "awake" : "online") + ".");
@@ -716,10 +716,10 @@ public class DeviceCommand extends InsteonCommand {
device.getLinkDB().setReload(true);
device.resetFeaturesQueryStatus();
if (!device.isAwake() || !device.isResponding() || !getModem().getDB().isComplete() || !immediate) {
if (!device.isAwake() || !device.isOnline() || !getModem().getDB().isComplete() || !immediate) {
console.println("The device " + device.getAddress()
+ " is scheduled to be refreshed the next time it is "
+ (device.isBatteryPowered() ? "awake" : !device.isResponding() ? "online" : "polled") + ".");
+ (device.isBatteryPowered() ? "awake" : !device.isOnline() ? "online" : "polled") + ".");
} else {
console.println("Refreshing device " + device.getAddress() + "...");
device.poll(0L);
@@ -23,6 +23,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
public abstract class InsteonBridgeConfiguration {
private int devicePollIntervalInSeconds = 300;
private int deviceResponseTimeoutInMinutes = 30;
private boolean deviceDiscoveryEnabled = true;
private boolean sceneDiscoveryEnabled = false;
private boolean deviceSyncEnabled = false;
@@ -31,6 +32,10 @@ public abstract class InsteonBridgeConfiguration {
return devicePollIntervalInSeconds * 1000; // in milliseconds
}
public int getDeviceResponseTimeout() {
return deviceResponseTimeoutInMinutes;
}
public boolean isDeviceDiscoveryEnabled() {
return deviceDiscoveryEnabled;
}
@@ -49,6 +54,7 @@ public abstract class InsteonBridgeConfiguration {
public String toString() {
String s = "";
s += " devicePollIntervalInSeconds=" + devicePollIntervalInSeconds;
s += " deviceResponseTimeoutInMinutes=" + deviceResponseTimeoutInMinutes;
s += " deviceDiscoveryEnabled=" + deviceDiscoveryEnabled;
s += " sceneDiscoveryEnabled=" + sceneDiscoveryEnabled;
s += " deviceSyncEnabled=" + deviceSyncEnabled;
@@ -149,8 +149,8 @@ public abstract class BaseDevice<@NonNull T extends DeviceAddress, @NonNull S ex
}
}
public boolean isResponding() {
return failedRequestCount < FAILED_REQUEST_THRESHOLD;
public boolean isOnline() {
return Optional.ofNullable(handler).map(InsteonThingHandler::isOnline).orElse(false);
}
public void setModem(@Nullable InsteonModem modem) {
@@ -592,17 +592,12 @@ public abstract class BaseDevice<@NonNull T extends DeviceAddress, @NonNull S ex
* @param feature the feature queried
*/
protected void featureQueriedAnswered(DeviceFeature feature) {
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();
}
}
/**
@@ -620,8 +615,8 @@ public abstract class BaseDevice<@NonNull T extends DeviceAddress, @NonNull S ex
setFeatureQueried(null);
feature.setQueryMessage(null);
feature.setQueryStatus(QueryStatus.NEVER_QUERIED);
// resend feature query message if defined and device is responding
if (msg != null && isResponding()) {
// resend feature query message if defined and failed request count is below threshold
if (msg != null && failedRequestCount < FAILED_REQUEST_THRESHOLD) {
sendMessage(msg.copy(), feature, 0L);
}
}
@@ -636,10 +631,6 @@ public abstract class BaseDevice<@NonNull T extends DeviceAddress, @NonNull S ex
failedRequestCount++;
// notify feature queried expired
featureQueriedExpired(feature);
// notify status changed if failed request count at threshold
if (failedRequestCount == FAILED_REQUEST_THRESHOLD) {
statusChanged();
}
}
/**
@@ -450,6 +450,8 @@ public class InsteonDevice extends BaseDevice<InsteonAddress, InsteonDeviceHandl
logger.trace("handled reply of direct for {}", feature.getName());
// notify feature queried was answered
featureQueriedAnswered(feature);
// reset response timeout
resetResponseTimeout();
});
// update all status features (e.g. device last update time)
getFeatures().stream().filter(DeviceFeature::isStatusFeature)
@@ -821,12 +823,22 @@ public class InsteonDevice extends BaseDevice<InsteonAddress, InsteonDeviceHandl
}
/**
* Resets heartbeat monitor
* Resets heartbeat timeout
*/
public void resetHeartbeatMonitor() {
public void resetHeartbeatTimeout() {
InsteonDeviceHandler handler = getHandler();
if (handler != null) {
handler.resetHeartbeatMonitor();
handler.resetHeartbeatTimeout();
}
}
/**
* Resets response timeout
*/
public void resetResponseTimeout() {
InsteonDeviceHandler handler = getHandler();
if (handler != null) {
handler.resetResponseTimeout();
}
}
@@ -298,8 +298,8 @@ public class LinkDB {
*/
public void load(long delay) {
DatabaseManager dbm = getDatabaseManager();
if (!device.isAwake() || !device.isResponding()) {
logger.debug("deferring load link db for {}, device is not awake or responding", device.getAddress());
if (!device.isAwake() || !device.isOnline()) {
logger.debug("deferring load link db for {}, device is not awake or online", device.getAddress());
setReload(true);
} else if (dbm == null) {
logger.debug("unable to load link db for {}, database manager not available", device.getAddress());
@@ -326,8 +326,8 @@ public class LinkDB {
if (getChanges().isEmpty()) {
logger.debug("no changes to update link db for {}", device.getAddress());
setUpdate(false);
} else if (!device.isAwake() || !device.isResponding()) {
logger.debug("deferring update link db for {}, device is not awake or responding", device.getAddress());
} else if (!device.isAwake() || !device.isOnline()) {
logger.debug("deferring update link db for {}, device is not awake or online", device.getAddress());
setUpdate(true);
} else if (dbm == null) {
logger.debug("unable to update link db for {}, database manager not available", device.getAddress());
@@ -584,9 +584,9 @@ public abstract class MessageHandler extends BaseFeatureHandler {
@Override
public void handleMessage(byte cmd1, Msg msg) {
// reset device heartbeat monitor on all link broadcast or cleanup message not replayed
// reset device heartbeat timeout on all link broadcast or cleanup message not replayed
if (msg.isAllLinkBroadcastOrCleanup() && !msg.isReplayed()) {
getInsteonDevice().resetHeartbeatMonitor();
getInsteonDevice().resetHeartbeatTimeout();
}
}
}
@@ -1003,8 +1003,8 @@ public abstract class MessageHandler extends BaseFeatureHandler {
@Override
public void handleMessage(byte cmd1, Msg msg) {
super.handleMessage(cmd1, msg);
// reset device heartbeat monitor
getInsteonDevice().resetHeartbeatMonitor();
// reset device heartbeat timeout
getInsteonDevice().resetHeartbeatTimeout();
}
}
@@ -1229,8 +1229,8 @@ public abstract class MessageHandler extends BaseFeatureHandler {
@Override
public void handleMessage(byte cmd1, Msg msg) {
super.handleMessage(cmd1, msg);
// reset device heartbeat monitor
getInsteonDevice().resetHeartbeatMonitor();
// reset device heartbeat timeout
getInsteonDevice().resetHeartbeatTimeout();
}
@Override
@@ -77,6 +77,11 @@ public abstract class InsteonBaseThingHandler extends BaseThingHandler implement
.map(InsteonBridgeHandler.class::cast).orElse(null);
}
@Override
public boolean isOnline() {
return getThing().getStatus() == ThingStatus.ONLINE;
}
@Override
public void channelLinked(ChannelUID channelUID) {
logger.debug("channel {} linked", channelUID);
@@ -145,9 +150,8 @@ public abstract class InsteonBaseThingHandler extends BaseThingHandler implement
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("channel {} received command {}", channelUID, command);
ThingStatus status = getThing().getStatus();
if (status != ThingStatus.ONLINE) {
logger.debug("thing {} not ready to handle commands, it will be ignored", getThing().getUID());
if (!isOnline()) {
logger.debug("thing {} not online, ignoring command", getThing().getUID());
return;
}
@@ -120,6 +120,10 @@ public class InsteonBridgeHandler extends InsteonBaseThingHandler implements Bri
return getBridgeConfig().getDevicePollInterval();
}
public int getDeviceResponseTimeout() {
return getBridgeConfig().getDeviceResponseTimeout();
}
public boolean isDeviceDiscoveryEnabled() {
return getBridgeConfig().isDeviceDiscoveryEnabled();
}
@@ -55,6 +55,7 @@ public class InsteonDeviceHandler extends InsteonBaseThingHandler {
private @Nullable InsteonDevice device;
private @Nullable ScheduledFuture<?> heartbeatJob;
private @Nullable ScheduledFuture<?> responseJob;
private InsteonStateDescriptionProvider stateDescriptionProvider;
public InsteonDeviceHandler(Thing thing, InsteonStateDescriptionProvider stateDescriptionProvider) {
@@ -187,14 +188,16 @@ public class InsteonDeviceHandler extends InsteonBaseThingHandler {
}
this.device = null;
stopHeartbeatMonitor();
stopHeartbeatTimeout();
stopResponseTimeout();
super.dispose();
}
@Override
public void refresh() {
resetHeartbeatMonitor();
resetHeartbeatTimeout();
resetResponseTimeout();
super.refresh();
}
@@ -221,7 +224,8 @@ public class InsteonDeviceHandler extends InsteonBaseThingHandler {
public void deviceLinkDBUpdated(InsteonDevice device) {
if (device.getLinkDB().isComplete()) {
resetHeartbeatMonitor();
resetHeartbeatTimeout();
resetResponseTimeout();
InsteonModem modem = getModem();
if (modem != null) {
@@ -267,11 +271,6 @@ public class InsteonDeviceHandler extends InsteonBaseThingHandler {
return;
}
if (!device.isResponding() && !device.isBatteryPowered()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Device not responding.");
return;
}
if (device.getProductData() == null) {
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "Waiting for product data.");
return;
@@ -316,8 +315,8 @@ public class InsteonDeviceHandler extends InsteonBaseThingHandler {
}, RESET_DELAY, TimeUnit.MILLISECONDS);
}
public void resetHeartbeatMonitor() {
if (stopHeartbeatMonitor()) {
public void resetHeartbeatTimeout() {
if (stopHeartbeatTimeout()) {
updateStatus();
}
@@ -327,13 +326,13 @@ public class InsteonDeviceHandler extends InsteonBaseThingHandler {
}
if (device.getMissingLinks().contains(FEATURE_HEARTBEAT)) {
logger.warn("heartbeat link missing, timeout monitor disabled for {}", getThing().getUID());
logger.warn("heartbeat link missing, timeout disabled for {}", getThing().getUID());
return;
}
int timeout = device.getHeartbeatTimeout();
if (timeout > 0) {
logger.debug("setting heartbeat timeout monitor to {} min for {}", timeout, getThing().getUID());
logger.debug("setting heartbeat timeout to {} min for {}", timeout, getThing().getUID());
heartbeatJob = scheduler.schedule(() -> {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Heartbeat timed out.");
@@ -341,7 +340,7 @@ public class InsteonDeviceHandler extends InsteonBaseThingHandler {
}
}
private boolean stopHeartbeatMonitor() {
private boolean stopHeartbeatTimeout() {
boolean hasTimedOut = false;
ScheduledFuture<?> heartbeatJob = this.heartbeatJob;
if (heartbeatJob != null) {
@@ -351,4 +350,31 @@ public class InsteonDeviceHandler extends InsteonBaseThingHandler {
}
return hasTimedOut;
}
public void resetResponseTimeout() {
if (stopResponseTimeout()) {
updateStatus();
}
InsteonDevice device = getDevice();
InsteonBridgeHandler handler = getInsteonBridgeHandler();
if (device == null || !device.hasModemDBEntry() || device.isBatteryPowered() || handler == null) {
return;
}
responseJob = scheduler.schedule(() -> {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Device not responding.");
}, handler.getDeviceResponseTimeout(), TimeUnit.MINUTES);
}
private boolean stopResponseTimeout() {
boolean hasTimedOut = false;
ScheduledFuture<?> responseJob = this.responseJob;
if (responseJob != null) {
hasTimedOut = responseJob.isDone();
responseJob.cancel(true);
this.responseJob = null;
}
return hasTimedOut;
}
}
@@ -58,6 +58,13 @@ public interface InsteonThingHandler extends ThingHandler {
*/
public Map<String, String> getChannelsInfo();
/**
* Returns if the thing status is online
*
* @return true if the thing status is online
*/
public boolean isOnline();
/**
* Updates a channel state
*
@@ -38,6 +38,8 @@ thing-type.config.insteon.hub1.deviceDiscoveryEnabled.label = Discover Devices
thing-type.config.insteon.hub1.deviceDiscoveryEnabled.description = Discover Insteon devices found in the modem database.
thing-type.config.insteon.hub1.devicePollIntervalInSeconds.label = Device Poll Interval
thing-type.config.insteon.hub1.devicePollIntervalInSeconds.description = Device poll interval in seconds.
thing-type.config.insteon.hub1.deviceResponseTimeoutInMinutes.label = Device Response Timeout
thing-type.config.insteon.hub1.deviceResponseTimeoutInMinutes.description = Device response timeout in minutes before a device is considered offline.
thing-type.config.insteon.hub1.deviceSyncEnabled.label = Synchronize Related Devices
thing-type.config.insteon.hub1.deviceSyncEnabled.description = Synchronize related devices based on their all-link database.
thing-type.config.insteon.hub1.hostname.label = Network Address
@@ -51,6 +53,8 @@ thing-type.config.insteon.hub2.deviceDiscoveryEnabled.label = Discover Devices
thing-type.config.insteon.hub2.deviceDiscoveryEnabled.description = Discover Insteon devices found in the modem database.
thing-type.config.insteon.hub2.devicePollIntervalInSeconds.label = Device Poll Interval
thing-type.config.insteon.hub2.devicePollIntervalInSeconds.description = Device poll interval in seconds.
thing-type.config.insteon.hub2.deviceResponseTimeoutInMinutes.label = Device Response Timeout
thing-type.config.insteon.hub2.deviceResponseTimeoutInMinutes.description = Device response timeout in minutes before a device is considered offline.
thing-type.config.insteon.hub2.deviceSyncEnabled.label = Synchronize Related Devices
thing-type.config.insteon.hub2.deviceSyncEnabled.description = Synchronize related devices based on their all-link database.
thing-type.config.insteon.hub2.hostname.label = Network Address
@@ -134,6 +138,8 @@ thing-type.config.insteon.plm.deviceDiscoveryEnabled.label = Discover Devices
thing-type.config.insteon.plm.deviceDiscoveryEnabled.description = Discover Insteon devices found in the modem database.
thing-type.config.insteon.plm.devicePollIntervalInSeconds.label = Device Poll Interval
thing-type.config.insteon.plm.devicePollIntervalInSeconds.description = Device poll interval in seconds.
thing-type.config.insteon.plm.deviceResponseTimeoutInMinutes.label = Device Response Timeout
thing-type.config.insteon.plm.deviceResponseTimeoutInMinutes.description = Device response timeout in minutes before a device is considered offline.
thing-type.config.insteon.plm.deviceSyncEnabled.label = Synchronize Related Devices
thing-type.config.insteon.plm.deviceSyncEnabled.description = Synchronize related devices based on their all-link database.
thing-type.config.insteon.plm.sceneDiscoveryEnabled.label = Discover Scenes
@@ -30,6 +30,13 @@
<advanced>true</advanced>
</parameter>
<parameter name="deviceResponseTimeoutInMinutes" type="integer" min="30" max="360">
<label>Device Response Timeout</label>
<description>Device response timeout in minutes before a device is considered offline.</description>
<default>30</default>
<advanced>true</advanced>
</parameter>
<parameter name="deviceDiscoveryEnabled" type="boolean">
<label>Discover Devices</label>
<description>Discover Insteon devices found in the modem database.</description>
@@ -48,6 +48,13 @@
<advanced>true</advanced>
</parameter>
<parameter name="deviceResponseTimeoutInMinutes" type="integer" min="30" max="360">
<label>Device Response Timeout</label>
<description>Device response timeout in minutes before a device is considered offline.</description>
<default>30</default>
<advanced>true</advanced>
</parameter>
<parameter name="deviceDiscoveryEnabled" type="boolean">
<label>Discover Devices</label>
<description>Discover Insteon devices found in the modem database.</description>
@@ -40,6 +40,13 @@
<advanced>true</advanced>
</parameter>
<parameter name="deviceResponseTimeoutInMinutes" type="integer" min="30" max="360">
<label>Device Response Timeout</label>
<description>Device response timeout in minutes before a device is considered offline.</description>
<default>30</default>
<advanced>true</advanced>
</parameter>
<parameter name="deviceDiscoveryEnabled" type="boolean">
<label>Discover Devices</label>
<description>Discover Insteon devices found in the modem database.</description>