[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:
parent
38915f5009
commit
53bbda267f
|
@ -7,10 +7,11 @@ A binding for D-Link Smart Home devices.
|
||||||
### DCH-S150 (WiFi motion sensor)
|
### DCH-S150 (WiFi motion sensor)
|
||||||
|
|
||||||
The binding has been tested with hardware revisions A1 and A2 running firmware version 1.22.
|
The binding has been tested with hardware revisions A1 and A2 running firmware version 1.22.
|
||||||
|
The mydlink Home service is now end of life and the device requires a daily reboot (performed by the binding) to keep it responsive.
|
||||||
|
|
||||||
## Discovery
|
## Discovery
|
||||||
|
|
||||||
The binding can automatically discover devices that have already been added to the Wifi network. Please refer to your mydlink Home app for instructions on how to add your device to your Wifi network.
|
The binding can automatically discover devices that have already been added to the Wifi network.
|
||||||
|
|
||||||
## Binding Configuration
|
## Binding Configuration
|
||||||
|
|
||||||
|
@ -25,6 +26,7 @@ Once added the configuration must be updated to specify the PIN code located on
|
||||||
|
|
||||||
- **ipAddress** - Hostname or IP of the device
|
- **ipAddress** - Hostname or IP of the device
|
||||||
- **pin** - PIN code from the back of the device
|
- **pin** - PIN code from the back of the device
|
||||||
|
- **rebootHour** - Hour (24h) of the day that the device will be rebooted to ensure that it remains responsive (default is 3).
|
||||||
|
|
||||||
To manually configure a DCH-S150 Thing you must specify its IP address and PIN code.
|
To manually configure a DCH-S150 Thing you must specify its IP address and PIN code.
|
||||||
|
|
||||||
|
|
|
@ -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.DLinkSmartHomeBindingConstants.*;
|
||||||
import static org.openhab.binding.dlinksmarthome.internal.motionsensor.DLinkMotionSensorConfig.IP_ADDRESS;
|
import static org.openhab.binding.dlinksmarthome.internal.motionsensor.DLinkMotionSensorConfig.IP_ADDRESS;
|
||||||
|
|
||||||
|
import java.net.Inet4Address;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -96,7 +97,11 @@ public class DLinkSmartHomeDiscoveryParticipant implements MDNSDiscoveryParticip
|
||||||
|
|
||||||
private DiscoveryResult createMotionSensor(final ThingUID thingUID, final ThingTypeUID thingType,
|
private DiscoveryResult createMotionSensor(final ThingUID thingUID, final ThingTypeUID thingType,
|
||||||
final ServiceInfo serviceInfo) {
|
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 String mac = serviceInfo.getPropertyString("mac");
|
||||||
|
|
||||||
final Map<String, Object> properties = new HashMap<>();
|
final Map<String, Object> properties = new HashMap<>();
|
||||||
|
|
|
@ -64,6 +64,9 @@ public class DLinkMotionSensorHandler extends BaseThingHandler implements DLinkM
|
||||||
case ONLINE:
|
case ONLINE:
|
||||||
updateStatus(ThingStatus.ONLINE);
|
updateStatus(ThingStatus.ONLINE);
|
||||||
break;
|
break;
|
||||||
|
case REBOOTING:
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.DUTY_CYCLE, "Device rebooting");
|
||||||
|
break;
|
||||||
case COMMUNICATION_ERROR:
|
case COMMUNICATION_ERROR:
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
*/
|
*/
|
||||||
package org.openhab.binding.dlinksmarthome.internal.motionsensor;
|
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.ScheduledExecutorService;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -43,10 +45,14 @@ public class DLinkMotionSensorCommunication extends DLinkHNAPCommunication {
|
||||||
|
|
||||||
// SOAP actions
|
// SOAP actions
|
||||||
private static final String DETECTION_ACTION = "\"http://purenetworks.com/HNAP1/GetLatestDetection\"";
|
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_TIMEOUT_MS = 5000;
|
||||||
private static final int DETECT_POLL_S = 1;
|
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
|
* Indicates the device status
|
||||||
*
|
*
|
||||||
|
@ -64,6 +70,10 @@ public class DLinkMotionSensorCommunication extends DLinkHNAPCommunication {
|
||||||
* Problem communicating with device
|
* Problem communicating with device
|
||||||
*/
|
*/
|
||||||
COMMUNICATION_ERROR,
|
COMMUNICATION_ERROR,
|
||||||
|
/**
|
||||||
|
* Device is being rebooted
|
||||||
|
*/
|
||||||
|
REBOOTING,
|
||||||
/**
|
/**
|
||||||
* Internal error
|
* Internal error
|
||||||
*/
|
*/
|
||||||
|
@ -84,18 +94,24 @@ public class DLinkMotionSensorCommunication extends DLinkHNAPCommunication {
|
||||||
private final Logger logger = LoggerFactory.getLogger(DLinkMotionSensorCommunication.class);
|
private final Logger logger = LoggerFactory.getLogger(DLinkMotionSensorCommunication.class);
|
||||||
|
|
||||||
private final DLinkMotionSensorListener listener;
|
private final DLinkMotionSensorListener listener;
|
||||||
|
private final ScheduledExecutorService scheduler;
|
||||||
|
|
||||||
|
private int rebootHour;
|
||||||
|
|
||||||
private SOAPMessage detectionAction;
|
private SOAPMessage detectionAction;
|
||||||
|
private SOAPMessage rebootAction;
|
||||||
|
|
||||||
private boolean loginSuccess;
|
private boolean loginSuccess;
|
||||||
private boolean detectSuccess;
|
private boolean detectSuccess;
|
||||||
|
private boolean rebootSuccess;
|
||||||
|
|
||||||
private long prevDetection;
|
private long prevDetection;
|
||||||
private long lastDetection;
|
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;
|
private DeviceStatus status = DeviceStatus.INITIALISING;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -104,15 +120,17 @@ public class DLinkMotionSensorCommunication extends DLinkHNAPCommunication {
|
||||||
private final Runnable detect = new Runnable() {
|
private final Runnable detect = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
boolean updateStatus = false;
|
final DeviceStatus currentStatus = status;
|
||||||
|
final boolean tryReboot = rebootRequired;
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case INITIALISING:
|
case INITIALISING:
|
||||||
online = false;
|
case REBOOTING:
|
||||||
updateStatus = true;
|
loginSuccess = false;
|
||||||
// FALL-THROUGH
|
// FALL-THROUGH
|
||||||
case COMMUNICATION_ERROR:
|
case COMMUNICATION_ERROR:
|
||||||
case ONLINE:
|
case ONLINE:
|
||||||
|
if (!tryReboot) {
|
||||||
if (!loginSuccess) {
|
if (!loginSuccess) {
|
||||||
login(detectionAction, DETECT_TIMEOUT_MS);
|
login(detectionAction, DETECT_TIMEOUT_MS);
|
||||||
}
|
}
|
||||||
|
@ -122,17 +140,26 @@ public class DLinkMotionSensorCommunication extends DLinkHNAPCommunication {
|
||||||
login(detectionAction, DETECT_TIMEOUT_MS);
|
login(detectionAction, DETECT_TIMEOUT_MS);
|
||||||
getLastDetection(true);
|
getLastDetection(true);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
login(rebootAction, REBOOT_TIMEOUT_MS);
|
||||||
|
reboot();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
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;
|
status = DeviceStatus.ONLINE;
|
||||||
if (!online) {
|
if (currentStatus != DeviceStatus.ONLINE) {
|
||||||
online = true;
|
|
||||||
listener.sensorStatus(status);
|
|
||||||
|
|
||||||
// Ignore old detections
|
// Ignore old detections
|
||||||
prevDetection = lastDetection;
|
prevDetection = lastDetection;
|
||||||
}
|
}
|
||||||
|
@ -140,12 +167,22 @@ public class DLinkMotionSensorCommunication extends DLinkHNAPCommunication {
|
||||||
if (lastDetection != prevDetection) {
|
if (lastDetection != prevDetection) {
|
||||||
listener.motionDetected();
|
listener.motionDetected();
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
if (online || updateStatus) {
|
|
||||||
online = false;
|
if (currentStatus != status) {
|
||||||
listener.sensorStatus(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) {
|
final DLinkMotionSensorListener listener, final ScheduledExecutorService scheduler) {
|
||||||
super(config.ipAddress, config.pin);
|
super(config.ipAddress, config.pin);
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
|
this.scheduler = scheduler;
|
||||||
|
this.rebootHour = config.rebootHour;
|
||||||
|
|
||||||
if (getHNAPStatus() == HNAPStatus.INTERNAL_ERROR) {
|
if (getHNAPStatus() == HNAPStatus.INTERNAL_ERROR) {
|
||||||
status = DeviceStatus.INTERNAL_ERROR;
|
status = DeviceStatus.INTERNAL_ERROR;
|
||||||
|
@ -161,8 +200,10 @@ public class DLinkMotionSensorCommunication extends DLinkHNAPCommunication {
|
||||||
try {
|
try {
|
||||||
final MessageFactory messageFactory = MessageFactory.newInstance();
|
final MessageFactory messageFactory = MessageFactory.newInstance();
|
||||||
detectionAction = messageFactory.createMessage();
|
detectionAction = messageFactory.createMessage();
|
||||||
|
rebootAction = messageFactory.createMessage();
|
||||||
|
|
||||||
buildDetectionAction();
|
buildDetectionAction();
|
||||||
|
buildRebootAction();
|
||||||
|
|
||||||
} catch (final SOAPException e) {
|
} catch (final SOAPException e) {
|
||||||
logger.debug("DLinkMotionSensorCommunication - Internal error", 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);
|
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
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
detectFuture.cancel(true);
|
detectFuture.cancel(true);
|
||||||
|
rebootFuture.cancel(true);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,6 +241,40 @@ public class DLinkMotionSensorCommunication extends DLinkHNAPCommunication {
|
||||||
headers.addHeader(SOAPACTION, DETECTION_ACTION);
|
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.
|
* Output unexpected responses to the debug log and sets the FIRMWARE error.
|
||||||
*
|
*
|
||||||
|
@ -292,4 +369,32 @@ public class DLinkMotionSensorCommunication extends DLinkHNAPCommunication {
|
||||||
|
|
||||||
return detectSuccess;
|
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
|
* Constants representing the configuration strings
|
||||||
*/
|
*/
|
||||||
public static final String IP_ADDRESS = "ipAddress";
|
public static final String IP_ADDRESS = "ipAddress";
|
||||||
public static final String PIN = "pin";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The IP address of the device
|
* The IP address of the device
|
||||||
|
@ -34,4 +33,9 @@ public class DLinkMotionSensorConfig {
|
||||||
* The pin code of the device
|
* The pin code of the device
|
||||||
*/
|
*/
|
||||||
public String pin;
|
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.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.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.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>
|
<label>PIN Code</label>
|
||||||
<description>PIN code from the back of the device.</description>
|
<description>PIN code from the back of the device.</description>
|
||||||
</parameter>
|
</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>
|
</config-description>
|
||||||
</thing-type>
|
</thing-type>
|
||||||
</thing:thing-descriptions>
|
</thing:thing-descriptions>
|
||||||
|
|
Loading…
Reference in New Issue