mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
Changes to get separate Connection Manager working
After initial changes, tested various scenarios and made changes so they are working like before. Signed-off-by: Bob Eckhoff <katmandodo@yahoo.com>
This commit is contained in:
parent
a2159bed2a
commit
99ed3d5000
@ -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
|
||||
|
||||
|
@ -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";
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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<String, String> 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);
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
||||
|
@ -41,10 +41,10 @@
|
||||
<label>IP Address</label>
|
||||
<description>IP Address of the device.</description>
|
||||
</parameter>
|
||||
<parameter name="ipPort" type="text" required="true">
|
||||
<parameter name="ipPort" type="decimal" required="true">
|
||||
<context>ipPort</context>
|
||||
<label>IP Port</label>
|
||||
<description>IP port of the device (for V2: 6444).</description>
|
||||
<description>IP port of the device.</description>
|
||||
<default>6444</default>
|
||||
</parameter>
|
||||
<parameter name="deviceId" type="text" required="true">
|
||||
@ -106,11 +106,11 @@
|
||||
<description>After sending a command device will play "ding" tone when command is received and executed.</description>
|
||||
<default>false</default>
|
||||
</parameter>
|
||||
<parameter name="version" type="text" required="true">
|
||||
<parameter name="version" type="decimal" required="true">
|
||||
<context>version</context>
|
||||
<label>AC Version</label>
|
||||
<description>Version 3 requires Token, Key and Cloud provider. Version 2 doesn't. Leave blank to discover</description>
|
||||
<default>3</default>
|
||||
<description>Version 3 requires Token, Key and Cloud provider. Version 2 doesn't.</description>
|
||||
<default>0</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user