From 58470caf165d6079295f9450de2c39590e195313 Mon Sep 17 00:00:00 2001 From: Christian Wild <40909464+wildcs@users.noreply.github.com> Date: Fri, 5 May 2023 09:12:03 +0200 Subject: [PATCH] [tapocontrol] Moved error messages to i18n (#14790) * [tapocontrol] Moved error messages to i18n --------- Signed-off-by: Christian Wild --- .../internal/api/TapoCloudConnector.java | 8 +- .../internal/api/TapoDeviceConnector.java | 10 +- .../internal/api/TapoDeviceHttpApi.java | 34 ++-- .../internal/constants/TapoErrorCode.java | 119 +++++++++++++ .../constants/TapoErrorConstants.java | 157 ------------------ .../internal/constants/TapoErrorType.java | 29 ++++ .../internal/device/TapoDevice.java | 41 +++-- .../internal/helpers/TapoErrorHandler.java | 101 ++++++----- .../internal/structures/TapoEnergyData.java | 22 +-- .../internal/structures/TapoLightEffect.java | 14 +- .../OH-INF/i18n/tapocontrol.properties | 60 +++++++ 11 files changed, 335 insertions(+), 260 deletions(-) create mode 100644 bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/constants/TapoErrorCode.java delete mode 100644 bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/constants/TapoErrorConstants.java create mode 100644 bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/constants/TapoErrorType.java diff --git a/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/api/TapoCloudConnector.java b/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/api/TapoCloudConnector.java index c442964c9c164..81cf005e91128 100644 --- a/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/api/TapoCloudConnector.java +++ b/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/api/TapoCloudConnector.java @@ -13,7 +13,7 @@ package org.openhab.binding.tapocontrol.internal.api; import static org.openhab.binding.tapocontrol.internal.constants.TapoBindingSettings.*; -import static org.openhab.binding.tapocontrol.internal.constants.TapoErrorConstants.*; +import static org.openhab.binding.tapocontrol.internal.constants.TapoErrorCode.*; import java.util.UUID; import java.util.concurrent.TimeoutException; @@ -139,11 +139,11 @@ private String getTokenFromResponse(ContentResponse response) { logger.trace("cloud returns error: '{}'", rBody); } } else { - handleError(new TapoErrorHandler(ERR_JSON_DECODE_FAIL)); + handleError(new TapoErrorHandler(ERR_API_JSON_DECODE_FAIL)); logger.trace("unexpected json-response '{}'", rBody); } } else { - handleError(new TapoErrorHandler(ERR_HTTP_RESPONSE, ERR_HTTP_RESPONSE_MSG)); + handleError(new TapoErrorHandler(ERR_BINDING_HTTP_RESPONSE)); logger.warn("invalid response while login"); token = ""; } @@ -229,7 +229,7 @@ protected ContentResponse sendCloudRequest(String url, String payload) { handleError(new TapoErrorHandler(e)); } catch (TimeoutException e) { logger.debug("({}) sending request timeout: {}", uid, e.toString()); - handleError(new TapoErrorHandler(ERR_CONNECT_TIMEOUT, e.toString())); + handleError(new TapoErrorHandler(ERR_BINDING_CONNECT_TIMEOUT, e.toString())); } catch (Exception e) { logger.debug("({}) sending request failed: {}", uid, e.toString()); handleError(new TapoErrorHandler(e)); diff --git a/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/api/TapoDeviceConnector.java b/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/api/TapoDeviceConnector.java index 33594f4d6fd0b..0913654bc7dd5 100644 --- a/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/api/TapoDeviceConnector.java +++ b/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/api/TapoDeviceConnector.java @@ -13,7 +13,7 @@ package org.openhab.binding.tapocontrol.internal.api; import static org.openhab.binding.tapocontrol.internal.constants.TapoBindingSettings.*; -import static org.openhab.binding.tapocontrol.internal.constants.TapoErrorConstants.*; +import static org.openhab.binding.tapocontrol.internal.constants.TapoErrorCode.*; import static org.openhab.binding.tapocontrol.internal.constants.TapoThingConstants.*; import static org.openhab.binding.tapocontrol.internal.helpers.TapoUtils.jsonObjectToInt; @@ -97,7 +97,7 @@ public boolean login() { return this.loggedIn(); } else { logger.debug("({}) no ping while login '{}'", uid, this.ipAddress); - handleError(new TapoErrorHandler(ERR_DEVICE_OFFLINE, "no ping while login")); + handleError(new TapoErrorHandler(ERR_BINDING_DEVICE_OFFLINE, "no ping while login")); return false; } } @@ -296,7 +296,7 @@ protected void sendSecurePasstrhroug(String payload, String command) { @Override protected void handleSuccessResponse(String responseBody) { JsonObject jsnResult = getJsonFromResponse(responseBody); - Integer errorCode = jsonObjectToInt(jsnResult, "error_code", ERR_JSON_DECODE_FAIL); + Integer errorCode = jsonObjectToInt(jsnResult, "error_code", ERR_API_JSON_DECODE_FAIL.getCode()); if (errorCode != 0) { logger.debug("({}) set deviceInfo not successful: {}", uid, jsnResult); this.device.handleConnectionState(); @@ -409,7 +409,7 @@ private JsonObject getJsonFromResponse(String responseBody) { } } logger.debug("({}) sendPayload exception {}", uid, responseBody); - handleError(new TapoErrorHandler(ERR_HTTP_RESPONSE)); + handleError(new TapoErrorHandler(ERR_BINDING_HTTP_RESPONSE)); return new JsonObject(); } @@ -440,7 +440,7 @@ public Boolean isOnline(Boolean raiseError) { } else { logger.trace("({}) device is offline (no ping)", uid); if (raiseError) { - handleError(new TapoErrorHandler(ERR_DEVICE_OFFLINE)); + handleError(new TapoErrorHandler(ERR_BINDING_DEVICE_OFFLINE)); } logout(); return false; diff --git a/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/api/TapoDeviceHttpApi.java b/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/api/TapoDeviceHttpApi.java index 3e58ead0d8b1b..26ed25aebaee1 100644 --- a/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/api/TapoDeviceHttpApi.java +++ b/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/api/TapoDeviceHttpApi.java @@ -13,7 +13,7 @@ package org.openhab.binding.tapocontrol.internal.api; import static org.openhab.binding.tapocontrol.internal.constants.TapoBindingSettings.*; -import static org.openhab.binding.tapocontrol.internal.constants.TapoErrorConstants.*; +import static org.openhab.binding.tapocontrol.internal.constants.TapoErrorCode.*; import static org.openhab.binding.tapocontrol.internal.helpers.TapoUtils.*; import java.util.concurrent.TimeUnit; @@ -167,7 +167,7 @@ protected String createHandshake() { } } catch (Exception e) { logger.debug("({}) could not createHandshake: {}", uid, e.toString()); - handleError(new TapoErrorHandler(ERR_HAND_SHAKE_FAILED, "could not createHandshake")); + handleError(new TapoErrorHandler(ERR_API_HAND_SHAKE_FAILED, "could not createHandshake")); } return cookie; } @@ -186,7 +186,7 @@ private String getKeyFromResponse(ContentResponse response) { return jsonObjectToString(jsonObj.getAsJsonObject("result"), "key"); } else { logger.warn("({}) could not getKeyFromResponse '{}'", uid, rBody); - handleError(new TapoErrorHandler(ERR_HAND_SHAKE_FAILED, "could not getKeyFromResponse")); + handleError(new TapoErrorHandler(ERR_API_HAND_SHAKE_FAILED, "could not getKeyFromResponse")); } return ""; } @@ -204,7 +204,7 @@ private String getCookieFromResponse(ContentResponse response) { logger.trace("({}) got cookie: '{}'", uid, cookie); } catch (Exception e) { logger.warn("({}) could not getCookieFromResponse", uid); - handleError(new TapoErrorHandler(ERR_HAND_SHAKE_FAILED, "could not getCookieFromResponse")); + handleError(new TapoErrorHandler(ERR_API_HAND_SHAKE_FAILED, "could not getCookieFromResponse")); } return cookie; } @@ -258,7 +258,7 @@ private String getTokenFromResponse(@Nullable ContentResponse response) { /* get errocode (0=success) */ JsonObject jsonObject = GSON.fromJson(decryptedResponse, JsonObject.class); if (jsonObject != null) { - Integer errorCode = jsonObjectToInt(jsonObject, "error_code", ERR_JSON_DECODE_FAIL); + Integer errorCode = jsonObjectToInt(jsonObject, "error_code", ERR_API_JSON_DECODE_FAIL.getCode()); if (errorCode == 0) { /* return result if set / else request was successful */ result = jsonObjectToString(jsonObject.getAsJsonObject("result"), "token"); @@ -269,11 +269,11 @@ private String getTokenFromResponse(@Nullable ContentResponse response) { } } else { logger.debug("({}) unexpected json-response '{}'", uid, decryptedResponse); - tapoError.raiseError(ERR_JSON_ENCODE_FAIL, "could not get token"); + tapoError.raiseError(ERR_API_JSON_ENCODE_FAIL, "could not get token"); } } else { logger.debug("({}) invalid response while login", uid); - tapoError.raiseError(ERR_HTTP_RESPONSE, "invalid response while login"); + tapoError.raiseError(ERR_BINDING_HTTP_RESPONSE, "invalid response while login"); } /* handle error */ if (tapoError.hasError()) { @@ -315,7 +315,7 @@ protected ContentResponse sendRequest(String url, String payload) { handleError(new TapoErrorHandler(e)); } catch (TimeoutException e) { logger.debug("({}) sending request timeout: {}", uid, e.toString()); - handleError(new TapoErrorHandler(ERR_CONNECT_TIMEOUT, e.toString())); + handleError(new TapoErrorHandler(ERR_BINDING_CONNECT_TIMEOUT, e.toString())); } catch (Exception e) { logger.debug("({}) sending request failed: {}", uid, e.toString()); handleError(new TapoErrorHandler(e)); @@ -355,14 +355,14 @@ public void onComplete(Result result) { String errorMessage = getValueOrDefault(e.getMessage(), ""); if (e instanceof TimeoutException) { logger.debug("({}) sendAsyncRequest timeout'{}'", uid, errorMessage); - handleError(new TapoErrorHandler(ERR_CONNECT_TIMEOUT, errorMessage)); + handleError(new TapoErrorHandler(ERR_BINDING_CONNECT_TIMEOUT, errorMessage)); } else { logger.debug("({}) sendAsyncRequest failed'{}'", uid, errorMessage); handleError(new TapoErrorHandler(new Exception(e), errorMessage)); } } else if (response.getStatus() != 200) { logger.debug("({}) sendAsyncRequest response error'{}'", uid, response.getStatus()); - handleError(new TapoErrorHandler(ERR_HTTP_RESPONSE, getContentAsString())); + handleError(new TapoErrorHandler(ERR_BINDING_HTTP_RESPONSE, getContentAsString())); } else { /* request successful */ String rBody = getContentAsString(); @@ -411,10 +411,10 @@ protected Integer getErrorCode(@Nullable ContentResponse response) { String responseBody = response.getContentAsString(); return getErrorCode(responseBody); } else { - return ERR_HTTP_RESPONSE; + return ERR_BINDING_HTTP_RESPONSE.getCode(); } } catch (Exception e) { - return ERR_HTTP_RESPONSE; + return ERR_BINDING_HTTP_RESPONSE.getCode(); } } @@ -428,7 +428,7 @@ protected Integer getErrorCode(String responseBody) { try { JsonObject jsonObject = GSON.fromJson(responseBody, JsonObject.class); /* get errocode (0=success) */ - Integer errorCode = jsonObjectToInt(jsonObject, "error_code", ERR_JSON_DECODE_FAIL); + Integer errorCode = jsonObjectToInt(jsonObject, "error_code", ERR_API_JSON_DECODE_FAIL.getCode()); if (errorCode == 0) { return 0; } else { @@ -437,7 +437,7 @@ protected Integer getErrorCode(String responseBody) { return errorCode; } } catch (Exception e) { - return ERR_HTTP_RESPONSE; + return ERR_BINDING_HTTP_RESPONSE.getCode(); } } @@ -451,7 +451,7 @@ protected Boolean hasErrorCode(String responseBody) { if (isValidJson(responseBody)) { JsonObject jsonObject = GSON.fromJson(responseBody, JsonObject.class); /* get errocode (0=success) */ - Integer errorCode = jsonObjectToInt(jsonObject, "error_code", ERR_JSON_DECODE_FAIL); + Integer errorCode = jsonObjectToInt(jsonObject, "error_code", ERR_API_JSON_DECODE_FAIL.getCode()); if (errorCode > 0) { return true; } @@ -491,7 +491,7 @@ protected String decryptResponse(String responseBody) { String encryptedResponse = jsonObjectToString(jsonObject.getAsJsonObject("result"), "response"); return tapoCipher.decode(encryptedResponse); } else { - handleError(new TapoErrorHandler(ERR_JSON_DECODE_FAIL)); + handleError(new TapoErrorHandler(ERR_API_JSON_DECODE_FAIL)); } } catch (Exception ex) { logger.debug("({}) exception '{}' decryptingResponse: '{}'", uid, ex.toString(), responseBody); @@ -549,7 +549,7 @@ public Boolean loggedIn(Boolean raiseError) { } else { logger.trace("({}) not logged in", uid); if (raiseError) { - handleError(new TapoErrorHandler(ERR_LOGIN)); + handleError(new TapoErrorHandler(ERR_API_LOGIN)); } return false; } diff --git a/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/constants/TapoErrorCode.java b/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/constants/TapoErrorCode.java new file mode 100644 index 0000000000000..a96b8f23d007c --- /dev/null +++ b/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/constants/TapoErrorCode.java @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2010-2023 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.tapocontrol.internal.constants; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link TapoErrorCode} enum lists known errorcodes can be received or thrown by binding + * + * @author Christian Wild - Initial contribution + */ +@NonNullByDefault +public enum TapoErrorCode { + NO_ERROR(0), + ERR_UNKNOWN(-1, TapoErrorType.UNKNOWN), + ERR_API_SESSION_TIMEOUT(9999, TapoErrorType.COMMUNICATION_RETRY), + ERR_API_NULL_TRANSPORT(1000), + ERR_API_REQUEST(1002), + ERR_API_HAND_SHAKE_FAILED(1100, TapoErrorType.COMMUNICATION_RETRY), + ERR_API_LOGIN_FAILED(1111), + ERR_API_HTTP_TRANSPORT_FAILED(1112), + ERR_API_MULTI_REQUEST_FAILED(1200), + ERR_API_JSON_DECODE_FAIL(-1003), + ERR_API_JSON_ENCODE_FAIL(-1004), + ERR_API_AES_DECODE_FAIL(-1005), + ERR_API_REQUEST_LEN_ERROR(-1006), + ERR_API_CLOUD_FAILED(-1007), + ERR_API_PARAMS(-1008), + ERR_API_RSA_KEY_LENGTH(-1010), + ERR_API_SESSION_PARAM(-1101), + ERR_API_QUICK_SETUP(-1201), + ERR_API_DEVICE(-1301), + ERR_API_DEVICE_NEXT_EVENT(-1302), + ERR_API_FIRMWARE(-1401), + ERR_API_FIRMWARE_VER_ERROR(-1402), + ERR_API_LOGIN(-1501), + ERR_API_TIME(-1601), + ERR_API_TIME_SYS(-1602), + ERR_API_TIME_SAVE(-1603), + ERR_API_WIRELESS(-1701), + ERR_API_WIRELESS_UNSUPPORTED(-1702), + ERR_API_SCHEDULE(-1801), + ERR_API_SCHEDULE_FULL(-1802), + ERR_API_SCHEDULE_CONFLICT(-1803), + ERR_API_SCHEDULE_SAVE(-1804), + ERR_API_SCHEDULE_INDEX(-1805), + ERR_API_COUNTDOWN(-1901), + ERR_API_COUNTDOWN_CONFLICT(-1902), + ERR_API_COUNTDOWN_SAVE(-1903), + ERR_API_ANTITHEFT(-2001), + ERR_API_ANTITHEFT_CONFLICT(-2002), + ERR_API_ANTITHEFT_SAVE(-2003), + ERR_API_ACCOUNT(-2101), + ERR_API_STAT(-2201), + ERR_API_STAT_SAVE(-2202), + ERR_API_DST(-2301), + ERR_API_DST_SAVE(-2302), + + // List of Cloud-ErrorCodes + ERR_CLOUD_API_RATE(-20004), + ERR_CLOUD_CREDENTIALS(-20601), + ERR_CLOUD_JSON_FORMAT(-10100), + ERR_CLOUD_METHOD_MISSING(-20103), + ERR_CLOUD_PARAMETER_MISSING(-20104), + ERR_CLOUD_TOKEN_EXPIRED(-20651), + + // List of Binding-ErrorCodes + ERR_BINDING_HTTP_RESPONSE(9001, TapoErrorType.COMMUNICATION_ERROR), + ERR_BINDING_COOKIE(9002, TapoErrorType.COMMUNICATION_ERROR), + ERR_BINDING_CREDENTIALS(9003, TapoErrorType.CONFIGURATION_ERROR), + ERR_BINDING_DEVICE_OFFLINE(9009, TapoErrorType.COMMUNICATION_ERROR), + ERR_BINDING_CONNECT_TIMEOUT(9010, TapoErrorType.COMMUNICATION_ERROR), + + // List of Binding-Config-ErrorCodes + ERR_CONFIG_IP(10001, TapoErrorType.CONFIGURATION_ERROR), // ip not set + ERR_CONFIG_CREDENTIALS(10002, TapoErrorType.CONFIGURATION_ERROR), // credentials not set + ERR_CONFIG_NO_BRIDGE(10003, TapoErrorType.CONFIGURATION_ERROR); // no bridge configured + + private Integer code; + private TapoErrorType errorType; + + /* set code */ + private TapoErrorCode(Integer code) { + this.code = code; + this.errorType = TapoErrorType.GENERAL; + } + + private TapoErrorCode(Integer code, TapoErrorType errorType) { + this.code = code; + this.errorType = errorType; + } + + /* get vlaues */ + public Integer getCode() { + return this.code; + } + + public TapoErrorType getType() { + return this.errorType; + } + + public static TapoErrorCode fromCode(int errorCode) { + for (TapoErrorCode e : TapoErrorCode.values()) { + if (e.code.equals(errorCode)) + return e; + } + return ERR_UNKNOWN; + } +} diff --git a/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/constants/TapoErrorConstants.java b/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/constants/TapoErrorConstants.java deleted file mode 100644 index db9c3764606f9..0000000000000 --- a/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/constants/TapoErrorConstants.java +++ /dev/null @@ -1,157 +0,0 @@ -/** - * Copyright (c) 2010-2023 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.tapocontrol.internal.constants; - -import java.util.Set; - -import org.eclipse.jdt.annotation.NonNullByDefault; - -/** - * The {@link TapoErrorConstants} class defines error-message constants - * - * @author Christian Wild - Initial contribution - */ -@NonNullByDefault -public class TapoErrorConstants { - /**************************************** - * LIST OF ERROR CODES - ****************************************/ - // List of API-ErrorCodes - public static final Integer ERR_COMMON_FAILED = -1; - public static final Integer ERR_SESSION_TIMEOUT = 9999; - public static final Integer ERR_NULL_TRANSPORT = 1000; - public static final Integer ERR_REQUEST = 1002; - public static final Integer ERR_HAND_SHAKE_FAILED = 1100; - public static final Integer ERR_LOGIN_FAILED = 1111; - public static final Integer ERR_HTTP_TRANSPORT_FAILED = 1112; - public static final Integer ERR_MULTI_REQUEST_FAILED = 1200; - public static final Integer ERR_JSON_DECODE_FAIL = -1003; - public static final Integer ERR_JSON_ENCODE_FAIL = -1004; - public static final Integer ERR_AES_DECODE_FAIL = -1005; - public static final Integer ERR_REQUEST_LEN_ERROR = -1006; - public static final Integer ERR_CLOUD_FAILED = -1007; - public static final Integer ERR_PARAMS = -1008; - public static final Integer ERR_RSA_KEY_LENGTH = -1010; - public static final Integer ERR_SESSION_PARAM = -1101; - public static final Integer ERR_QUICK_SETUP = -1201; - public static final Integer ERR_DEVICE = -1301; - public static final Integer ERR_DEVICE_NEXT_EVENT = -1302; - public static final Integer ERR_FIRMWARE = -1401; - public static final Integer ERR_FIRMWARE_VER_ERROR = -1402; - public static final Integer ERR_LOGIN = -1501; - public static final Integer ERR_TIME = -1601; - public static final Integer ERR_TIME_SYS = -1602; - public static final Integer ERR_TIME_SAVE = -1603; - public static final Integer ERR_WIRELESS = -1701; - public static final Integer ERR_WIRELESS_UNSUPPORTED = -1702; - public static final Integer ERR_SCHEDULE = -1801; - public static final Integer ERR_SCHEDULE_FULL = -1802; - public static final Integer ERR_SCHEDULE_CONFLICT = -1803; - public static final Integer ERR_SCHEDULE_SAVE = -1804; - public static final Integer ERR_SCHEDULE_INDEX = -1805; - public static final Integer ERR_COUNTDOWN = -1901; - public static final Integer ERR_COUNTDOWN_CONFLICT = -1902; - public static final Integer ERR_COUNTDOWN_SAVE = -1903; - public static final Integer ERR_ANTITHEFT = -2001; - public static final Integer ERR_ANTITHEFT_CONFLICT = -2002; - public static final Integer ERR_ANTITHEFT_SAVE = -2003; - public static final Integer ERR_ACCOUNT = -2101; - public static final Integer ERR_STAT = -2201; - public static final Integer ERR_STAT_SAVE = -2202; - public static final Integer ERR_DST = -2301; - public static final Integer ERR_DST_SAVE = -2302; - // -20661 - - // List of Binding-ErrorCodes - public static final Integer ERR_HTTP_RESPONSE = 9001; - public static final Integer ERR_COOKIE = 9002; - public static final Integer ERR_CREDENTIALS = 9003; - public static final Integer ERR_DEVICE_OFFLINE = 9009; - public static final Integer ERR_CONNECT_TIMEOUT = 9010; - - // List of Config-ErrorCodes - public static final Integer ERR_CONF_IP = 10001; // ip not set - public static final Integer ERR_CONF_CREDENTIALS = 10002; // credentials not set - public static final Integer ERR_NO_BRIDGE = 10003; // no bridge configured - - /**************************************** - * LIST OF ERROR MESSAGES - ****************************************/ - // List of CLOUD-Error-Messages - public static final String ERR_COMMON_FAILED_MSG = ""; // -1; - public static final String ERR_SESSION_TIMEOUT_MSG = "Session Timeout"; // 9999; - public static final String ERR_NULL_TRANSPORT_MSG = ""; // 1000; - public static final String ERR_REQUEST_MSG = "Invalid request or command"; // 1002; - public static final String ERR_HAND_SHAKE_FAILED_MSG = "Can't create handshake"; // 1100; - public static final String ERR_LOGIN_FAILED_MSG = ""; // 1111; - public static final String ERR_HTTP_TRANSPORT_FAILED_MSG = ""; // 1112; - public static final String ERR_MULTI_REQUEST_FAILED_MSG = ""; // 1200; - public static final String ERR_JSON_DECODE_FAIL_MSG = "json decode failed"; // -1003; - public static final String ERR_JSON_ENCODE_FAIL_MSG = "json encode failed"; // -1004; - public static final String ERR_AES_DECODE_FAIL_MSG = ""; // -1005; - public static final String ERR_REQUEST_LEN_ERROR_MSG = ""; // -1006; - public static final String ERR_CLOUD_FAILED_MSG = ""; // -1007; - public static final String ERR_PARAMS_MSG = "received invalid parameter"; // -1008; - public static final String ERR_RSA_KEY_LENGTH_MSG = "Invalid Public Key Length"; // -1010; - public static final String ERR_SESSION_PARAM_MSG = ""; // -1101; - public static final String ERR_QUICK_SETUP_MSG = ""; // -1201; - public static final String ERR_DEVICE_MSG = ""; // -1301; - public static final String ERR_DEVICE_NEXT_EVENT_MSG = ""; // -1302; - public static final String ERR_FIRMWARE_MSG = ""; // -1401; - public static final String ERR_FIRMWARE_VER_ERROR_MSG = ""; // -1402; - public static final String ERR_LOGIN_MSG = "Login Error"; // -1501; - public static final String ERR_TIME_MSG = ""; // -1601; - public static final String ERR_TIME_SYS_MSG = ""; // -1602; - public static final String ERR_TIME_SAVE_MSG = ""; // -1603; - public static final String ERR_WIRELESS_MSG = ""; // -1701; - public static final String ERR_WIRELESS_UNSUPPORTED_MSG = ""; // -1702; - public static final String ERR_SCHEDULE_MSG = ""; // -1801; - public static final String ERR_SCHEDULE_FULL_MSG = ""; // -1802; - public static final String ERR_SCHEDULE_CONFLICT_MSG = ""; // -1803; - public static final String ERR_SCHEDULE_SAVE_MSG = ""; // -1804; - public static final String ERR_SCHEDULE_INDEX_MSG = ""; // -1805; - public static final String ERR_COUNTDOWN_MSG = ""; // -1901; - public static final String ERR_COUNTDOWN_CONFLICT_MSG = ""; // -1902; - public static final String ERR_COUNTDOWN_SAVE_MSG = ""; // -1903; - public static final String ERR_ANTITHEFT_MSG = ""; // -2001; - public static final String ERR_ANTITHEFT_CONFLICT_MSG = ""; // -2002; - public static final String ERR_ANTITHEFT_SAVE_MSG = ""; // -2003; - public static final String ERR_ACCOUNT_MSG = ""; // -2101; - public static final String ERR_STAT_MSG = ""; // -2201; - public static final String ERR_STAT_SAVE_MSG = ""; // -2202; - public static final String ERR_DST_MSG = ""; // -2301; - public static final String ERR_DST_SAVE_MSG = ""; // -2302; - - // List of Binding-Error-Messages - public static final String ERR_HTTP_RESPONSE_MSG = "Invalid HTTP-Response"; // 9001 - public static final String ERR_COOKIE_MSG = "Cookie Error"; // 9002 - public static final String ERR_DEVICE_OFFLINE_MSG = "Device Offline"; // 9009 - public static final String ERR_CREDENTIALS_MSG = "Invalid Request or Credentials"; - public static final String ERR_CONNECT_TIMEOUT_MSG = "Connection Timeout - device not reachable"; - - // List of Config-Error-Messages - public static final String ERR_CONF_IP_MSG = "IP-Address not valid"; // 10001; - public static final String ERR_CONF_CREDENTIALS_MSG = "credentials not set (bridge)"; // 10002; - public static final String ERR_NO_BRIDGE_MSG = "no bridge configured"; // 10003; - - /**************************************** - * ErrorTypes - ****************************************/ - // communication errors - set device to offline (retry connect) - public static final Set LIST_COMMUNICATION_ERRORS = Set.of(ERR_HTTP_RESPONSE, ERR_COOKIE, - ERR_DEVICE_OFFLINE, ERR_CONNECT_TIMEOUT); - // configuration errors - set device to state configuration error (don't retry) - public static final Set LIST_CONFIGURATION_ERRORS = Set.of(ERR_CREDENTIALS); - // reauthenticate errors (trying login immediatly) - public static final Set LIST_REAUTH_ERRORS = Set.of(ERR_SESSION_TIMEOUT, ERR_HAND_SHAKE_FAILED); -} diff --git a/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/constants/TapoErrorType.java b/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/constants/TapoErrorType.java new file mode 100644 index 0000000000000..ee7288da104c8 --- /dev/null +++ b/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/constants/TapoErrorType.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2023 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.tapocontrol.internal.constants; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link TapoErrorType} enum lists known errortypes can be received or thrown by binding + * + * @author Christian Wild - Initial contribution + */ +@NonNullByDefault +public enum TapoErrorType { + COMMUNICATION_ERROR, // communication error + COMMUNICATION_RETRY, // communication error - retry to connect immediately + CONFIGURATION_ERROR, // configuration error + GENERAL, // general error (e.g. known api err) + UNKNOWN // unknown error +} diff --git a/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/device/TapoDevice.java b/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/device/TapoDevice.java index d169c0fd055c5..dc2455f338095 100644 --- a/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/device/TapoDevice.java +++ b/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/device/TapoDevice.java @@ -13,7 +13,7 @@ package org.openhab.binding.tapocontrol.internal.device; import static org.openhab.binding.tapocontrol.internal.constants.TapoBindingSettings.*; -import static org.openhab.binding.tapocontrol.internal.constants.TapoErrorConstants.*; +import static org.openhab.binding.tapocontrol.internal.constants.TapoErrorCode.*; import static org.openhab.binding.tapocontrol.internal.constants.TapoThingConstants.*; import static org.openhab.binding.tapocontrol.internal.helpers.TapoUtils.*; @@ -25,6 +25,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.tapocontrol.internal.api.TapoDeviceConnector; +import org.openhab.binding.tapocontrol.internal.constants.TapoErrorCode; import org.openhab.binding.tapocontrol.internal.helpers.TapoErrorHandler; import org.openhab.binding.tapocontrol.internal.structures.TapoChildData; import org.openhab.binding.tapocontrol.internal.structures.TapoDeviceConfiguration; @@ -140,17 +141,17 @@ protected TapoErrorHandler checkSettings() { /* check bridge */ if (bridge == null || !(bridge instanceof TapoBridgeHandler)) { - configErr.raiseError(ERR_NO_BRIDGE); + configErr.raiseError(ERR_CONFIG_NO_BRIDGE); return configErr; } /* check ip-address */ if (!config.ipAddress.matches(IPV4_REGEX)) { - configErr.raiseError(ERR_CONF_IP); + configErr.raiseError(ERR_CONFIG_IP); return configErr; } /* check credentials */ if (!bridge.getCredentials().areSet()) { - configErr.raiseError(ERR_CONF_CREDENTIALS); + configErr.raiseError(ERR_CONFIG_CREDENTIALS); return configErr; } return configErr; @@ -232,10 +233,14 @@ protected void pollingSchedulerAction() { * * @return */ - public TapoErrorHandler getError() { + public TapoErrorHandler getErrorHandler() { return this.deviceError; } + public TapoErrorCode getError() { + return this.deviceError.getError(); + } + /** * set device error * @@ -456,21 +461,27 @@ public void disconnect() { */ public void handleConnectionState() { ThingStatus deviceState = getThing().getStatus(); - Integer errorCode = deviceError.getCode(); + TapoErrorCode errorCode = deviceError.getError(); - if (errorCode == 0) { + if (errorCode == TapoErrorCode.NO_ERROR) { if (deviceState != ThingStatus.ONLINE) { updateStatus(ThingStatus.ONLINE); } - } else if (LIST_REAUTH_ERRORS.contains(errorCode)) { - connect(); - } else if (LIST_COMMUNICATION_ERRORS.contains(errorCode)) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, deviceError.getMessage()); - disconnect(); - } else if (LIST_CONFIGURATION_ERRORS.contains(errorCode)) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, deviceError.getMessage()); } else { - updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, deviceError.getMessage()); + switch (errorCode.getType()) { + case COMMUNICATION_RETRY: + connect(); + break; + case COMMUNICATION_ERROR: + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, deviceError.getMessage()); + disconnect(); + break; + case CONFIGURATION_ERROR: + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, deviceError.getMessage()); + break; + default: + updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, deviceError.getMessage()); + } } } diff --git a/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/helpers/TapoErrorHandler.java b/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/helpers/TapoErrorHandler.java index 69710e10acac2..7efa99280ba3d 100644 --- a/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/helpers/TapoErrorHandler.java +++ b/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/helpers/TapoErrorHandler.java @@ -12,13 +12,11 @@ */ package org.openhab.binding.tapocontrol.internal.helpers; -import static org.openhab.binding.tapocontrol.internal.helpers.TapoUtils.*; - -import java.lang.reflect.Field; +import static org.openhab.binding.tapocontrol.internal.constants.TapoErrorCode.*; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.tapocontrol.internal.constants.TapoErrorConstants; +import org.openhab.binding.tapocontrol.internal.constants.TapoErrorCode; import com.google.gson.Gson; import com.google.gson.JsonObject; @@ -30,9 +28,8 @@ */ @NonNullByDefault public class TapoErrorHandler extends Exception { + private TapoErrorCode errorCode = TapoErrorCode.NO_ERROR; private static final long serialVersionUID = 0L; - private Integer errorCode = 0; - private String errorMessage = ""; private String infoMessage = ""; private Gson gson = new Gson(); @@ -81,6 +78,25 @@ public TapoErrorHandler(Exception ex, String infoMessage) { raiseError(ex, infoMessage); } + /** + * Constructor TapoErrorCodeEnum + * + * @param errorCode error code (TapoErrorCodeEnum) + */ + public TapoErrorHandler(TapoErrorCode errorCode) { + raiseError(errorCode); + } + + /** + * Constructor + * + * @param errorCode error code (TapoErrorCodeEnum) + * @param infoMessage optional info-message + */ + public TapoErrorHandler(TapoErrorCode errorCode, String infoMessage) { + raiseError(errorCode, infoMessage); + } + /*********************************** * * Private Functions @@ -90,30 +106,12 @@ public TapoErrorHandler(Exception ex, String infoMessage) { /** * GET ERROR-MESSAGE * - * @param errCode error Number (or constant ERR_CODE ) - * @return error-message if set constant ERR_CODE_MSG. if not name of ERR_CODE is returned + * @param errCode error Number (or constant ERR_API_CODE ) + * @return error-message if code found in i18n, else return code */ private String getErrorMessage(Integer errCode) { - Field[] fields = TapoErrorConstants.class.getDeclaredFields(); - /* loop ErrorConstants and search for code in value */ - for (Field f : fields) { - String constName = f.getName(); - try { - Integer val = (Integer) f.get(this); - if (val != null && val.equals(errCode)) { - Field constantName = TapoErrorConstants.class.getDeclaredField(constName + "_MSG"); - String msg = getValueOrDefault(constantName.get(null), "").toString(); - if (msg.length() > 2) { - return msg; - } else { - return infoMessage + " (" + constName + ")"; - } - } - } catch (Exception e) { - // next loop - } - } - return infoMessage + " (" + errCode.toString() + ")"; + String key = TapoErrorCode.fromCode(errCode).name().replace("ERR_", "error-").replace("_", "-").toLowerCase(); + return String.format("@text/%s [ \"%s\" ]", key, errCode.toString()); } /*********************************** @@ -138,9 +136,7 @@ public void raiseError(Integer errorCode) { * @param infoMessage optional info-message */ public void raiseError(Integer errorCode, String infoMessage) { - this.errorCode = errorCode; - this.infoMessage = infoMessage; - this.errorMessage = getErrorMessage(errorCode); + raiseError(TapoErrorCode.fromCode(errorCode), infoMessage); } /** @@ -159,9 +155,27 @@ public void raiseError(Exception ex) { * @param infoMessage optional info-message */ public void raiseError(Exception ex, String infoMessage) { - this.errorCode = ex.hashCode(); + raiseError(TapoErrorCode.fromCode(ex.hashCode()), infoMessage); + } + + /** + * Raises new error + * + * @param errorCode error code (TapoErrorCodeEnum) + */ + public void raiseError(TapoErrorCode errorCode) { + raiseError(errorCode, ""); + } + + /** + * Raises new error + * + * @param errorCode error code (TapoErrorCodeEnum) + * @param infoMessage optional info-message + */ + public void raiseError(TapoErrorCode errorCode, String infoMessage) { + this.errorCode = errorCode; this.infoMessage = infoMessage; - this.errorMessage = getValueOrDefault(ex.getMessage(), ex.toString()); } /** @@ -170,17 +184,15 @@ public void raiseError(Exception ex, String infoMessage) { * @param tapoError */ public void set(TapoErrorHandler tapoError) { - this.errorCode = tapoError.getNumber(); + this.errorCode = TapoErrorCode.fromCode(tapoError.getCode()); this.infoMessage = tapoError.getExtendedInfo(); - this.errorMessage = getErrorMessage(this.errorCode); } /** * Reset Error */ public void reset() { - this.errorCode = 0; - this.errorMessage = ""; + this.errorCode = NO_ERROR; this.infoMessage = ""; } @@ -198,7 +210,7 @@ public void reset() { @Override @Nullable public String getMessage() { - return this.errorMessage; + return getErrorMessage(errorCode.getCode()); } /** @@ -217,7 +229,7 @@ public String getMessage(Integer errorCode) { * @return error code (integer) */ public Integer getCode() { - return this.errorCode; + return this.errorCode.getCode(); } /** @@ -230,11 +242,11 @@ public String getExtendedInfo() { } /** - * Get Error Number + * Get Error Code * - * @return error number + * @return error code */ - public Integer getNumber() { + public TapoErrorCode getError() { return this.errorCode; } @@ -244,7 +256,7 @@ public Integer getNumber() { * @return true if has error */ public Boolean hasError() { - return this.errorCode != 0; + return this.errorCode != NO_ERROR; } /** @@ -254,7 +266,8 @@ public Boolean hasError() { */ public JsonObject getJson() { JsonObject json; - json = gson.fromJson("{'error_code': '" + errorCode + "', 'error_message':'" + errorMessage + "'}", + json = gson.fromJson( + "{'error_code': '" + errorCode + "', 'error_message':'" + getErrorMessage(getCode()) + "'}", JsonObject.class); if (json == null) { json = new JsonObject(); diff --git a/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/structures/TapoEnergyData.java b/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/structures/TapoEnergyData.java index 8210fd844dda5..71fd9804fb1cb 100644 --- a/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/structures/TapoEnergyData.java +++ b/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/structures/TapoEnergyData.java @@ -63,7 +63,7 @@ public TapoEnergyData setData(JsonObject jso) { if (jso.has(JSON_KEY_ENERGY_POWER)) { this.jsonObject = jso; } else { - jsonObject = new JsonObject(); + this.jsonObject = new JsonObject(); } setData(); return this; @@ -88,42 +88,42 @@ private void setData() { ************************************/ public Number getCurrentPower() { - return this.currentPower; + return currentPower; } public Number getTodayEnergy() { - return this.todayEnergy; + return todayEnergy; } public Number getMonthEnergy() { - return this.monthEnergy; + return monthEnergy; } public Number getYearEnergy() { int sum = 0; - for (int i = 0; i < this.past1y.length; i++) { - sum += this.past1y[i].intValue(); + for (int i = 0; i < past1y.length; i++) { + sum += past1y[i].intValue(); } return sum; } public Number getTodayRuntime() { - return this.todayRuntime; + return todayRuntime; } public Number getMonthRuntime() { - return this.monthRuntime; + return monthRuntime; } public Number[] getPast24hUsage() { - return this.past24h; + return past24h; } public Number[] getPast30dUsage() { - return this.past30d; + return past30d; } public Number[] getPast1yUsage() { - return this.past1y; + return past1y; } } diff --git a/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/structures/TapoLightEffect.java b/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/structures/TapoLightEffect.java index 396160f217152..d618fefdc7986 100644 --- a/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/structures/TapoLightEffect.java +++ b/bundles/org.openhab.binding.tapocontrol/src/main/java/org/openhab/binding/tapocontrol/internal/structures/TapoLightEffect.java @@ -118,31 +118,31 @@ public void setBrightness(Integer value) { ************************************/ public Boolean getEnable() { - return this.enable; + return enable; } public String getId() { - return this.id; + return id; } public String getName() { - return this.name; + return name; } public Boolean getCustom() { - return this.custom; + return custom; } public Integer getBrightness() { - return this.brightness; + return brightness; } public Integer[] getColorTempRange() { - return this.colorTempRange; + return colorTempRange; } public Color[] getDisplayColors() { - return this.displayColors; + return displayColors; } @Override diff --git a/bundles/org.openhab.binding.tapocontrol/src/main/resources/OH-INF/i18n/tapocontrol.properties b/bundles/org.openhab.binding.tapocontrol/src/main/resources/OH-INF/i18n/tapocontrol.properties index 755a90a6b8613..c53e9bb902f77 100644 --- a/bundles/org.openhab.binding.tapocontrol/src/main/resources/OH-INF/i18n/tapocontrol.properties +++ b/bundles/org.openhab.binding.tapocontrol/src/main/resources/OH-INF/i18n/tapocontrol.properties @@ -163,3 +163,63 @@ channel-type.tapocontrol.effectName.label = Effect Name channel-type.tapocontrol.effectName.description = Name of LightningEffect channel-type.tapocontrol.effectOn.label = Lightning Effect Enable channel-type.tapocontrol.effectOn.description = Switches the lightning effect on/off + +# error messages + +error-api-account = received account error (-2101) +error-api-aes-decode-fail = aes decode failed (-1005) +error-api-antitheft-conflict = device antitheft conflict (-2002) +error-api-antitheft = device antitheft error (-2001) +error-api-antitheft-save = antitheft save error (-2003) +error-api-cloud-failed = cloud failed (-1007) +error-api-countdown-conflict = api-countdown-conflict (-1902) +error-api-countdown = api-countdown error (-1901) +error-api-countdown-save = api-countdown-save error (-1903) +error-api-device = received device error (-1301) +error-api-device-next-event = device next event error (-1302) +error-api-dst = received dst error (-2301) +error-api-dst-save = received dst-save error (-2302) +error-api-firmware = firmware error (-1401) +error-api-firmware-ver-error = firmware version error (-1402) +error-api-hand-shake-failed = can't create handshake (1100) +error-api-http-transport-failed = http transport failed (1112) +error-api-json-decode-fail = json decode failed (-1003) +error-api-json-encode-fail = json encode failed (-1004) +error-api-login-failed = login failed (1111) +error-api-login = invalid request or credentials (-1501) +error-api-multi-request-failed = multi request failed (1200) +error-api-null-transport = null transport error (1000) +error-api-params = received invalid parameter (-1008) +error-api-quick-setup = quick setup error (-1201) +error-api-request-len-error = request length error (-1006) +error-api-request = invalid request or command (1002) +error-api-rsa-key-length = invalid public key length (-1010) +error-api-schedule-conflict = schedule conflict error (-1803) +error-api-schedule-full = schedule full error (-1802) +error-api-schedule-index = schedule index error (-1805) +error-api-schedule = schedule error (-1801) +error-api-schedule-save = saving schedule rule failed (-1804) +error-api-session-param = invalid session key (-1101) +error-api-session-timeout = device session expired (9999) +error-api-stat = state failed (-2201) +error-api-stat-save = saving state failed (-2202) +error-api-time = device time error (-1601) +error-api-time-save = saving time failed (-1603) +error-api-time-sys = time system error (-1602) +error-api-wireless = wireless error (-1701) +error-api-wireless-unsupported = wireless unsuported error (-1702) +error-binding-connect-timeout = connection timeout - device not reachable (9010) +error-binding-cookie = cookie error (9002) +error-binding-credentials = invalid request or credentials (9003) +error-binding-device-offline = device offline (9009) +error-binding-http-response = invalid http-response (9001) +error-cloud-api-rate = api rate limit exceeded (-20004) +error-cloud-credentials = incorrect email or password (-20601) +error-cloud-json-format = json format error (-10100) +error-cloud-method-missing = method does not exist or is not available (-20103) +error-cloud-parameter-missing = parameter doesn't exist (-20104) +error-cloud-token-expired = token expired (-20651) +error-config-credentials = credentials not set (bridge) (10002) +error-config-ip = ip-address not valid (10001) +error-config-no-bridge = no bridge configured (10003) +error-unknown = unknown api error ({0})