[dlinksmarthome] Reboot device daily following shutdown of cloud service (#14479)
* Reboot device --------- Signed-off-by: Mike Major <mike_j_major@hotmail.com>
This commit is contained in:
@@ -15,6 +15,7 @@ package org.openhab.binding.dlinksmarthome.internal;
|
||||
import static org.openhab.binding.dlinksmarthome.internal.DLinkSmartHomeBindingConstants.*;
|
||||
import static org.openhab.binding.dlinksmarthome.internal.motionsensor.DLinkMotionSensorConfig.IP_ADDRESS;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -96,7 +97,11 @@ public class DLinkSmartHomeDiscoveryParticipant implements MDNSDiscoveryParticip
|
||||
|
||||
private DiscoveryResult createMotionSensor(final ThingUID thingUID, final ThingTypeUID thingType,
|
||||
final ServiceInfo serviceInfo) {
|
||||
final String host = serviceInfo.getHostAddresses()[0];
|
||||
final Inet4Address[] addresses = serviceInfo.getInet4Addresses();
|
||||
if (addresses.length != 1) {
|
||||
return null;
|
||||
}
|
||||
final String host = addresses[0].getHostAddress();
|
||||
final String mac = serviceInfo.getPropertyString("mac");
|
||||
|
||||
final Map<String, Object> properties = new HashMap<>();
|
||||
|
||||
@@ -64,6 +64,9 @@ public class DLinkMotionSensorHandler extends BaseThingHandler implements DLinkM
|
||||
case ONLINE:
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
break;
|
||||
case REBOOTING:
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.DUTY_CYCLE, "Device rebooting");
|
||||
break;
|
||||
case COMMUNICATION_ERROR:
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
||||
break;
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
*/
|
||||
package org.openhab.binding.dlinksmarthome.internal.motionsensor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -43,10 +45,14 @@ public class DLinkMotionSensorCommunication extends DLinkHNAPCommunication {
|
||||
|
||||
// SOAP actions
|
||||
private static final String DETECTION_ACTION = "\"http://purenetworks.com/HNAP1/GetLatestDetection\"";
|
||||
private static final String REBOOT_ACTION = "\"http://purenetworks.com/HNAP1/Reboot\"";
|
||||
|
||||
private static final int DETECT_TIMEOUT_MS = 5000;
|
||||
private static final int DETECT_POLL_S = 1;
|
||||
|
||||
private static final int REBOOT_TIMEOUT_MS = 60000;
|
||||
private static final int REBOOT_WAIT_S = 35;
|
||||
|
||||
/**
|
||||
* Indicates the device status
|
||||
*
|
||||
@@ -64,6 +70,10 @@ public class DLinkMotionSensorCommunication extends DLinkHNAPCommunication {
|
||||
* Problem communicating with device
|
||||
*/
|
||||
COMMUNICATION_ERROR,
|
||||
/**
|
||||
* Device is being rebooted
|
||||
*/
|
||||
REBOOTING,
|
||||
/**
|
||||
* Internal error
|
||||
*/
|
||||
@@ -84,18 +94,24 @@ public class DLinkMotionSensorCommunication extends DLinkHNAPCommunication {
|
||||
private final Logger logger = LoggerFactory.getLogger(DLinkMotionSensorCommunication.class);
|
||||
|
||||
private final DLinkMotionSensorListener listener;
|
||||
private final ScheduledExecutorService scheduler;
|
||||
|
||||
private int rebootHour;
|
||||
|
||||
private SOAPMessage detectionAction;
|
||||
private SOAPMessage rebootAction;
|
||||
|
||||
private boolean loginSuccess;
|
||||
private boolean detectSuccess;
|
||||
private boolean rebootSuccess;
|
||||
|
||||
private long prevDetection;
|
||||
private long lastDetection;
|
||||
|
||||
private final ScheduledFuture<?> detectFuture;
|
||||
private ScheduledFuture<?> detectFuture;
|
||||
private ScheduledFuture<?> rebootFuture;
|
||||
|
||||
private boolean online = true;
|
||||
private boolean rebootRequired = false;
|
||||
private DeviceStatus status = DeviceStatus.INITIALISING;
|
||||
|
||||
/**
|
||||
@@ -104,35 +120,46 @@ public class DLinkMotionSensorCommunication extends DLinkHNAPCommunication {
|
||||
private final Runnable detect = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
boolean updateStatus = false;
|
||||
final DeviceStatus currentStatus = status;
|
||||
final boolean tryReboot = rebootRequired;
|
||||
|
||||
switch (status) {
|
||||
case INITIALISING:
|
||||
online = false;
|
||||
updateStatus = true;
|
||||
case REBOOTING:
|
||||
loginSuccess = false;
|
||||
// FALL-THROUGH
|
||||
case COMMUNICATION_ERROR:
|
||||
case ONLINE:
|
||||
if (!loginSuccess) {
|
||||
login(detectionAction, DETECT_TIMEOUT_MS);
|
||||
}
|
||||
if (!tryReboot) {
|
||||
if (!loginSuccess) {
|
||||
login(detectionAction, DETECT_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
if (!getLastDetection(false)) {
|
||||
// Try login again in case the session has timed out
|
||||
login(detectionAction, DETECT_TIMEOUT_MS);
|
||||
getLastDetection(true);
|
||||
if (!getLastDetection(false)) {
|
||||
// Try login again in case the session has timed out
|
||||
login(detectionAction, DETECT_TIMEOUT_MS);
|
||||
getLastDetection(true);
|
||||
}
|
||||
} else {
|
||||
login(rebootAction, REBOOT_TIMEOUT_MS);
|
||||
reboot();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (loginSuccess && detectSuccess) {
|
||||
if (tryReboot) {
|
||||
if (rebootSuccess) {
|
||||
rebootRequired = false;
|
||||
status = DeviceStatus.REBOOTING;
|
||||
detectFuture.cancel(false);
|
||||
detectFuture = scheduler.scheduleWithFixedDelay(detect, REBOOT_WAIT_S, DETECT_POLL_S,
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
} else if (loginSuccess && detectSuccess) {
|
||||
status = DeviceStatus.ONLINE;
|
||||
if (!online) {
|
||||
online = true;
|
||||
listener.sensorStatus(status);
|
||||
|
||||
if (currentStatus != DeviceStatus.ONLINE) {
|
||||
// Ignore old detections
|
||||
prevDetection = lastDetection;
|
||||
}
|
||||
@@ -140,12 +167,22 @@ public class DLinkMotionSensorCommunication extends DLinkHNAPCommunication {
|
||||
if (lastDetection != prevDetection) {
|
||||
listener.motionDetected();
|
||||
}
|
||||
} else {
|
||||
if (online || updateStatus) {
|
||||
online = false;
|
||||
listener.sensorStatus(status);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentStatus != status) {
|
||||
listener.sensorStatus(status);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Reboot the device
|
||||
*/
|
||||
private final Runnable reboot = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
rebootRequired = true;
|
||||
rebootFuture = scheduler.schedule(reboot, getNextRebootTime(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -153,6 +190,8 @@ public class DLinkMotionSensorCommunication extends DLinkHNAPCommunication {
|
||||
final DLinkMotionSensorListener listener, final ScheduledExecutorService scheduler) {
|
||||
super(config.ipAddress, config.pin);
|
||||
this.listener = listener;
|
||||
this.scheduler = scheduler;
|
||||
this.rebootHour = config.rebootHour;
|
||||
|
||||
if (getHNAPStatus() == HNAPStatus.INTERNAL_ERROR) {
|
||||
status = DeviceStatus.INTERNAL_ERROR;
|
||||
@@ -161,8 +200,10 @@ public class DLinkMotionSensorCommunication extends DLinkHNAPCommunication {
|
||||
try {
|
||||
final MessageFactory messageFactory = MessageFactory.newInstance();
|
||||
detectionAction = messageFactory.createMessage();
|
||||
rebootAction = messageFactory.createMessage();
|
||||
|
||||
buildDetectionAction();
|
||||
buildRebootAction();
|
||||
|
||||
} catch (final SOAPException e) {
|
||||
logger.debug("DLinkMotionSensorCommunication - Internal error", e);
|
||||
@@ -170,6 +211,7 @@ public class DLinkMotionSensorCommunication extends DLinkHNAPCommunication {
|
||||
}
|
||||
|
||||
detectFuture = scheduler.scheduleWithFixedDelay(detect, 0, DETECT_POLL_S, TimeUnit.SECONDS);
|
||||
rebootFuture = scheduler.schedule(reboot, getNextRebootTime(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,6 +220,7 @@ public class DLinkMotionSensorCommunication extends DLinkHNAPCommunication {
|
||||
@Override
|
||||
public void dispose() {
|
||||
detectFuture.cancel(true);
|
||||
rebootFuture.cancel(true);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -198,6 +241,40 @@ public class DLinkMotionSensorCommunication extends DLinkHNAPCommunication {
|
||||
headers.addHeader(SOAPACTION, DETECTION_ACTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the SOAP message used to reboot the device. This message will
|
||||
* only receive a successful response after the login process has been completed and the
|
||||
* authentication data has been set. Device needs rebooting as it eventually becomes
|
||||
* unresponsive due to cloud services being shutdown.
|
||||
*
|
||||
* @throws SOAPException
|
||||
*/
|
||||
private void buildRebootAction() throws SOAPException {
|
||||
rebootAction.getSOAPHeader().detachNode();
|
||||
final SOAPBody soapBody = rebootAction.getSOAPBody();
|
||||
soapBody.addChildElement("Reboot", "", HNAP_XMLNS);
|
||||
|
||||
final MimeHeaders headers = rebootAction.getMimeHeaders();
|
||||
headers.addHeader(SOAPACTION, REBOOT_ACTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of milliseconds to the next reboot time
|
||||
*
|
||||
* @return Time in ms to next reboot
|
||||
*/
|
||||
private long getNextRebootTime() {
|
||||
final LocalDateTime now = LocalDateTime.now();
|
||||
LocalDateTime nextReboot = LocalDateTime.of(now.getYear(), now.getMonth(), now.getDayOfMonth(), rebootHour, 0,
|
||||
0);
|
||||
|
||||
if (!nextReboot.isAfter(now)) {
|
||||
nextReboot = nextReboot.plusDays(1);
|
||||
}
|
||||
|
||||
return now.until(nextReboot, ChronoUnit.MILLIS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output unexpected responses to the debug log and sets the FIRMWARE error.
|
||||
*
|
||||
@@ -292,4 +369,32 @@ public class DLinkMotionSensorCommunication extends DLinkHNAPCommunication {
|
||||
|
||||
return detectSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the reboot message
|
||||
*
|
||||
*/
|
||||
private void reboot() {
|
||||
rebootSuccess = false;
|
||||
|
||||
if (loginSuccess) {
|
||||
try {
|
||||
final Document soapResponse = sendReceive(rebootAction, REBOOT_TIMEOUT_MS);
|
||||
|
||||
final Node result = soapResponse.getElementsByTagName("RebootResult").item(0);
|
||||
|
||||
if (result != null && OK.equals(result.getTextContent())) {
|
||||
rebootSuccess = true;
|
||||
} else {
|
||||
unexpectedResult("reboot - Unexpected response", soapResponse);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
// Assume there has been some problem trying to send one of the messages
|
||||
if (status != DeviceStatus.COMMUNICATION_ERROR) {
|
||||
logger.debug("getLastDetection - Communication error", e);
|
||||
status = DeviceStatus.COMMUNICATION_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ public class DLinkMotionSensorConfig {
|
||||
* Constants representing the configuration strings
|
||||
*/
|
||||
public static final String IP_ADDRESS = "ipAddress";
|
||||
public static final String PIN = "pin";
|
||||
|
||||
/**
|
||||
* The IP address of the device
|
||||
@@ -34,4 +33,9 @@ public class DLinkMotionSensorConfig {
|
||||
* The pin code of the device
|
||||
*/
|
||||
public String pin;
|
||||
|
||||
/**
|
||||
* The hour to reboot the device
|
||||
*/
|
||||
public int rebootHour;
|
||||
}
|
||||
|
||||
@@ -16,3 +16,5 @@ thing-type.config.dlinksmarthome.DCH-S150.ipAddress.label = Hostname or IP
|
||||
thing-type.config.dlinksmarthome.DCH-S150.ipAddress.description = Hostname or IP of the device.
|
||||
thing-type.config.dlinksmarthome.DCH-S150.pin.label = PIN Code
|
||||
thing-type.config.dlinksmarthome.DCH-S150.pin.description = PIN code from the back of the device.
|
||||
thing-type.config.dlinksmarthome.DCH-S150.rebootHour.label = Reboot Hour
|
||||
thing-type.config.dlinksmarthome.DCH-S150.rebootHour.description = Hour (24h) of the day that the device will be rebooted to ensure that it remains responsive.
|
||||
|
||||
@@ -26,6 +26,12 @@
|
||||
<label>PIN Code</label>
|
||||
<description>PIN code from the back of the device.</description>
|
||||
</parameter>
|
||||
<parameter name="rebootHour" type="integer" min="0" max="23" required="false">
|
||||
<default>3</default>
|
||||
<advanced>true</advanced>
|
||||
<label>Reboot Hour</label>
|
||||
<description>Hour (24h) of the day that the device will be rebooted to ensure that it remains responsive.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
</thing:thing-descriptions>
|
||||
|
||||
Reference in New Issue
Block a user