mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[daikin] Fix communication errors by retrying failed http requests (#12239)
* [daikin] Fix communication errors by retrying failed http requests Signed-off-by: Jimmy Tanagra <jcode@tanagra.id.au>
This commit is contained in:
parent
85866c63c0
commit
f18ee99b08
@ -12,13 +12,12 @@
|
||||
*/
|
||||
package org.openhab.binding.daikin.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.EOFException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
@ -37,7 +36,6 @@ import org.openhab.binding.daikin.internal.api.airbase.AirbaseBasicInfo;
|
||||
import org.openhab.binding.daikin.internal.api.airbase.AirbaseControlInfo;
|
||||
import org.openhab.binding.daikin.internal.api.airbase.AirbaseModelInfo;
|
||||
import org.openhab.binding.daikin.internal.api.airbase.AirbaseZoneInfo;
|
||||
import org.openhab.core.io.net.http.HttpUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -47,11 +45,12 @@ import org.slf4j.LoggerFactory;
|
||||
* @author Tim Waterhouse - Initial Contribution
|
||||
* @author Paul Smedley <paul@smedley.id.au> - Modifications to support Airbase Controllers
|
||||
* @author Jimmy Tanagra - Add support for https and Daikin's uuid authentication
|
||||
* Implement connection retry
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DaikinWebTargets {
|
||||
private static final int TIMEOUT_MS = 30000;
|
||||
private static final int TIMEOUT_MS = 5000;
|
||||
|
||||
private String getBasicInfoUri;
|
||||
private String setControlInfoUri;
|
||||
@ -183,73 +182,73 @@ public class DaikinWebTargets {
|
||||
}
|
||||
|
||||
private String invoke(String uri) throws DaikinCommunicationException {
|
||||
return invoke(uri, new HashMap<>());
|
||||
return invoke(uri, null);
|
||||
}
|
||||
|
||||
private String invoke(String uri, Map<String, String> params) throws DaikinCommunicationException {
|
||||
String uriWithParams = uri + paramsToQueryString(params);
|
||||
logger.debug("Calling url: {}", uriWithParams);
|
||||
String response;
|
||||
synchronized (this) {
|
||||
try {
|
||||
if (httpClient != null) {
|
||||
response = executeUrl(uriWithParams);
|
||||
} else {
|
||||
// a fall back method
|
||||
logger.debug("Using HttpUtil fall scback");
|
||||
response = HttpUtil.executeUrl("GET", uriWithParams, TIMEOUT_MS);
|
||||
}
|
||||
} catch (DaikinCommunicationException ex) {
|
||||
throw ex;
|
||||
} catch (IOException ex) {
|
||||
// Response will also be set to null if parsing in executeUrl fails so we use null here to make the
|
||||
// error check below consistent.
|
||||
response = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
throw new DaikinCommunicationException("Daikin controller returned error while invoking " + uriWithParams);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private String executeUrl(String url) throws DaikinCommunicationException {
|
||||
private synchronized String invoke(String url, @Nullable Map<String, String> params)
|
||||
throws DaikinCommunicationException {
|
||||
int attemptCount = 1;
|
||||
try {
|
||||
Request request = httpClient.newRequest(url).method(HttpMethod.GET).timeout(TIMEOUT_MS,
|
||||
TimeUnit.MILLISECONDS);
|
||||
if (uuid != null) {
|
||||
request.header("X-Daikin-uuid", uuid);
|
||||
logger.debug("Header: X-Daikin-uuid: {}", uuid);
|
||||
while (true) {
|
||||
try {
|
||||
String result = executeUrl(url, params);
|
||||
if (attemptCount > 1) {
|
||||
logger.debug("HTTP request successful on attempt #{}: {}", attemptCount, url);
|
||||
}
|
||||
return result;
|
||||
} catch (ExecutionException | TimeoutException e) {
|
||||
if (attemptCount >= 3) {
|
||||
logger.debug("HTTP request failed after {} attempts: {}", attemptCount, url, e);
|
||||
Throwable rootCause = getRootCause(e);
|
||||
String message = rootCause.getMessage();
|
||||
// EOFException message is too verbose/gibberish
|
||||
if (message == null || rootCause instanceof EOFException) {
|
||||
message = "Connection error";
|
||||
}
|
||||
throw new DaikinCommunicationException(message);
|
||||
}
|
||||
logger.debug("HTTP request error on attempt #{}: {} {}", attemptCount, url, e.getMessage());
|
||||
Thread.sleep(500 * attemptCount);
|
||||
attemptCount++;
|
||||
}
|
||||
}
|
||||
ContentResponse response = request.send();
|
||||
|
||||
if (response.getStatus() == HttpStatus.FORBIDDEN_403) {
|
||||
throw new DaikinCommunicationForbiddenException("Daikin controller access denied. Check uuid/key.");
|
||||
}
|
||||
|
||||
if (response.getStatus() != HttpStatus.OK_200) {
|
||||
logger.debug("Daikin controller HTTP status: {} - {}", response.getStatus(), response.getReason());
|
||||
}
|
||||
|
||||
return response.getContentAsString();
|
||||
} catch (DaikinCommunicationException e) {
|
||||
throw e;
|
||||
} catch (ExecutionException | TimeoutException e) {
|
||||
throw new DaikinCommunicationException("Daikin HTTP error", e);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new DaikinCommunicationException("Daikin HTTP interrupted", e);
|
||||
throw new DaikinCommunicationException("Execution interrupted");
|
||||
}
|
||||
}
|
||||
|
||||
private String paramsToQueryString(Map<String, String> params) {
|
||||
if (params.isEmpty()) {
|
||||
return "";
|
||||
private String executeUrl(String url, @Nullable Map<String, String> params)
|
||||
throws InterruptedException, TimeoutException, ExecutionException, DaikinCommunicationException {
|
||||
Request request = httpClient.newRequest(url).method(HttpMethod.GET).timeout(TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
if (uuid != null) {
|
||||
request.header("X-Daikin-uuid", uuid);
|
||||
logger.trace("Header: X-Daikin-uuid: {}", uuid);
|
||||
}
|
||||
if (params != null) {
|
||||
params.forEach((key, value) -> request.param(key, value));
|
||||
}
|
||||
logger.trace("Calling url: {}", request.getURI());
|
||||
|
||||
ContentResponse response = request.send();
|
||||
|
||||
if (response.getStatus() != HttpStatus.OK_200) {
|
||||
logger.debug("Daikin controller HTTP status: {} - {} {}", response.getStatus(), response.getReason(), url);
|
||||
}
|
||||
|
||||
return "?" + params.entrySet().stream().map(param -> param.getKey() + "=" + param.getValue())
|
||||
.collect(Collectors.joining("&"));
|
||||
if (response.getStatus() == HttpStatus.FORBIDDEN_403) {
|
||||
throw new DaikinCommunicationForbiddenException("Daikin controller access denied. Check uuid/key.");
|
||||
}
|
||||
|
||||
return response.getContentAsString();
|
||||
}
|
||||
|
||||
private Throwable getRootCause(Throwable exception) {
|
||||
Throwable cause = exception.getCause();
|
||||
while (cause != null) {
|
||||
exception = cause;
|
||||
cause = cause.getCause();
|
||||
}
|
||||
return exception;
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ import org.slf4j.LoggerFactory;
|
||||
/**
|
||||
* Holds information from the basic_info call.
|
||||
*
|
||||
* @author Jimy Tanagra - Initial contribution
|
||||
* @author Jimmy Tanagra - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@ -38,7 +38,7 @@ public class BasicInfo {
|
||||
}
|
||||
|
||||
public static BasicInfo parse(String response) {
|
||||
LOGGER.debug("Parsing string: \"{}\"", response);
|
||||
LOGGER.trace("Parsing string: \"{}\"", response);
|
||||
|
||||
Map<String, String> responseMap = InfoParser.parse(response);
|
||||
|
||||
|
@ -50,7 +50,7 @@ public class ControlInfo {
|
||||
}
|
||||
|
||||
public static ControlInfo parse(String response) {
|
||||
LOGGER.debug("Parsing string: \"{}\"", response);
|
||||
LOGGER.trace("Parsing string: \"{}\"", response);
|
||||
|
||||
Map<String, String> responseMap = InfoParser.parse(response);
|
||||
|
||||
|
@ -12,10 +12,8 @@
|
||||
*/
|
||||
package org.openhab.binding.daikin.internal.api;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.slf4j.Logger;
|
||||
@ -42,22 +40,14 @@ public class EnergyInfoDayAndWeek {
|
||||
}
|
||||
|
||||
public static EnergyInfoDayAndWeek parse(String response) {
|
||||
EnergyInfoDayAndWeek info = new EnergyInfoDayAndWeek();
|
||||
|
||||
LOGGER.trace("Parsing string: \"{}\"", response);
|
||||
|
||||
// /aircon/get_week_power_ex
|
||||
// ret=OK,s_dayw=0,week_heat=1/1/1/1/1/5/2/1/1/1/1/2/1/1,week_cool=0/0/0/0/0/0/0/0/0/0/0/0/0/0
|
||||
// week_heat=<today>/<today-1>/<today-2>/<today-3>/...
|
||||
Map<String, String> responseMap = Arrays.asList(response.split(",")).stream().filter(kv -> kv.contains("="))
|
||||
.map(kv -> {
|
||||
String[] keyValue = kv.split("=");
|
||||
String key = keyValue[0];
|
||||
String value = keyValue.length > 1 ? keyValue[1] : "";
|
||||
return new String[] { key, value };
|
||||
}).collect(Collectors.toMap(x -> x[0], x -> x[1]));
|
||||
|
||||
if (responseMap.get("ret") != null && ("OK".equals(responseMap.get("ret")))) {
|
||||
Map<String, String> responseMap = InfoParser.parse(response);
|
||||
EnergyInfoDayAndWeek info = new EnergyInfoDayAndWeek();
|
||||
if ("OK".equals(responseMap.get("ret"))) {
|
||||
Optional<Integer> dayOfWeek = Optional.ofNullable(responseMap.get("s_dayw"))
|
||||
.flatMap(value -> InfoParser.parseInt(value));
|
||||
|
||||
@ -94,7 +84,7 @@ public class EnergyInfoDayAndWeek {
|
||||
info.energyCoolingLastWeek = Optional.of(previousWeekEnergy / 10);
|
||||
}
|
||||
} else {
|
||||
LOGGER.debug("did not receive 'ret=OK' from adapter");
|
||||
LOGGER.debug("EnergyInfoDayAndWeek::parse() did not receive 'ret=OK' from adapter");
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
@ -43,10 +43,10 @@ public class EnergyInfoYear {
|
||||
|
||||
EnergyInfoYear info = new EnergyInfoYear();
|
||||
info.energyHeatingThisYear = Optional.ofNullable(responseMap.get("curr_year_heat"))
|
||||
.flatMap(value -> InfoParser.parseArrayofInt(value, 12));
|
||||
.flatMap(value -> InfoParser.parseArrayOfInt(value, 12));
|
||||
|
||||
info.energyCoolingThisYear = Optional.ofNullable(responseMap.get("curr_year_cool"))
|
||||
.flatMap(value -> InfoParser.parseArrayofInt(value, 12));
|
||||
.flatMap(value -> InfoParser.parseArrayOfInt(value, 12));
|
||||
|
||||
return info;
|
||||
}
|
||||
|
@ -12,21 +12,29 @@
|
||||
*/
|
||||
package org.openhab.binding.daikin.internal.api;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Class for parsing the comma separated values and array values returned by the Daikin Controller.
|
||||
*
|
||||
* @author Jimmy Tanagra - Initial Contribution
|
||||
* urldecode the parsed value
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class InfoParser {
|
||||
private static final Logger logger = LoggerFactory.getLogger(InfoParser.class);
|
||||
|
||||
private InfoParser() {
|
||||
}
|
||||
|
||||
@ -34,7 +42,7 @@ public class InfoParser {
|
||||
return Stream.of(response.split(",")).filter(kv -> kv.contains("=")).map(kv -> {
|
||||
String[] keyValue = kv.split("=");
|
||||
String key = keyValue[0];
|
||||
String value = keyValue.length > 1 ? keyValue[1] : "";
|
||||
String value = keyValue.length > 1 ? urldecode(keyValue[1]) : "";
|
||||
return new String[] { key, value };
|
||||
}).collect(Collectors.toMap(x -> x[0], x -> x[1]));
|
||||
}
|
||||
@ -61,7 +69,7 @@ public class InfoParser {
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<Integer[]> parseArrayofInt(String value) {
|
||||
public static Optional<Integer[]> parseArrayOfInt(String value) {
|
||||
if ("-".equals(value)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@ -72,11 +80,20 @@ public class InfoParser {
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<Integer[]> parseArrayofInt(String value, int expectedArraySize) {
|
||||
Optional<Integer[]> result = parseArrayofInt(value);
|
||||
public static Optional<Integer[]> parseArrayOfInt(String value, int expectedArraySize) {
|
||||
Optional<Integer[]> result = parseArrayOfInt(value);
|
||||
if (result.isPresent() && result.get().length == expectedArraySize) {
|
||||
return result;
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public static String urldecode(String value) {
|
||||
try {
|
||||
return URLDecoder.decode(value, StandardCharsets.UTF_8.toString());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
logger.warn("Unsupported encoding error in '{}'", value, e);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ public class SensorInfo {
|
||||
}
|
||||
|
||||
public static SensorInfo parse(String response) {
|
||||
LOGGER.debug("Parsing string: \"{}\"", response);
|
||||
LOGGER.trace("Parsing string: \"{}\"", response);
|
||||
|
||||
Map<String, String> responseMap = InfoParser.parse(response);
|
||||
|
||||
|
@ -39,7 +39,7 @@ public class AirbaseBasicInfo {
|
||||
}
|
||||
|
||||
public static AirbaseBasicInfo parse(String response) {
|
||||
LOGGER.debug("Parsing string: \"{}\"", response);
|
||||
LOGGER.trace("Parsing string: \"{}\"", response);
|
||||
|
||||
Map<String, String> responseMap = InfoParser.parse(response);
|
||||
|
||||
|
@ -49,7 +49,7 @@ public class AirbaseControlInfo {
|
||||
}
|
||||
|
||||
public static AirbaseControlInfo parse(String response) {
|
||||
LOGGER.debug("Parsing string: \"{}\"", response);
|
||||
LOGGER.trace("Parsing string: \"{}\"", response);
|
||||
|
||||
Map<String, String> responseMap = InfoParser.parse(response);
|
||||
|
||||
|
@ -43,7 +43,7 @@ public class AirbaseModelInfo {
|
||||
}
|
||||
|
||||
public static AirbaseModelInfo parse(String response) {
|
||||
LOGGER.debug("Parsing string: \"{}\"", response);
|
||||
LOGGER.trace("Parsing string: \"{}\"", response);
|
||||
|
||||
Map<String, String> responseMap = InfoParser.parse(response);
|
||||
|
||||
|
@ -27,6 +27,7 @@ import org.slf4j.LoggerFactory;
|
||||
* Holds information from the basic_info call.
|
||||
*
|
||||
* @author Paul Smedley - Initial contribution
|
||||
* @author Jimmy Tanagra - Refactor zone array to 0-based
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@ -34,13 +35,13 @@ public class AirbaseZoneInfo {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AirbaseZoneInfo.class);
|
||||
|
||||
public String zonenames = "";
|
||||
public boolean zone[] = new boolean[9];
|
||||
public boolean zone[] = new boolean[8];
|
||||
|
||||
private AirbaseZoneInfo() {
|
||||
}
|
||||
|
||||
public static AirbaseZoneInfo parse(String response) {
|
||||
LOGGER.debug("Parsing string: \"{}\"", response);
|
||||
LOGGER.trace("Parsing string: \"{}\"", response);
|
||||
|
||||
Map<String, String> responseMap = InfoParser.parse(response);
|
||||
|
||||
@ -48,18 +49,19 @@ public class AirbaseZoneInfo {
|
||||
info.zonenames = Optional.ofNullable(responseMap.get("zone_name")).orElse("");
|
||||
String zoneinfo = Optional.ofNullable(responseMap.get("zone_onoff")).orElse("");
|
||||
|
||||
String[] zones = zoneinfo.split("%3b");
|
||||
String[] zones = zoneinfo.split(";");
|
||||
|
||||
for (int i = 1; i < 9; i++) {
|
||||
info.zone[i] = "1".equals(zones[i - 1]);
|
||||
int count = Math.min(info.zone.length, zones.length);
|
||||
for (int i = 0; i < count; i++) {
|
||||
info.zone[i] = "1".equals(zones[i]);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
public Map<String, String> getParamString() {
|
||||
Map<String, String> params = new LinkedHashMap<>();
|
||||
String onoffstring = IntStream.range(1, zone.length).mapToObj(idx -> zone[idx] ? "1" : "0")
|
||||
.collect(Collectors.joining("%3b"));
|
||||
String onoffstring = IntStream.range(0, zone.length).mapToObj(idx -> zone[idx] ? "1" : "0")
|
||||
.collect(Collectors.joining(";"));
|
||||
params.put("zone_name", zonenames);
|
||||
params.put("zone_onoff", onoffstring);
|
||||
|
||||
|
@ -77,7 +77,7 @@ public class DaikinACUnitDiscoveryService extends AbstractDiscoveryService {
|
||||
|
||||
@Override
|
||||
protected void startBackgroundDiscovery() {
|
||||
logger.debug("Starting background discovery");
|
||||
logger.trace("Starting background discovery");
|
||||
|
||||
if (backgroundFuture != null && !backgroundFuture.isDone()) {
|
||||
backgroundFuture.cancel(true);
|
||||
@ -100,7 +100,7 @@ public class DaikinACUnitDiscoveryService extends AbstractDiscoveryService {
|
||||
return () -> {
|
||||
long timestampOfLastScan = getTimestampOfLastScan();
|
||||
for (InetAddress broadcastAddress : getBroadcastAddresses()) {
|
||||
logger.debug("Starting broadcast for {}", broadcastAddress.toString());
|
||||
logger.trace("Starting broadcast for {}", broadcastAddress.toString());
|
||||
|
||||
try (DatagramSocket socket = new DatagramSocket()) {
|
||||
socket.setBroadcast(true);
|
||||
@ -133,7 +133,7 @@ public class DaikinACUnitDiscoveryService extends AbstractDiscoveryService {
|
||||
|
||||
String host = incomingPacket.getAddress().toString().substring(1);
|
||||
String data = new String(incomingPacket.getData(), 0, incomingPacket.getLength(), "US-ASCII");
|
||||
logger.debug("Received packet from {}: {}", host, data);
|
||||
logger.trace("Received packet from {}: {}", host, data);
|
||||
|
||||
Map<String, String> parsedData = InfoParser.parse(data);
|
||||
Boolean secure = "1".equals(parsedData.get("en_secure"));
|
||||
@ -165,7 +165,7 @@ public class DaikinACUnitDiscoveryService extends AbstractDiscoveryService {
|
||||
}
|
||||
DiscoveryResult result = resultBuilder.build();
|
||||
|
||||
logger.debug("Successfully discovered host {}", host);
|
||||
logger.trace("Successfully discovered host {}", host);
|
||||
thingDiscovered(result);
|
||||
return true;
|
||||
}
|
||||
@ -176,7 +176,7 @@ public class DaikinACUnitDiscoveryService extends AbstractDiscoveryService {
|
||||
.withProperty(DaikinConfiguration.HOST, host).withLabel("Daikin Airbase AC Unit (" + host + ")")
|
||||
.withRepresentationProperty(DaikinConfiguration.HOST).build();
|
||||
|
||||
logger.debug("Successfully discovered host {}", host);
|
||||
logger.trace("Successfully discovered host {}", host);
|
||||
thingDiscovered(result);
|
||||
return true;
|
||||
}
|
||||
|
@ -12,7 +12,6 @@
|
||||
*/
|
||||
package org.openhab.binding.daikin.internal.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.IntStream;
|
||||
@ -40,7 +39,6 @@ import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
@ -66,12 +64,11 @@ public class DaikinAcUnitHandler extends DaikinBaseHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void pollStatus() throws IOException {
|
||||
DaikinWebTargets webTargets = this.webTargets;
|
||||
if (webTargets == null) {
|
||||
return;
|
||||
}
|
||||
protected void pollStatus() throws DaikinCommunicationException {
|
||||
ControlInfo controlInfo = webTargets.getControlInfo();
|
||||
if (!"OK".equals(controlInfo.ret)) {
|
||||
throw new DaikinCommunicationException("Invalid response from host");
|
||||
}
|
||||
updateState(DaikinBindingConstants.CHANNEL_AC_POWER, controlInfo.power ? OnOffType.ON : OnOffType.OFF);
|
||||
updateTemperatureChannel(DaikinBindingConstants.CHANNEL_AC_TEMP, controlInfo.temp);
|
||||
|
||||
@ -152,7 +149,6 @@ public class DaikinAcUnitHandler extends DaikinBaseHandler {
|
||||
// Suppress any error if energy info is not supported.
|
||||
logger.debug("getEnergyInfoDayAndWeek() error: {}", e.getMessage());
|
||||
}
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -12,7 +12,6 @@
|
||||
*/
|
||||
package org.openhab.binding.daikin.internal.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
@ -37,7 +36,6 @@ import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.StateOption;
|
||||
import org.slf4j.Logger;
|
||||
@ -75,46 +73,40 @@ public class DaikinAirbaseUnitHandler extends DaikinBaseHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void pollStatus() throws IOException {
|
||||
AirbaseControlInfo controlInfo = webTargets.getAirbaseControlInfo();
|
||||
|
||||
protected void pollStatus() throws DaikinCommunicationException {
|
||||
if (airbaseModelInfo == null || !"OK".equals(airbaseModelInfo.ret)) {
|
||||
airbaseModelInfo = webTargets.getAirbaseModelInfo();
|
||||
updateChannelStateDescriptions();
|
||||
}
|
||||
|
||||
if (controlInfo != null) {
|
||||
updateState(DaikinBindingConstants.CHANNEL_AC_POWER, controlInfo.power ? OnOffType.ON : OnOffType.OFF);
|
||||
updateTemperatureChannel(DaikinBindingConstants.CHANNEL_AC_TEMP, controlInfo.temp);
|
||||
updateState(DaikinBindingConstants.CHANNEL_AC_MODE, new StringType(controlInfo.mode.name()));
|
||||
updateState(DaikinBindingConstants.CHANNEL_AIRBASE_AC_FAN_SPEED,
|
||||
new StringType(controlInfo.fanSpeed.name()));
|
||||
AirbaseControlInfo controlInfo = webTargets.getAirbaseControlInfo();
|
||||
if (!"OK".equals(controlInfo.ret)) {
|
||||
throw new DaikinCommunicationException("Invalid response from host");
|
||||
}
|
||||
|
||||
if (!controlInfo.power) {
|
||||
updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.OFF.getValue()));
|
||||
} else if (controlInfo.mode == AirbaseMode.COLD) {
|
||||
updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.COOL.getValue()));
|
||||
} else if (controlInfo.mode == AirbaseMode.HEAT) {
|
||||
updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.HEAT.getValue()));
|
||||
} else if (controlInfo.mode == AirbaseMode.AUTO) {
|
||||
updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.AUTO.getValue()));
|
||||
}
|
||||
updateState(DaikinBindingConstants.CHANNEL_AC_POWER, OnOffType.from(controlInfo.power));
|
||||
updateTemperatureChannel(DaikinBindingConstants.CHANNEL_AC_TEMP, controlInfo.temp);
|
||||
updateState(DaikinBindingConstants.CHANNEL_AC_MODE, new StringType(controlInfo.mode.name()));
|
||||
updateState(DaikinBindingConstants.CHANNEL_AIRBASE_AC_FAN_SPEED, new StringType(controlInfo.fanSpeed.name()));
|
||||
|
||||
if (!controlInfo.power) {
|
||||
updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.OFF.getValue()));
|
||||
} else if (controlInfo.mode == AirbaseMode.COLD) {
|
||||
updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.COOL.getValue()));
|
||||
} else if (controlInfo.mode == AirbaseMode.HEAT) {
|
||||
updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.HEAT.getValue()));
|
||||
} else if (controlInfo.mode == AirbaseMode.AUTO) {
|
||||
updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.AUTO.getValue()));
|
||||
}
|
||||
|
||||
SensorInfo sensorInfo = webTargets.getAirbaseSensorInfo();
|
||||
if (sensorInfo != null) {
|
||||
updateTemperatureChannel(DaikinBindingConstants.CHANNEL_INDOOR_TEMP, sensorInfo.indoortemp);
|
||||
|
||||
updateTemperatureChannel(DaikinBindingConstants.CHANNEL_OUTDOOR_TEMP, sensorInfo.outdoortemp);
|
||||
}
|
||||
updateTemperatureChannel(DaikinBindingConstants.CHANNEL_INDOOR_TEMP, sensorInfo.indoortemp);
|
||||
updateTemperatureChannel(DaikinBindingConstants.CHANNEL_OUTDOOR_TEMP, sensorInfo.outdoortemp);
|
||||
|
||||
AirbaseZoneInfo zoneInfo = webTargets.getAirbaseZoneInfo();
|
||||
if (zoneInfo != null) {
|
||||
IntStream.range(0, zoneInfo.zone.length)
|
||||
.forEach(idx -> updateState(DaikinBindingConstants.CHANNEL_AIRBASE_AC_ZONE + idx,
|
||||
OnOffType.from(zoneInfo.zone[idx])));
|
||||
}
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
IntStream.range(0, zoneInfo.zone.length)
|
||||
.forEach(idx -> updateState(DaikinBindingConstants.CHANNEL_AIRBASE_AC_ZONE + (idx + 1),
|
||||
OnOffType.from(zoneInfo.zone[idx])));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -177,14 +169,21 @@ public class DaikinAirbaseUnitHandler extends DaikinBaseHandler {
|
||||
webTargets.setAirbaseControlInfo(info);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Turn the zone on/off
|
||||
* The Airbase controller allows turning off all zones, so we allow it here too
|
||||
*
|
||||
* @param zone the zone number starting from 1
|
||||
* @param command true to turn on the zone, false to turn it off
|
||||
*
|
||||
*/
|
||||
protected void changeZone(int zone, boolean command) throws DaikinCommunicationException {
|
||||
AirbaseZoneInfo zoneInfo = webTargets.getAirbaseZoneInfo();
|
||||
long commonZones = 0;
|
||||
long maxZones = zoneInfo.zone.length;
|
||||
|
||||
if (airbaseModelInfo != null) {
|
||||
maxZones = Math.min(maxZones - 1, airbaseModelInfo.zonespresent);
|
||||
commonZones = airbaseModelInfo.commonzone;
|
||||
maxZones = Math.min(maxZones, airbaseModelInfo.zonespresent);
|
||||
}
|
||||
if (zone <= 0 || zone > maxZones) {
|
||||
logger.warn("The given zone number ({}) is outside the number of zones supported by the controller ({})",
|
||||
@ -192,14 +191,8 @@ public class DaikinAirbaseUnitHandler extends DaikinBaseHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
long openZones = IntStream.range(0, zoneInfo.zone.length).filter(idx -> zoneInfo.zone[idx]).count()
|
||||
+ commonZones;
|
||||
logger.debug("Number of open zones: \"{}\"", openZones);
|
||||
|
||||
if (openZones >= 1) {
|
||||
zoneInfo.zone[zone] = command;
|
||||
webTargets.setAirbaseZoneInfo(zoneInfo);
|
||||
}
|
||||
zoneInfo.zone[zone - 1] = command;
|
||||
webTargets.setAirbaseZoneInfo(zoneInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -12,7 +12,6 @@
|
||||
*/
|
||||
package org.openhab.binding.daikin.internal.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -68,7 +67,7 @@ public abstract class DaikinBaseHandler extends BaseThingHandler {
|
||||
private boolean uuidRegistrationAttempted = false;
|
||||
|
||||
// Abstract methods to be overridden by specific Daikin implementation class
|
||||
protected abstract void pollStatus() throws IOException;
|
||||
protected abstract void pollStatus() throws DaikinCommunicationException;
|
||||
|
||||
protected abstract void changePower(boolean power) throws DaikinCommunicationException;
|
||||
|
||||
@ -131,8 +130,8 @@ public abstract class DaikinBaseHandler extends BaseThingHandler {
|
||||
}
|
||||
logger.debug("Received command ({}) of wrong type for thing '{}' on channel {}", command,
|
||||
thing.getUID().getAsString(), channelUID.getId());
|
||||
} catch (DaikinCommunicationException ex) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, ex.getMessage());
|
||||
} catch (DaikinCommunicationException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,7 +147,6 @@ public abstract class DaikinBaseHandler extends BaseThingHandler {
|
||||
}
|
||||
webTargets = new DaikinWebTargets(httpClient, config.host, config.secure, config.uuid);
|
||||
refreshInterval = config.refresh;
|
||||
|
||||
schedulePoll();
|
||||
}
|
||||
}
|
||||
@ -182,8 +180,11 @@ public abstract class DaikinBaseHandler extends BaseThingHandler {
|
||||
|
||||
private synchronized void poll() {
|
||||
try {
|
||||
logger.debug("Polling for state");
|
||||
logger.trace("Polling for state");
|
||||
pollStatus();
|
||||
if (getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
} catch (DaikinCommunicationForbiddenException e) {
|
||||
if (!uuidRegistrationAttempted && config.key != null && config.uuid != null) {
|
||||
logger.debug("poll: Attempting to register uuid {} with key {}", config.uuid, config.key);
|
||||
@ -194,9 +195,7 @@ public abstract class DaikinBaseHandler extends BaseThingHandler {
|
||||
"Access denied. Check uuid/key.");
|
||||
logger.warn("{} access denied by adapter. Check uuid/key.", thing.getUID());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
} catch (RuntimeException e) {
|
||||
} catch (DaikinCommunicationException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user