[haassohnpelletstove] Improve connection handling (#18212)
* Adding a Reconnect Rate to improve connectivity for the thing in case of weak wifi signal and automatically reconnect the thing. Signed-off-by: chingon007 <tron81@gmx.de>pull/14578/merge
parent
c3fa94302d
commit
96def56c6d
|
@ -12,7 +12,7 @@ WIFI module. More information about the WIFI module can be found here: <https://
|
||||||
## Thing Configuration
|
## Thing Configuration
|
||||||
|
|
||||||
In general two parameters are required. The IP-Address of the WIFI-Modul of the Stove in the local Network and the Access PIN of the Stove.
|
In general two parameters are required. The IP-Address of the WIFI-Modul of the Stove in the local Network and the Access PIN of the Stove.
|
||||||
The PIN can be found directly at the stove under the Menue/Network/WLAN-PIN
|
The PIN can be found directly at the stove under the Menue/Network/WLAN-PIN.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
Thing haassohnpelletstove:oven:myOven "Pelletstove" [ hostIP="192.168.0.23", hostPIN="1234"]
|
Thing haassohnpelletstove:oven:myOven "Pelletstove" [ hostIP="192.168.0.23", hostPIN="1234"]
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
package org.openhab.binding.haassohnpelletstove.internal;
|
package org.openhab.binding.haassohnpelletstove.internal;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link HaasSohnpelletstoveConfiguration} class contains fields mapping thing configuration parameters.
|
* The {@link HaasSohnpelletstoveConfiguration} class contains fields mapping thing configuration parameters.
|
||||||
|
@ -23,7 +22,7 @@ import org.eclipse.jdt.annotation.Nullable;
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class HaasSohnpelletstoveConfiguration {
|
public class HaasSohnpelletstoveConfiguration {
|
||||||
|
|
||||||
public @Nullable String hostIP = null;
|
public String hostIP = "";
|
||||||
public @Nullable String hostPIN = null;
|
public String hostPIN = "";
|
||||||
public int refreshRate = 30;
|
public int refreshRate = 30;
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,82 +108,63 @@ public class HaasSohnpelletstoveHandler extends BaseThingHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Calls the service to update the oven data
|
|
||||||
*
|
|
||||||
* @param postdata
|
|
||||||
*/
|
|
||||||
private boolean updateOvenData(@Nullable String postdata) {
|
|
||||||
Helper message = new Helper();
|
|
||||||
if (serviceCommunication.updateOvenData(postdata, message, this.getThing().getUID().toString())) {
|
|
||||||
updateStatus(ThingStatus.ONLINE);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
|
|
||||||
message.getStatusDesription());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
logger.debug("Initializing haassohnpelletstove handler for thing {}", getThing().getUID());
|
logger.debug("Initializing haassohnpelletstove handler for thing {}", getThing().getUID());
|
||||||
config = getConfigAs(HaasSohnpelletstoveConfiguration.class);
|
config = getConfigAs(HaasSohnpelletstoveConfiguration.class);
|
||||||
boolean validConfig = true;
|
if (config.refreshRate < 1 || config.refreshRate > 1000) {
|
||||||
String errors = "";
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||||
String statusDescr = null;
|
"Parameter 'refresh Rate' must be in the range 1-1000!");
|
||||||
if (config.refreshRate < 0 && config.refreshRate > 999) {
|
return;
|
||||||
errors += " Parameter 'refresh Rate' greater then 0 and less then 1000.";
|
|
||||||
statusDescr = "Parameter 'refresh Rate' greater then 0 and less then 1000.";
|
|
||||||
validConfig = false;
|
|
||||||
}
|
}
|
||||||
if (config.hostIP == null) {
|
if (config.hostIP.isBlank()) {
|
||||||
errors += " Parameter 'hostIP' must be configured.";
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "IP Address must be configured!");
|
||||||
statusDescr = "IP Address must be configured!";
|
return;
|
||||||
validConfig = false;
|
|
||||||
}
|
}
|
||||||
if (config.hostPIN == null) {
|
|
||||||
errors += " Parameter 'hostPin' must be configured.";
|
if (config.hostPIN.isBlank()) {
|
||||||
statusDescr = "PIN must be configured!";
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||||
validConfig = false;
|
"Parameter 'hostPin' must be configured!");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
errors = errors.trim();
|
|
||||||
Helper message = new Helper();
|
serviceCommunication.setConfig(config);
|
||||||
message.setStatusDescription(statusDescr);
|
updateStatus(ThingStatus.UNKNOWN);
|
||||||
if (validConfig) {
|
scheduler.submit(() -> {
|
||||||
serviceCommunication.setConfig(config);
|
if (updateOvenData(null)) {
|
||||||
if (serviceCommunication.refreshOvenConnection(message, this.getThing().getUID().toString())) {
|
for (Channel channel : getThing().getChannels()) {
|
||||||
if (updateOvenData(null)) {
|
if (isLinked(channel.getUID().getId())) {
|
||||||
updateStatus(ThingStatus.ONLINE);
|
channelLinked(channel.getUID());
|
||||||
updateLinkedChannels();
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, message.getStatusDesription());
|
|
||||||
}
|
}
|
||||||
} else {
|
});
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, message.getStatusDesription());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateLinkedChannels() {
|
/**
|
||||||
verifyLinkedChannel(CHANNELISTEMP);
|
* Calls the service to update the oven data
|
||||||
verifyLinkedChannel(CHANNELMODE);
|
*
|
||||||
verifyLinkedChannel(CHANNELPOWER);
|
* @return true if the update succeeded, false otherwise
|
||||||
verifyLinkedChannel(CHANNELSPTEMP);
|
*/
|
||||||
verifyLinkedChannel(CHANNELECOMODE);
|
private boolean updateOvenData(@Nullable String postdata) {
|
||||||
verifyLinkedChannel(CHANNELIGNITIONS);
|
String error = "";
|
||||||
verifyLinkedChannel(CHANNELMAINTENANCEIN);
|
if (postdata != null) {
|
||||||
verifyLinkedChannel(CHANNELCLEANINGIN);
|
error = serviceCommunication.updateOvenData(postdata);
|
||||||
verifyLinkedChannel(CHANNELCONSUMPTION);
|
} else {
|
||||||
verifyLinkedChannel(CHANNELONTIME);
|
error = serviceCommunication.refreshOvenConnection();
|
||||||
if (!linkedChannels.isEmpty()) {
|
|
||||||
updateOvenData(null);
|
|
||||||
for (Channel channel : getThing().getChannels()) {
|
|
||||||
updateChannel(channel.getUID().getId());
|
|
||||||
}
|
|
||||||
startAutomaticRefresh();
|
|
||||||
automaticRefreshing = true;
|
|
||||||
}
|
}
|
||||||
|
if (error.isEmpty()) {
|
||||||
|
if (ThingStatus.OFFLINE.equals(getThing().getStatus())) {
|
||||||
|
updateStatus(ThingStatus.UNKNOWN);
|
||||||
|
}
|
||||||
|
if (!ThingStatus.ONLINE.equals(getThing().getStatus())) {
|
||||||
|
updateStatus(ThingStatus.ONLINE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return error.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyLinkedChannel(String channelID) {
|
private void verifyLinkedChannel(String channelID) {
|
||||||
|
@ -195,14 +176,24 @@ public class HaasSohnpelletstoveHandler extends BaseThingHandler {
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
stopScheduler();
|
stopScheduler();
|
||||||
|
linkedChannels.clear();
|
||||||
|
automaticRefreshing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stopScheduler() {
|
private void stopScheduler() {
|
||||||
ScheduledFuture<?> job = refreshJob;
|
ScheduledFuture<?> job = refreshJob;
|
||||||
if (job != null) {
|
if (job == null || job.isCancelled()) {
|
||||||
job.cancel(true);
|
refreshJob = scheduler.scheduleWithFixedDelay(this::refreshChannels, 0, config.refreshRate,
|
||||||
|
TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshChannels() {
|
||||||
|
if (updateOvenData(null)) {
|
||||||
|
for (Channel channel : getThing().getChannels()) {
|
||||||
|
updateChannel(channel.getUID().getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
refreshJob = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -217,9 +208,10 @@ public class HaasSohnpelletstoveHandler extends BaseThingHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void run() {
|
private void run() {
|
||||||
updateOvenData(null);
|
if (updateOvenData(null)) {
|
||||||
for (Channel channel : getThing().getChannels()) {
|
for (Channel channel : getThing().getChannels()) {
|
||||||
updateChannel(channel.getUID().getId());
|
updateChannel(channel.getUID().getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonSyntaxException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class handles the JSON communication with the Wifi Modul of the Stove
|
* This class handles the JSON communication with the Wifi Modul of the Stove
|
||||||
|
@ -52,105 +53,65 @@ public class HaasSohnpelletstoveJSONCommunication {
|
||||||
/**
|
/**
|
||||||
* Refreshes the oven Connection with the internal oven token.
|
* Refreshes the oven Connection with the internal oven token.
|
||||||
*
|
*
|
||||||
* @param message Message object to pass errors to the calling method.
|
* @return an empty string if no error occurred, the error message otherwise.
|
||||||
* @param thingUID Thing UID for logging purposes
|
|
||||||
* @return true if no error occurred, false otherwise.
|
|
||||||
*/
|
*/
|
||||||
public boolean refreshOvenConnection(Helper message, String thingUID) {
|
public String refreshOvenConnection() {
|
||||||
if (config.hostIP == null || config.hostPIN == null) {
|
String result = "";
|
||||||
message.setStatusDescription("Error in configuration. Please recreate Thing.");
|
HaasSohnpelletstoveJsonDataDTO responseObject = null;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
HaasSohnpelletstoveJsonDataDTO result = null;
|
|
||||||
boolean resultOk = false;
|
|
||||||
String error = "", errorDetail = "", statusDescr = "";
|
|
||||||
String urlStr = "http://" + config.hostIP + "/status.cgi";
|
String urlStr = "http://" + config.hostIP + "/status.cgi";
|
||||||
|
|
||||||
String response = null;
|
String response = null;
|
||||||
try {
|
try {
|
||||||
response = HttpUtil.executeUrl("GET", urlStr, 10000);
|
response = HttpUtil.executeUrl("GET", urlStr, 10000);
|
||||||
logger.debug("OvenData = {}", response);
|
logger.debug("OvenData = {}", response);
|
||||||
result = gson.fromJson(response, HaasSohnpelletstoveJsonDataDTO.class);
|
responseObject = gson.fromJson(response, HaasSohnpelletstoveJsonDataDTO.class);
|
||||||
resultOk = true;
|
ovenData = responseObject;
|
||||||
} catch (IOException e) {
|
xhspin = getValidXHSPIN(ovenData);
|
||||||
|
} catch (IOException | JsonSyntaxException e) {
|
||||||
logger.debug("Error processiong Get request {}", urlStr);
|
logger.debug("Error processiong Get request {}", urlStr);
|
||||||
statusDescr = "Timeout error with" + config.hostIP
|
result = "Timeout error with " + config.hostIP
|
||||||
+ ". Cannot find service on give IP. Please verify the IP-Address!";
|
+ ". Cannot find service on given IP. Please verify the IP-Address!";
|
||||||
errorDetail = e.getMessage();
|
logger.debug("Error in establishing connection: {}", e.getMessage());
|
||||||
resultOk = false;
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.debug("Unknwon Error: {}", e.getMessage());
|
logger.debug("Unknwon Error: {}", e.getMessage());
|
||||||
errorDetail = e.getMessage();
|
|
||||||
resultOk = false;
|
|
||||||
}
|
|
||||||
if (resultOk) {
|
|
||||||
ovenData = result;
|
|
||||||
xhspin = getValidXHSPIN(ovenData);
|
|
||||||
} else {
|
|
||||||
logger.debug("Setting thing '{}' to OFFLINE: Error '{}': {}", thingUID, error, errorDetail);
|
|
||||||
ovenData = new HaasSohnpelletstoveJsonDataDTO();
|
ovenData = new HaasSohnpelletstoveJsonDataDTO();
|
||||||
}
|
}
|
||||||
message.setStatusDescription(statusDescr);
|
return result;
|
||||||
return resultOk;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the status of the oven
|
* Gets the status of the oven
|
||||||
*
|
*
|
||||||
* @return true if success or false in case of error
|
* @return an empty string if no error occurred, the error message otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean updateOvenData(@Nullable String postData, Helper helper, String thingUID) {
|
public String updateOvenData(@Nullable String postData) {
|
||||||
String statusDescr = "";
|
String error = "";
|
||||||
boolean resultOk = false;
|
|
||||||
String error = "", errorDetail = "";
|
|
||||||
if (config.hostIP == null || config.hostPIN == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
String urlStr = "http://" + config.hostIP + "/status.cgi";
|
String urlStr = "http://" + config.hostIP + "/status.cgi";
|
||||||
|
|
||||||
// Run the HTTP POST request and get the JSON response from Oven
|
// Run the HTTP POST request and get the JSON response from Oven
|
||||||
String response = null;
|
String response = null;
|
||||||
|
|
||||||
Properties httpHeader = new Properties();
|
Properties httpHeader = new Properties();
|
||||||
|
|
||||||
if (postData != null) {
|
try {
|
||||||
try {
|
InputStream targetStream = null;
|
||||||
InputStream targetStream = new ByteArrayInputStream(postData.getBytes(StandardCharsets.UTF_8));
|
if (postData != null) {
|
||||||
refreshOvenConnection(helper, thingUID);
|
targetStream = new ByteArrayInputStream(postData.getBytes(StandardCharsets.UTF_8));
|
||||||
httpHeader = createHeader(postData);
|
|
||||||
response = HttpUtil.executeUrl("POST", urlStr, httpHeader, targetStream, "application/json", 10000);
|
|
||||||
resultOk = true;
|
|
||||||
logger.debug("Execute POST request with content to {} with header: {}", urlStr, httpHeader.toString());
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.debug("Error processiong POST request {}", urlStr);
|
|
||||||
statusDescr = "Cannot execute command on Stove. Please verify connection and Thing Status";
|
|
||||||
resultOk = false;
|
|
||||||
}
|
}
|
||||||
} else {
|
refreshOvenConnection();
|
||||||
try {
|
httpHeader = createHeader(postData != null ? postData : null);
|
||||||
refreshOvenConnection(helper, thingUID);
|
response = HttpUtil.executeUrl("POST", urlStr, httpHeader, targetStream != null ? targetStream : null,
|
||||||
httpHeader = createHeader(null);
|
"application/json", 10000);
|
||||||
response = HttpUtil.executeUrl("POST", urlStr, httpHeader, null, "", 10000);
|
logger.debug("Execute POST request with content to {} with header: {}", urlStr, httpHeader.toString());
|
||||||
resultOk = true;
|
|
||||||
logger.debug("Execute POST request to {} with header: {}", urlStr, httpHeader.toString());
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.debug("Error processiong POST request {}", e.getMessage());
|
|
||||||
String message = e.getMessage();
|
|
||||||
if (message != null && message.contains("Authentication challenge without WWW-Authenticate ")) {
|
|
||||||
statusDescr = "Cannot connect to stove. Given PIN: " + config.hostPIN + " is incorrect!";
|
|
||||||
}
|
|
||||||
resultOk = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (resultOk) {
|
|
||||||
logger.debug("OvenData = {}", response);
|
|
||||||
ovenData = gson.fromJson(response, HaasSohnpelletstoveJsonDataDTO.class);
|
ovenData = gson.fromJson(response, HaasSohnpelletstoveJsonDataDTO.class);
|
||||||
} else {
|
logger.debug("OvenData = {}", response);
|
||||||
logger.debug("Setting thing '{}' to OFFLINE: Error '{}': {}", thingUID, error, errorDetail);
|
} catch (IOException e) {
|
||||||
ovenData = new HaasSohnpelletstoveJsonDataDTO();
|
logger.debug("Error processiong POST request {}", urlStr);
|
||||||
|
error = "Cannot execute command on Stove. Please verify connection or PIN";
|
||||||
|
|
||||||
|
} catch (JsonSyntaxException e) {
|
||||||
|
logger.debug("Error in establishing connection: {}", e.getMessage());
|
||||||
|
error = "Cannot find service on given IP " + config.hostIP + ". Please verify the IP address!";
|
||||||
}
|
}
|
||||||
helper.setStatusDescription(statusDescr);
|
return error;
|
||||||
return resultOk;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
|
||||||
*
|
|
||||||
* See the NOTICE file(s) distributed with this work for additional
|
|
||||||
* information.
|
|
||||||
*
|
|
||||||
* This program and the accompanying materials are made available under the
|
|
||||||
* terms of the Eclipse Public License 2.0 which is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-2.0
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: EPL-2.0
|
|
||||||
*/
|
|
||||||
package org.openhab.binding.haassohnpelletstove.internal;
|
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The {@link Helper} is a Helper class to overcome Call by value for a Status Description.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author Christian Feininger - Initial contribution
|
|
||||||
*/
|
|
||||||
@NonNullByDefault
|
|
||||||
public class Helper {
|
|
||||||
|
|
||||||
private String statusDescription = "";
|
|
||||||
|
|
||||||
/***
|
|
||||||
* Gets the Status Description
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public String getStatusDesription() {
|
|
||||||
return statusDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
* Sets the Status Description
|
|
||||||
*
|
|
||||||
* @param status
|
|
||||||
*/
|
|
||||||
public void setStatusDescription(@Nullable String status) {
|
|
||||||
if (status != null) {
|
|
||||||
statusDescription = statusDescription + "\n" + status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -36,7 +36,7 @@
|
||||||
<label>PIN</label>
|
<label>PIN</label>
|
||||||
<description>Please add the PIN of your oven here. You can find it in the Menu directly in your oven.</description>
|
<description>Please add the PIN of your oven here. You can find it in the Menu directly in your oven.</description>
|
||||||
</parameter>
|
</parameter>
|
||||||
<parameter name="refreshRate" type="integer" unit="s" min="0" max="1000">
|
<parameter name="refreshRate" type="integer" unit="s" min="1" max="1000">
|
||||||
<label>Refresh Rate</label>
|
<label>Refresh Rate</label>
|
||||||
<description>How often the Pellet Stove should schedule a refresh after a channel is linked to an item. Temperature
|
<description>How often the Pellet Stove should schedule a refresh after a channel is linked to an item. Temperature
|
||||||
data will be refreshed according this set time in seconds. Valid input is 0 - 999.
|
data will be refreshed according this set time in seconds. Valid input is 0 - 999.
|
||||||
|
|
Loading…
Reference in New Issue