diff --git a/bundles/org.openhab.binding.mideaac/README.md b/bundles/org.openhab.binding.mideaac/README.md index 206126153a1..cb41f093599 100644 --- a/bundles/org.openhab.binding.mideaac/README.md +++ b/bundles/org.openhab.binding.mideaac/README.md @@ -41,7 +41,7 @@ No binding configuration is required. | pollingTime | Yes | Polling time in seconds. Minimum time is 30 seconds. | 60 | | timeout | Yes | Connecting timeout. Minimum time is 2 second, maximum 10 seconds. | 4 | | promptTone | Yes | "Ding" tone when command is received and executed. | False | -| version | Yes | Version 3 has token, key and cloud requirements. | 3 | +| version | Yes | Version 3 has token, key and cloud requirements. | 0 | ## Channels diff --git a/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/MideaACBindingConstants.java b/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/MideaACBindingConstants.java index 35d419e3f27..c9559a57185 100644 --- a/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/MideaACBindingConstants.java +++ b/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/MideaACBindingConstants.java @@ -85,8 +85,8 @@ public class MideaACBindingConstants { public static final String CONFIG_POLLING_TIME = "pollingTime"; public static final String CONFIG_CONNECTING_TIMEOUT = "timeout"; public static final String CONFIG_PROMPT_TONE = "promptTone"; + public static final String CONFIG_VERSION = "version"; - public static final String PROPERTY_VERSION = "version"; public static final String PROPERTY_SN = "sn"; public static final String PROPERTY_SSID = "ssid"; public static final String PROPERTY_TYPE = "type"; diff --git a/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/MideaACConfiguration.java b/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/MideaACConfiguration.java index e19e4333502..b31abacacd0 100644 --- a/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/MideaACConfiguration.java +++ b/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/MideaACConfiguration.java @@ -27,7 +27,7 @@ public class MideaACConfiguration { public int ipPort = 6444; - public String deviceId = ""; + public String deviceId = "0"; public String email = ""; @@ -43,7 +43,7 @@ public class MideaACConfiguration { public int timeout = 4; - public boolean promptTone; + public boolean promptTone = false; public int version = 0; @@ -53,7 +53,8 @@ public class MideaACConfiguration { * @return true(valid), false (not valid) */ public boolean isValid() { - return !("0".equalsIgnoreCase(deviceId) || deviceId.isBlank() || ipPort <= 0 || ipAddress.isBlank()); + return !("0".equalsIgnoreCase(deviceId) || deviceId.isBlank() || ipPort <= 0 || ipAddress.isBlank() + || version <= 1); } /** @@ -63,7 +64,7 @@ public class MideaACConfiguration { */ public boolean isDiscoveryNeeded() { return ("0".equalsIgnoreCase(deviceId) || deviceId.isBlank() || ipPort <= 0 || ipAddress.isBlank() - || !Utils.validateIP(ipAddress)); + || !Utils.validateIP(ipAddress) || version <= 1); } /** diff --git a/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/connection/CommandHelper.java b/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/connection/CommandHelper.java index 61d00d9f02c..1b4e58a1689 100644 --- a/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/connection/CommandHelper.java +++ b/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/connection/CommandHelper.java @@ -135,7 +135,7 @@ public class CommandHelper { if (command instanceof DecimalType decimalCommand) { logger.debug("Handle Target Temperature as DecimalType in degrees C"); commandSet.setTargetTemperature(limitTargetTemperatureToRange(decimalCommand.floatValue())); - } else if (command instanceof QuantityType quantityCommand) { + } else if (command instanceof QuantityType quantityCommand) { if (quantityCommand.getUnit().equals(ImperialUnits.FAHRENHEIT)) { quantityCommand = Objects.requireNonNull(quantityCommand.toUnit(SIUnits.CELSIUS)); } diff --git a/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/connection/ConnectionManager.java b/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/connection/ConnectionManager.java index accbb93b820..cd837711aa6 100644 --- a/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/connection/ConnectionManager.java +++ b/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/connection/ConnectionManager.java @@ -66,17 +66,14 @@ public class ConnectionManager { private Security security; private final int version; private final boolean promptTone; + private boolean deviceIsConnected; + private int droppedCommands = 0; /** * True allows one short retry after connection problem */ private boolean retry = true; - /** - * Suppresses the connection message if was online before - */ - private boolean connectionMessage = true; - public ConnectionManager(String ipAddress, int ipPort, int timeout, String key, String token, String cloud, String email, String password, String deviceId, int version, boolean promptTone) { this.deviceIsConnected = false; @@ -95,9 +92,6 @@ public class ConnectionManager { this.security = new Security(cloudProvider); } - private boolean deviceIsConnected; - private int droppedCommands = 0; - private Socket socket = new Socket(); private InputStream inputStream = new ByteArrayInputStream(new byte[0]); private DataOutputStream writer = new DataOutputStream(System.out); @@ -121,25 +115,6 @@ public class ConnectionManager { return str.trim().isEmpty(); } - /** - * Reset dropped commands from initialization in MideaACHandler - * Channel created for easy observation - * Dropped commands when no bytes to read after two tries or other - * byte reading problem. Device not responding. - */ - public void resetDroppedCommands() { - droppedCommands = 0; - } - - /** - * Resets Dropped command - * - * @return dropped commands - */ - public int getDroppedCommands() { - return droppedCommands = 0; - } - /** * After checking if the key and token need to be updated (Default = 0 Never) * The socket is established with the writer and inputStream (for reading responses) @@ -155,17 +130,28 @@ public class ConnectionManager { socket.setSoTimeout(timeout * 1000); socket.connect(new InetSocketAddress(ipAddress, ipPort), timeout * 1000); } catch (IOException e) { - logger.debug("IOException connecting to {}: {}", ipAddress, e.getMessage()); - deviceIsConnected = false; + // Retry addresses most common wifi connection problems- wait 5 seconds and try again if (retry) { + logger.debug("Retrying Socket, IOException connecting to {}: {}", ipAddress, e.getMessage()); try { Thread.sleep(5000); } catch (InterruptedException ex) { - logger.debug("An interupted error (pause) has occured {}", ex.getMessage()); + logger.debug("An interupted error (socket retry) has occured {}", ex.getMessage()); } - connect(); + retry = false; + try { + socket = new Socket(); + socket.setSoTimeout(timeout * 1000); + socket.connect(new InetSocketAddress(ipAddress, ipPort), timeout * 1000); + } catch (IOException e2) { + deviceIsConnected = false; + logger.debug("Second try IOException connecting to {}: {}", ipAddress, e2.getMessage()); + throw new MideaConnectionException(e2); + } + } else { + deviceIsConnected = false; + throw new MideaConnectionException(e); } - throw new MideaConnectionException(e); } // Create streams @@ -177,16 +163,9 @@ public class ConnectionManager { deviceIsConnected = false; throw new MideaConnectionException(e); } - if (!deviceIsConnected || !connectionMessage) { - logger.info("Connected to IP {}", ipAddress); - resetConnectionMessage(); - } - logger.debug("Connected to IP {}", ipAddress); - deviceIsConnected = true; - resetRetry(); if (version == 3) { - logger.debug("Device {} require authentication, going to authenticate", ipAddress); + logger.debug("Device at IP: {} requires authentication, going to authenticate", ipAddress); try { authenticate(); } catch (MideaAuthenticationException | MideaConnectionException e) { @@ -194,8 +173,13 @@ public class ConnectionManager { throw e; } } - // requestStatus(getDoPoll()); + + if (!deviceIsConnected) { + logger.info("Connected to IP {}", ipAddress); + } + logger.debug("Connected to IP {}", ipAddress); deviceIsConnected = true; + retry = true; } /** @@ -212,7 +196,7 @@ public class ConnectionManager { logger.trace("Cloud {}", cloud); if (!isBlank(token) && !isBlank(key) && !"".equals(cloud)) { - logger.debug("Device {} authenticating", ipAddress); + logger.debug("Device at IP: {} authenticating", ipAddress); doV3Handshake(); } else { throw new MideaAuthenticationException("Token, Key and / or cloud provider missing"); @@ -228,13 +212,13 @@ public class ConnectionManager { private void doV3Handshake() throws MideaConnectionException, MideaAuthenticationException { byte[] request = security.encode8370(Utils.hexStringToByteArray(token), MsgType.MSGTYPE_HANDSHAKE_REQUEST); try { - logger.trace("Device {} writing handshake_request: {}", ipAddress, Utils.bytesToHex(request)); + logger.trace("Device at IP: {} writing handshake_request: {}", ipAddress, Utils.bytesToHex(request)); write(request); byte[] response = read(); if (response != null && response.length > 0) { - logger.trace("Device {} response for handshake_request length: {}", ipAddress, response.length); + logger.trace("Device at IP: {} response for handshake_request length:{}", ipAddress, response.length); if (response.length == 72) { boolean success = security.tcpKey(Arrays.copyOfRange(response, 8, 72), Utils.hexStringToByteArray(key)); @@ -247,7 +231,6 @@ public class ConnectionManager { } catch (InterruptedException e) { logger.debug("An interupted error (success) has occured {}", e.getMessage()); } - // requestStatus(getDoPoll()); need to handle } else { throw new MideaAuthenticationException("Invalid Key. Correct Key in configuration."); } @@ -255,7 +238,7 @@ public class ConnectionManager { throw new MideaAuthenticationException("Authentication failed!"); } else { logger.warn("Authentication reponse unexpected data length ({} instead of 72)!", response.length); - throw new MideaAuthenticationException("Invalid Key. Correct Key in configuration."); + throw new MideaAuthenticationException("Unexpected authentication response length"); } } } catch (IOException e) { @@ -290,7 +273,7 @@ public class ConnectionManager { * Normal device response in 0.75 - 1 second range * If still empty, send the bytes again. If there are bytes, the read method is called. * If the socket times out with no response the command is dropped. There will be another poll - * in the time set by the user (30 seconds min) or the set command can be retried + * in the time set by the user (30 seconds min). A Set command will need to be resent. * * @param command either the set or polling command * @throws MideaAuthenticationException @@ -325,7 +308,7 @@ public class ConnectionManager { } catch (InterruptedException e) { logger.debug("An interupted error (retrycommand2) has occured {}", e.getMessage()); Thread.currentThread().interrupt(); - // Note, but continue anyway. Command will be dropped + // Note, but continue anyway for second write. } if (inputStream.available() == 0) { @@ -340,7 +323,7 @@ public class ConnectionManager { if (version == 3) { Decryption8370Result result = security.decode8370(responseBytes); for (byte[] response : result.getResponses()) { - logger.debug("Response length:{} IP address:{} ", response.length, ipAddress); + logger.debug("Response length: {} IP address: {} ", response.length, ipAddress); if (response.length > 40 + 16) { byte[] data = security.aesDecrypt(Arrays.copyOfRange(response, 40, response.length - 16)); @@ -383,12 +366,12 @@ public class ConnectionManager { default: logger.debug("Invalid response type: {}", data[0x9]); } - logger.trace("Response Type: {} and bodyType:{}", responseType, bodyType2); + logger.trace("Response Type: {} and bodyType: {}", responseType, bodyType2); // The response data from the appliance includes a packet header which we don't want data = Arrays.copyOfRange(data, 10, data.length); byte bodyType = data[0x0]; - logger.trace("Response Type expected: {} and bodyType:{}", responseType, bodyType); + logger.trace("Response Type expected: {} and bodyType: {}", responseType, bodyType); logger.trace("Bytes in HEX, decoded and stripped without header: length: {}, data: {}", data.length, Utils.bytesToHex(data)); logger.debug("Bytes in BINARY, decoded and stripped without header: length: {}, data: {}", @@ -401,7 +384,7 @@ public class ConnectionManager { } if (bodyType != -64) { if (bodyType == 30) { - logger.warn("Error response 0x1E received {} from IP Address {}", bodyType, + logger.warn("Error response 0x1E received {} from IP Address:{}", bodyType, ipAddress); return; } @@ -410,7 +393,7 @@ public class ConnectionManager { } lastResponse = new Response(data, version, responseType, bodyType); try { - logger.trace("data length is {} version is {} IP address is {}", data.length, + logger.trace("Data length is {}, version is {}, IP address is {}", data.length, version, ipAddress); if (callback != null) { callback.updateChannels(lastResponse); @@ -432,42 +415,42 @@ public class ConnectionManager { Utils.bytesToHex(data)); lastResponse = new Response(data, version, "", (byte) 0x00); - logger.debug("V2 data length is {} version is {} Ip Address is {}", data.length, version, + logger.debug("Data length is {}, version is {}, Ip Address is {}", data.length, version, ipAddress); if (callback != null) { callback.updateChannels(lastResponse); } } else { droppedCommands = droppedCommands + 1; - logger.debug("Problem with reading V2 response, skipping command {} dropped count{}", command, + logger.debug("Problem with reading V2 response, skipping {} skipped count since startup {}", command, droppedCommands); } } return; } else { droppedCommands = droppedCommands + 1; - logger.debug("Problem with reading response, skipping command {} dropped count{}", command, + logger.debug("Problem with reading response, skipping {} skipped count since startup {}", command, droppedCommands); return; } } catch (SocketException e) { - logger.debug("SocketException writing to {}: {}", ipAddress, e.getMessage()); droppedCommands = droppedCommands + 1; - logger.debug("Socket exception, skipping command {} dropped count{}", command, droppedCommands); + logger.debug("Socket exception on IP: {}, skipping command {} skipped count since startup {}", ipAddress, command, + droppedCommands); throw new MideaConnectionException(e); } catch (IOException e) { - logger.debug(" Send IOException writing to {}: {}", ipAddress, e.getMessage()); droppedCommands = droppedCommands + 1; - logger.debug("Socket exception, skipping command {} dropped count{}", command, droppedCommands); + logger.debug("IO exception on IP: {}, skipping command {} skipped count since startup {}", ipAddress, command, + droppedCommands); throw new MideaConnectionException(e); } } /** * Closes all elements of the connection before starting a new one + * Makes sure writer, inputStream and socket are closed before each command is started */ public synchronized void disconnect() { - // Make sure writer, inputStream and socket are closed before each command is started logger.debug("Disconnecting from device at {}", ipAddress); InputStream inputStream = this.inputStream; @@ -498,7 +481,7 @@ public class ConnectionManager { try { int len = inputStream.read(bytes); if (len > 0) { - logger.debug("Response received length: {} Device IP {}", len, ipAddress); + logger.debug("Response received length: {} from device at IP: {}", len, ipAddress); bytes = Arrays.copyOfRange(bytes, 0, len); return bytes; } @@ -527,25 +510,7 @@ public class ConnectionManager { } /** - * Reset Retry controls the short 5 second delay - * Before starting 30 second delays. (More severe Wifi issue) - * It is reset after a successful connection - */ - private void resetRetry() { - retry = true; - } - - /** - * Limit logging of INFO connection messages to - * only when the device was Offline in its prior - * state - */ - private void resetConnectionMessage() { - connectionMessage = true; - } - - /** - * Disconnects from the device + * Disconnects from the AC device * * @param force */ diff --git a/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/discovery/MideaACDiscoveryService.java b/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/discovery/MideaACDiscoveryService.java index cfdbe02de67..f5341e055d2 100644 --- a/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/discovery/MideaACDiscoveryService.java +++ b/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/discovery/MideaACDiscoveryService.java @@ -70,7 +70,7 @@ public class MideaACDiscoveryService extends AbstractDiscoveryService { private Security security; /** - * Discovery Service + * Discovery Service Uses the default decryption for all devices */ public MideaACDiscoveryService() { super(SUPPORTED_THING_TYPES_UIDS, discoveryTimeoutSeconds, false); @@ -152,7 +152,7 @@ public class MideaACDiscoveryService extends AbstractDiscoveryService { } } } catch (SocketTimeoutException e) { - logger.debug("Discovering poller timeout..."); + logger.trace("Discovering poller timeout..."); } catch (IOException e) { logger.debug("Error during discovery: {}", e.getMessage()); } finally { @@ -243,13 +243,14 @@ public class MideaACDiscoveryService extends AbstractDiscoveryService { final String ipAddress = packet.getAddress().getHostAddress(); byte[] data = Arrays.copyOfRange(packet.getData(), 0, packet.getLength()); - logger.debug("Midea AC discover data ({}) from {}: '{}'", data.length, ipAddress, Utils.bytesToHex(data)); + logger.trace("Midea AC discover data ({}) from {}: '{}'", data.length, ipAddress, Utils.bytesToHex(data)); if (data.length >= 104 && (Utils.bytesToHex(Arrays.copyOfRange(data, 0, 2)).equals("5A5A") || Utils.bytesToHex(Arrays.copyOfRange(data, 8, 10)).equals("5A5A"))) { logger.trace("Device supported"); - String mSmartId, mSmartVersion = "", mSmartip = "", mSmartPort = "", mSmartSN = "", mSmartSSID = "", - mSmartType = ""; + String mSmartId, mSmartip = "", mSmartSN = "", mSmartSSID = "", mSmartType = "", mSmartPort = "", + mSmartVersion = ""; + if (Utils.bytesToHex(Arrays.copyOfRange(data, 0, 2)).equals("5A5A")) { mSmartVersion = "2"; } @@ -260,7 +261,7 @@ public class MideaACDiscoveryService extends AbstractDiscoveryService { data = Arrays.copyOfRange(data, 8, data.length - 16); } - logger.trace("Version: {}", mSmartVersion); + logger.debug("Version: {}", mSmartVersion); byte[] id = Arrays.copyOfRange(data, 20, 26); logger.trace("Id Bytes: {}", Utils.bytesToHex(id)); @@ -273,10 +274,10 @@ public class MideaACDiscoveryService extends AbstractDiscoveryService { logger.debug("Id: '{}'", mSmartId); byte[] encryptData = Arrays.copyOfRange(data, 40, data.length - 16); - logger.debug("Encrypt data: '{}'", Utils.bytesToHex(encryptData)); + logger.trace("Encrypt data: '{}'", Utils.bytesToHex(encryptData)); byte[] reply = security.aesDecrypt(encryptData); - logger.debug("Length: {}, Reply: '{}'", reply.length, Utils.bytesToHex(reply)); + logger.trace("Length: {}, Reply: '{}'", reply.length, Utils.bytesToHex(reply)); mSmartip = Byte.toUnsignedInt(reply[3]) + "." + Byte.toUnsignedInt(reply[2]) + "." + Byte.toUnsignedInt(reply[1]) + "." + Byte.toUnsignedInt(reply[0]); @@ -343,7 +344,7 @@ public class MideaACDiscoveryService extends AbstractDiscoveryService { properties.put(CONFIG_IP_ADDRESS, ipAddress); properties.put(CONFIG_IP_PORT, port); properties.put(CONFIG_DEVICEID, id); - properties.put(PROPERTY_VERSION, version); + properties.put(CONFIG_VERSION, version); properties.put(PROPERTY_SN, sn); properties.put(PROPERTY_SSID, ssid); properties.put(PROPERTY_TYPE, type); diff --git a/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/handler/MideaACHandler.java b/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/handler/MideaACHandler.java index aad6aeb15dc..b1f8b8af421 100644 --- a/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/handler/MideaACHandler.java +++ b/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/handler/MideaACHandler.java @@ -14,6 +14,7 @@ package org.openhab.binding.mideaac.internal.handler; import static org.openhab.binding.mideaac.internal.MideaACBindingConstants.*; +import java.math.BigDecimal; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -75,11 +76,13 @@ public class MideaACHandler extends BaseThingHandler implements DiscoveryHandler private final Logger logger = LoggerFactory.getLogger(MideaACHandler.class); private final CloudsDTO clouds; private final boolean imperialUnits; + private boolean isPollRunning = false; private final HttpClient httpClient; private MideaACConfiguration config = new MideaACConfiguration(); private Map properties = new HashMap<>(); - private @Nullable ConnectionManager connectionManager; + // Default parameters are the same as in the MideaACConfiguration class + private ConnectionManager connectionManager = new ConnectionManager("", 6444, 4, "", "", "", "", "", "", 0, false); private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); private @Nullable ScheduledFuture scheduledTask = null; @@ -113,27 +116,22 @@ public class MideaACHandler extends BaseThingHandler implements DiscoveryHandler } /** - * This method handles the Channels that can be set (non-read only) - * First the Routine polling is stopped so there is no conflict - * Then connects and authorizes (if necessary) and returns here to - * create the command set which is then sent to the device. + * This method handles the AC Channels that can be set (non-read only) + * The command set is formed using the previous command to only + * change the item requested and leave the others the same. + * The command set which is then sent to the device via the connectionManager. */ @Override public void handleCommand(ChannelUID channelUID, Command command) { logger.debug("Handling channelUID {} with command {}", channelUID.getId(), command.toString()); ConnectionManager connectionManager = this.connectionManager; - if (connectionManager == null) { - logger.warn("The connection manager was unexpectedly null, please report a bug"); - return; - } + if (command instanceof RefreshType) { try { connectionManager.getStatus(callbackLambda); } catch (MideaAuthenticationException e) { - logger.warn("Unable to proces command: {}", e.getMessage()); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage()); } catch (MideaConnectionException | MideaException e) { - logger.warn("Unable to proces command: {}", e.getMessage()); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); } return; @@ -178,12 +176,14 @@ public class MideaACHandler extends BaseThingHandler implements DiscoveryHandler * Initialize is called on first pass or when a device parameter is changed * The basic check is if the information from Discovery (or the user update) * is valid. Because V2 devices do not require a cloud provider (or token/key) - * The check is for the IP, port and deviceID. This method also resets the dropped - * commands, disconnects the socket and stops the connection monitor (if these were - * running) + * The first check is for the IP, port and deviceID. The second part + * checks the security configuration if required (V3 device). */ @Override public void initialize() { + if (isPollRunning) { + stopScheduler(); + } config = getConfigAs(MideaACConfiguration.class); if (!config.isValid()) { @@ -196,10 +196,13 @@ public class MideaACHandler extends BaseThingHandler implements DiscoveryHandler try { discoveryService.discoverThing(config.ipAddress, this); + return; } catch (Exception e) { logger.error("Discovery failure for {}: {}", thing.getUID(), e.getMessage()); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "Discovery failure. Check configuration."); + return; } - return; } else { logger.debug("MideaACHandler config of {} is invalid. Check configuration", thing.getUID()); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, @@ -215,8 +218,10 @@ public class MideaACHandler extends BaseThingHandler implements DiscoveryHandler logger.info("Retrieving Token and/or Key from cloud"); CloudProviderDTO cloudProvider = CloudProviderDTO.getCloudProvider(config.cloud); getTokenKeyCloud(cloudProvider); + return; } else { - logger.warn("Configuration invalid for {}", thing.getUID()); + logger.warn("Configuration invalid for {} and no account info to retrieve from cloud", thing.getUID()); + return; } } else { logger.debug("Security Configuration (V3 Device) valid for {}", thing.getUID()); @@ -228,21 +233,29 @@ public class MideaACHandler extends BaseThingHandler implements DiscoveryHandler config.ipPort, config.timeout, config.key, config.token, config.cloud, config.email, config.password, config.deviceId, config.version, config.promptTone); - // startScheduler(2, config.pollingTime, TimeUnit.SECONDS); - scheduler.scheduleWithFixedDelay(this::pollJob, 2, config.pollingTime, TimeUnit.SECONDS); + startScheduler(2, config.pollingTime, TimeUnit.SECONDS); } - public void startScheduler(long initialDelay, long delay, TimeUnit unit) { - scheduledTask = scheduler.scheduleWithFixedDelay(this::pollJob, initialDelay, delay, unit); - logger.debug("Scheduled task started"); + /** + * Starts the Scheduler for the Polling + * + * @param initialDelay Seconds before first Poll + * @param delay Seconds between Polls + * @param unit Seconds + */ + private void startScheduler(long initialDelay, long delay, TimeUnit unit) { + if (scheduledTask == null) { + isPollRunning = true; + scheduledTask = scheduler.scheduleWithFixedDelay(this::pollJob, initialDelay, delay, unit); + logger.debug("Scheduled task started"); + } else { + logger.debug("Scheduler already running"); + } } private void pollJob() { ConnectionManager connectionManager = this.connectionManager; - if (connectionManager == null) { - logger.warn("The connection manager was unexpectedly null, please report a bug"); - return; - } + try { connectionManager.getStatus(callbackLambda); updateStatus(ThingStatus.ONLINE); @@ -315,13 +328,15 @@ public class MideaACHandler extends BaseThingHandler implements DiscoveryHandler Object propertyIpPort = Objects.requireNonNull(discoveryProps.get(CONFIG_IP_PORT)); configuration.put(CONFIG_IP_PORT, propertyIpPort.toString()); + Object propertyVersion = Objects.requireNonNull(discoveryProps.get(CONFIG_VERSION)); + BigDecimal bigDecimalVersion = new BigDecimal((String) propertyVersion); + logger.trace("Property Version in Handler {}", bigDecimalVersion.intValue()); + configuration.put(CONFIG_VERSION, bigDecimalVersion.intValue()); + updateConfiguration(configuration); properties = editProperties(); - Object propertyVersion = Objects.requireNonNull(discoveryProps.get(PROPERTY_VERSION)); - properties.put(PROPERTY_VERSION, propertyVersion.toString()); - Object propertySN = Objects.requireNonNull(discoveryProps.get(PROPERTY_SN)); properties.put(PROPERTY_SN, propertySN.toString()); @@ -332,10 +347,14 @@ public class MideaACHandler extends BaseThingHandler implements DiscoveryHandler properties.put(PROPERTY_TYPE, propertyType.toString()); updateProperties(properties); - initialize(); } + /** + * Gets the token and key from the Cloud + * + * @param cloudProvider Cloud Provider account + */ public void getTokenKeyCloud(CloudProviderDTO cloudProvider) { CloudDTO cloud = getClouds().get(config.email, config.password, cloudProvider); if (cloud != null) { @@ -350,7 +369,7 @@ public class MideaACHandler extends BaseThingHandler implements DiscoveryHandler logger.trace("Token: {}", tk.token()); logger.trace("Key: {}", tk.key()); - logger.info("Token and Key obtained from cloud, saving, initializing"); + logger.info("Token and Key obtained from cloud, saving, back to initialize"); initialize(); } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, String.format( @@ -360,22 +379,20 @@ public class MideaACHandler extends BaseThingHandler implements DiscoveryHandler } } - public void stopScheduler() { + private void stopScheduler() { ScheduledFuture localScheduledTask = this.scheduledTask; if (localScheduledTask != null && !localScheduledTask.isCancelled()) { localScheduledTask.cancel(true); logger.debug("Scheduled task cancelled."); + isPollRunning = false; scheduledTask = null; } - if (scheduler != null && !scheduler.isShutdown()) { - scheduler.shutdownNow(); - logger.debug("Scheduler service shut down."); - } } @Override public void dispose() { - // stopScheduler(); + stopScheduler(); + connectionManager.dispose(true); } } diff --git a/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/handler/Response.java b/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/handler/Response.java index b521f5aa9c0..56615ee2a80 100644 --- a/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/handler/Response.java +++ b/bundles/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/handler/Response.java @@ -68,6 +68,7 @@ public class Response { logger.debug("Swing Mode: {}", getSwingMode()); logger.debug("Sleep Function: {}", getSleepFunction()); logger.debug("Turbo Mode: {}", getTurboMode()); + logger.debug("Eco Mode: {}", getEcoMode()); logger.debug("Indoor Temperature: {}", getIndoorTemperature()); logger.debug("Outdoor Temperature: {}", getOutdoorTemperature()); logger.debug("LED Display: {}", getDisplayOn()); @@ -77,7 +78,6 @@ public class Response { logger.trace("Prompt Tone: {}", getPromptTone()); logger.trace("Appliance Error: {}", getApplianceError()); logger.trace("Auxiliary Heat: {}", getAuxHeat()); - logger.trace("Eco Mode: {}", getEcoMode()); logger.trace("Fahrenheit: {}", getFahrenheit()); logger.trace("Humidity: {}", getHumidity()); logger.trace("Alternate Target Temperature {}", getAlternateTargetTemperature()); diff --git a/bundles/org.openhab.binding.mideaac/src/main/resources/OH-INF/i18n/mideaac.properties b/bundles/org.openhab.binding.mideaac/src/main/resources/OH-INF/i18n/mideaac.properties index 98f30a9f964..0a449f11025 100644 --- a/bundles/org.openhab.binding.mideaac/src/main/resources/OH-INF/i18n/mideaac.properties +++ b/bundles/org.openhab.binding.mideaac/src/main/resources/OH-INF/i18n/mideaac.properties @@ -22,7 +22,7 @@ thing-type.config.mideaac.ac.email.description = Email for cloud account chosen thing-type.config.mideaac.ac.ipAddress.label = IP Address thing-type.config.mideaac.ac.ipAddress.description = IP Address of the device. thing-type.config.mideaac.ac.ipPort.label = IP Port -thing-type.config.mideaac.ac.ipPort.description = IP port of the device (for V2: 6444). +thing-type.config.mideaac.ac.ipPort.description = IP port of the device. thing-type.config.mideaac.ac.key.label = Key thing-type.config.mideaac.ac.key.description = Secret Key (length 64 HEX) used for secure connection authentication used with devices v3 (if not known, enter email and password for Cloud to retrieve it). thing-type.config.mideaac.ac.password.label = Password @@ -36,7 +36,7 @@ thing-type.config.mideaac.ac.timeout.description = Connecting timeout. Minimum t thing-type.config.mideaac.ac.token.label = Token thing-type.config.mideaac.ac.token.description = Secret Token (length 128 HEX) used for secure connection authentication used with devices v3 (if not known, enter email and password for Cloud to retrieve it). thing-type.config.mideaac.ac.version.label = AC Version -thing-type.config.mideaac.ac.version.description = Version 3 requires Token, Key and Cloud provider. Version 2 doesn't. Leave blank to discover +thing-type.config.mideaac.ac.version.description = Version 3 requires Token, Key and Cloud provider. Version 2 doesn't. # channel types diff --git a/bundles/org.openhab.binding.mideaac/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.mideaac/src/main/resources/OH-INF/thing/thing-types.xml index 46a1f9a48bb..13f29be8fe3 100644 --- a/bundles/org.openhab.binding.mideaac/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.mideaac/src/main/resources/OH-INF/thing/thing-types.xml @@ -41,10 +41,10 @@ IP Address of the device. - + ipPort - IP port of the device (for V2: 6444). + IP port of the device. 6444 @@ -106,11 +106,11 @@ After sending a command device will play "ding" tone when command is received and executed. false - + version - Version 3 requires Token, Key and Cloud provider. Version 2 doesn't. Leave blank to discover - 3 + Version 3 requires Token, Key and Cloud provider. Version 2 doesn't. + 0 diff --git a/bundles/org.openhab.binding.mideaac/src/test/java/org/openhab/binding/mideaac/internal/MideaACConfigurationTest.java b/bundles/org.openhab.binding.mideaac/src/test/java/org/openhab/binding/mideaac/internal/MideaACConfigurationTest.java index 4c330036448..d0463985edc 100644 --- a/bundles/org.openhab.binding.mideaac/src/test/java/org/openhab/binding/mideaac/internal/MideaACConfigurationTest.java +++ b/bundles/org.openhab.binding.mideaac/src/test/java/org/openhab/binding/mideaac/internal/MideaACConfigurationTest.java @@ -31,37 +31,84 @@ public class MideaACConfigurationTest { MideaACConfiguration config = new MideaACConfiguration(); /** - * Test for valid Configs + * Test for valid step 1 Configs */ @Test public void testValidConfigs() { config.ipAddress = "192.168.0.1"; config.ipPort = 6444; config.deviceId = "1234567890"; + config.version = 3; assertTrue(config.isValid()); assertFalse(config.isDiscoveryNeeded()); } /** - * Test for non-valid configs + * Test for non-valid step 1 configs */ @Test public void testnonValidConfigs() { config.ipAddress = "192.168.0.1"; config.ipPort = 0; config.deviceId = "1234567890"; + config.version = 3; assertFalse(config.isValid()); assertTrue(config.isDiscoveryNeeded()); } /** - * Test for bad IP configs + * Test for valid Security Configs + */ + @Test + public void testValidSecurityConfigs() { + config.key = "97c65a4eed4f49fda06a1a51d5cbd61d2c9b81d103ca4ca689f352a07a16fae6"; + config.token = "D24046B597DB9C8A7CA029660BC606F3FD7EBF12693E73B2EF1FFE4C3B7CA00C824E408C9F3CE972CC0D3F8250AD79D0E67B101B47AC2DD84B396E52EA05193F"; + config.cloud = "NetHome Plus"; + assertTrue(config.isV3ConfigValid()); + } + + /** + * Test for Invalid Security Configs + */ + @Test + public void testInvalidSecurityConfigs() { + config.key = "97c65a4eed4f49fda06a1a51d5cbd61d2c9b81d103ca4ca689f352a07a16fae6"; + config.token = "D24046B597DB9C8A7CA029660BC606F3FD7EBF12693E73B2EF1FFE4C3B7CA00C824E408C9F3CE972CC0D3F8250AD79D0E67B101B47AC2DD84B396E52EA05193F"; + config.cloud = ""; + assertFalse(config.isV3ConfigValid()); + } + + /** + * Test for if key and token are obtainable from cloud + */ + @Test + public void testIfTokenAndKeyCanBeObtainedFromCloud() { + config.email = "someemail.com"; + config.password = "somestrongpassword"; + config.cloud = "NetHome Plus"; + assertTrue(config.isTokenKeyObtainable()); + } + + /** + * Test for if key and token cannot be obtaines from cloud + */ + @Test + public void testIfTokenAndKeyCanNotBeObtainedFromCloud() { + config.email = ""; + config.password = "somestrongpassword"; + config.cloud = "NetHome Plus"; + assertFalse(config.isTokenKeyObtainable()); + } + + /** + * Test for bad IP v.4 address */ @Test public void testBadIpConfigs() { config.ipAddress = "192.1680.1"; config.ipPort = 6444; config.deviceId = "1234567890"; + config.version = 3; assertTrue(config.isValid()); assertTrue(config.isDiscoveryNeeded()); } diff --git a/bundles/org.openhab.binding.mideaac/src/test/java/org/openhab/binding/mideaac/internal/discovery/MideaACDiscoveryServiceTest.java b/bundles/org.openhab.binding.mideaac/src/test/java/org/openhab/binding/mideaac/internal/discovery/MideaACDiscoveryServiceTest.java index 76e9808a6e6..74d6794f943 100644 --- a/bundles/org.openhab.binding.mideaac/src/test/java/org/openhab/binding/mideaac/internal/discovery/MideaACDiscoveryServiceTest.java +++ b/bundles/org.openhab.binding.mideaac/src/test/java/org/openhab/binding/mideaac/internal/discovery/MideaACDiscoveryServiceTest.java @@ -40,6 +40,19 @@ public class MideaACDiscoveryServiceTest { String mSmartId = "", mSmartVersion = "", mSmartip = "", mSmartPort = "", mSmartSN = "", mSmartSSID = "", mSmartType = ""; + /** + * Test Version + */ + @Test + public void testVersion() { + if (Utils.bytesToHex(Arrays.copyOfRange(data, 0, 2)).equals("8370")) { + mSmartVersion = "3"; + } else { + mSmartVersion = "2"; + } + assertEquals("3", mSmartVersion); + } + /** * Test Id */